inital upload

This commit is contained in:
Pascal Bouquet 2025-04-19 14:30:37 +02:00
commit 4e3b90e45d

239
chkip.py Normal file
View File

@ -0,0 +1,239 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
chkip.py DNS- und Mailserver-Check-Tool
Erstellt von Pascal Bouquet am 17.04.2025
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,
weitergeben und/oder modifizieren, entweder gemäß Version 3 der Lizenz oder
(nach Ihrer Wahl) jeder späteren Version.
Dieses Programm wird in der Hoffnung verbreitet, dass es nützlich sein wird,
aber OHNE JEDE GEWÄHRLEISTUNG sogar ohne die implizite Gewährleistung der
MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. Siehe die GNU General
Public License für weitere Details.
Sie sollten eine Kopie der GNU General Public License zusammen mit diesem
Programm erhalten haben. Falls nicht, siehe <https://www.gnu.org/licenses/>.
"""
import sys
import re
import ipaddress
import dns.resolver
import dns.reversename
import requests
import argparse
import json
# Resolver festlegen
resolver = dns.resolver.Resolver()
resolver.nameservers = ['1.1.1.1', '9.9.9.9']
resolver.timeout = 2
resolver.lifetime = 3
def is_ip_address(value):
try:
ipaddress.ip_address(value)
return True
except ValueError:
return False
def resolve_a(domain):
try:
return str(resolver.resolve(domain, 'A')[0])
except:
return None
def resolve_aaaa(domain):
try:
return str(resolver.resolve(domain, 'AAAA')[0])
except:
return None
def resolve_mx(domain):
try:
answers = resolver.resolve(domain, 'MX')
return sorted([(r.preference, str(r.exchange).rstrip('.')) for r in answers])
except:
return []
def get_ptr(ip):
try:
rev_name = dns.reversename.from_address(ip)
return str(resolver.resolve(rev_name, "PTR")[0]).rstrip('.')
except:
return None
def fcrdns_check(ip, ptr):
try:
resolved_ips = [str(r) for r in resolver.resolve(ptr, 'A')]
return 'ok' if ip in resolved_ips else f'failed ({", ".join(resolved_ips)})'
except:
return 'failed (no A record)'
def ipinfo_hostname(ip):
try:
r = requests.get(f"https://ipinfo.io/{ip}", timeout=2)
return r.json().get("hostname", "N/A")
except:
return "N/A"
def resolve_spf(domain):
try:
txt = resolver.resolve(domain, 'TXT')
for r in txt:
s = b''.join(r.strings).decode()
if s.startswith('v=spf1'):
return s
return "No SPF record found"
except:
return "SPF lookup failed"
def resolve_dmarc(domain):
try:
txt = resolver.resolve(f"_dmarc.{domain}", 'TXT')
return b''.join(txt[0].strings).decode()
except:
return "No DMARC record found"
def resolve_mta_sts(domain):
try:
txt = resolver.resolve(f"_mta-sts.{domain}", 'TXT')
return b''.join(txt[0].strings).decode()
except:
return "No MTA-STS record found"
def resolve_dkim(domain, selector):
try:
txt = resolver.resolve(f"{selector}._domainkey.{domain}", 'TXT')
return b''.join(txt[0].strings).decode()
except:
return f"No DKIM record found for selector '{selector}'"
def resolve_tlsa(domain, mx_host):
tlsa_name = f"_25._tcp.{mx_host}"
try:
results = resolver.resolve(tlsa_name, 'TLSA')
return [f"{r.usage} {r.selector} {r.mtype} {r.cert.hex()}" for r in results]
except:
return "No TLSA record found"
def print_json(output):
print(json.dumps(output, indent=2))
def print_text(output, is_ip_mode=False):
if is_ip_mode:
print(f"PTR: {output.get('PTR')}")
print(f"rDNS: {output.get('rDNS')}")
print(f"FCrDNS: {output.get('FCrDNS')}")
else:
print(f"A: {output.get('A')}")
print(f"AAAA: {output.get('AAAA')}")
mx = output.get("MX")
if isinstance(mx, list):
print("MX:")
for entry in mx:
print(f" Host: {entry.get('host')}")
print(f" IP: {entry.get('ip')}")
print(f" PTR: {entry.get('ptr')}")
print(f" FCrDNS: {entry.get('fcrdns')}")
print()
elif isinstance(mx, str):
print(f"MX: {mx}")
print(f"rDNS: {output.get('rDNS')}")
if "SPF" in output:
print(f"SPF: {output['SPF']}")
if "DMARC" in output:
print(f"DMARC: {output['DMARC']}")
if "MTA-STS" in output:
print(f"MTA-STS: {output['MTA-STS']}")
if "TLSA" in output:
for item in output["TLSA"]:
for host, tlsa in item.items():
print(f"TLSA: _25._tcp.{host}")
if isinstance(tlsa, list):
for r in tlsa:
print(f" {r}")
else:
print(f" {tlsa}")
if "DKIM" in output:
print(f"DKIM: {output.get('DKIM')}")
def main():
parser = argparse.ArgumentParser(description="chkip.py DNS- und Mailserver-Check-Tool")
parser.add_argument("domain", help="Domain oder IP-Adresse")
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("-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("-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")
args = parser.parse_args()
domain = args.domain
is_ip = is_ip_address(domain)
output = {}
if is_ip:
output["PTR"] = get_ptr(domain) or "No PTR record"
output["rDNS"] = ipinfo_hostname(domain)
ptr = output["PTR"]
if ptr and not ptr.startswith("No"):
resolved = resolve_a(ptr)
output["FCrDNS"] = "ok" if resolved == domain else f"failed ({resolved})"
else:
output["FCrDNS"] = "failed (no PTR)"
if args.json:
print_json(output)
else:
print_text(output, is_ip_mode=True)
return # ✅ wichtig!
# Domain-Zweig
output["A"] = resolve_a(domain) or "No A record"
output["AAAA"] = resolve_aaaa(domain) or "No AAAA record"
mx_records = resolve_mx(domain)
if not mx_records:
output["MX"] = "No MX record"
else:
mx_list = []
for pref, mx_host in mx_records:
host = mx_host.rstrip('.')
ip = resolve_a(host)
ptr = get_ptr(ip) if ip else None
fcr = fcrdns_check(ip, ptr) if ip and ptr else "N/A"
mx_list.append({
"host": host,
"ip": ip,
"ptr": ptr,
"fcrdns": fcr
})
output["MX"] = mx_list
output["rDNS"] = ipinfo_hostname(output["A"]) if output["A"] else None
if args.spf:
output["SPF"] = resolve_spf(domain)
if args.dmarc:
output["DMARC"] = resolve_dmarc(domain)
if args.mta_sts:
output["MTA-STS"] = resolve_mta_sts(domain)
if args.dkim:
output["DKIM"] = resolve_dkim(domain, args.dkim)
if args.tlsa and mx_records:
output["TLSA"] = []
for _, mx in mx_records:
host = mx.rstrip('.')
output["TLSA"].append({host: resolve_tlsa(domain, host)})
if args.json:
print_json(output)
else:
print_text(output, is_ip_mode=False)
if __name__ == "__main__":
main()