Add --me flag to display local and public IPs with CGNAT detection

This commit introduces the --me flag, which allows users to quickly display
their current local IPv4 and public IPv4/IPv6 addresses.

The public IPs are retrieved using ifconfig.me via curl, ensuring accurate
detection even in CGNAT or split-DNS scenarios. Local IPv4 is determined
via the system's active route through the configured resolver.

The output also includes a CGNAT detection based on private IP ranges and
the reserved 100.64.0.0/10 block, providing a helpful warning if detected.

IPv6 privacy extensions are common, so local IPv6 detection has been removed
to avoid redundant or unstable information.
This commit is contained in:
Pascal Bouquet 2025-04-19 16:39:54 +02:00
parent 7058e26be3
commit fb00a11bef

View File

@ -5,6 +5,7 @@
chkip.py DNS- und Mailserver-Check-Tool chkip.py DNS- und Mailserver-Check-Tool
Erstellt von Pascal Bouquet am 17.04.2025 Erstellt von Pascal Bouquet am 17.04.2025
Aktualisiert am 19.04.2025
Dieses Programm ist freie Software: Sie können es unter den Bedingungen der Dieses Programm ist freie Software: Sie können es unter den Bedingungen der
GNU General Public License, wie von der Free Software Foundation veröffentlicht, GNU General Public License, wie von der Free Software Foundation veröffentlicht,
@ -20,6 +21,7 @@ Sie sollten eine Kopie der GNU General Public License zusammen mit diesem
Programm erhalten haben. Falls nicht, siehe <https://www.gnu.org/licenses/>. Programm erhalten haben. Falls nicht, siehe <https://www.gnu.org/licenses/>.
""" """
import sys import sys
import re import re
import ipaddress import ipaddress
@ -28,6 +30,8 @@ import dns.reversename
import requests import requests
import argparse import argparse
import json import json
import socket
import subprocess
# Resolver festlegen # Resolver festlegen
resolver = dns.resolver.Resolver() resolver = dns.resolver.Resolver()
@ -122,6 +126,43 @@ def resolve_tlsa(domain, mx_host):
except: except:
return "No TLSA record found" return "No TLSA record found"
def get_local_ipv4():
try:
ip = next((ip for ip in resolver.nameservers if '.' in ip), '1.1.1.1')
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect((ip, 80))
local_ip = s.getsockname()[0]
s.close()
return local_ip
except:
return "unavailable"
def get_my_ip():
def run_curl(protocol_flag):
try:
result = subprocess.run(
["curl", "-s", protocol_flag, "ifconfig.me/ip"],
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL,
timeout=2,
text=True
)
return result.stdout.strip()
except:
return "unavailable"
local_v4 = get_local_ipv4()
public_v4 = run_curl("-4")
public_v6 = run_curl("-6")
return local_v4, public_v4, public_v6
def is_cgnat(ip):
try:
ip_obj = ipaddress.ip_address(ip)
return ip_obj.is_private or ip_obj in ipaddress.ip_network("100.64.0.0/10")
except:
return False
def print_json(output): def print_json(output):
print(json.dumps(output, indent=2)) print(json.dumps(output, indent=2))
@ -165,15 +206,29 @@ def print_text(output, is_ip_mode=False):
def main(): def main():
parser = argparse.ArgumentParser(description="chkip.py DNS- und Mailserver-Check-Tool") parser = argparse.ArgumentParser(description="chkip.py DNS- und Mailserver-Check-Tool")
parser.add_argument("domain", help="Domain oder IP-Adresse") parser.add_argument("domain", nargs="?", help="Domain oder IP-Adresse")
parser.add_argument("-sS", "--spf", action="store_true", help="Prüft den SPF-Eintrag") parser.add_argument("-sS", "--spf", action="store_true", help="Prüft den SPF-Eintrag")
parser.add_argument("-sD", "--dmarc", action="store_true", help="Prüft den DMARC-Eintrag") parser.add_argument("-sD", "--dmarc", action="store_true", help="Prüft den DMARC-Eintrag")
parser.add_argument("-sM", "--mta_sts", action="store_true", help="Prüft den MTA-STS-Eintrag") parser.add_argument("-sM", "--mta_sts", action="store_true", help="Prüft den MTA-STS-Eintrag")
parser.add_argument("-sT", "--tlsa", action="store_true", help="Prüft den TLSA-Eintrag") parser.add_argument("-sT", "--tlsa", action="store_true", help="Prüft den TLSA-Eintrag")
parser.add_argument("-sDK", "--dkim", type=str, help="Prüft den DKIM-Eintrag für den angegebenen Selector") parser.add_argument("-sDK", "--dkim", type=str, help="Prüft den DKIM-Eintrag für den angegebenen Selector")
parser.add_argument("--json", action="store_true", help="Ausgabe als JSON") parser.add_argument("--json", action="store_true", help="Ausgabe als JSON")
parser.add_argument("--me", action="store_true", help="Zeigt lokale & öffentliche IPs und prüft auf CGNAT")
args = parser.parse_args() args = parser.parse_args()
if args.me:
local_v4, public_v4, public_v6 = get_my_ip()
print(f"Your local IPv4: {local_v4}")
print(f"Your public IPv4: {public_v4}")
print(f"Your public IPv6: {public_v6}")
if is_cgnat(public_v4):
print("⚠️ Hinweis: Du befindest dich möglicherweise hinter CGNAT (Carrier-Grade NAT)")
return
if not args.domain:
parser.print_help()
return
domain = args.domain domain = args.domain
is_ip = is_ip_address(domain) is_ip = is_ip_address(domain)
output = {} output = {}
@ -192,7 +247,7 @@ def main():
print_json(output) print_json(output)
else: else:
print_text(output, is_ip_mode=True) print_text(output, is_ip_mode=True)
return # ✅ wichtig! return
# Domain-Zweig # Domain-Zweig
output["A"] = resolve_a(domain) or "No A record" output["A"] = resolve_a(domain) or "No A record"