#!/usr/bin/env python3 """Listet alle für den Benutzer sichtbaren Domains alphabetisch sortiert auf.""" import os import subprocess import sys from inwx_common import get_client, call_api PAGE_LIMIT = 100 # Domains pro API-Seite BASE_DIR = os.path.dirname(os.path.abspath(__file__)) OWNER_FILE = os.path.join(BASE_DIR, "domain_owner") TXT_PREFIX = "citeq:" # Präfix der Gruppen-TXT-Einträge def fetch_all_domains(api): """Holt alle Domains über alle Seiten hinweg (domain.list ist paginiert).""" domains = [] page = 1 while True: res = call_api(api, 'domain.list', {'page': page, 'pageLimit': PAGE_LIMIT}) if res['code'] != 1000: print(f"❌ API Fehler: {res['msg']} (Code: {res['code']})") sys.exit(1) data = res['resData'] domains.extend(data.get('domain', [])) # Solange wir noch nicht alle laut 'count' geladen haben, weiterblättern. if len(domains) >= data.get('count', 0) or not data.get('domain'): break page += 1 return domains def get_nameservers(api, domain): """Ermittelt die zuständigen Nameserver (Registry-Delegation) einer Domain. Nutzt domain.info -> Feld 'ns', das die delegierten Nameserver enthält und auch dann funktioniert, wenn die DNS-Zone nicht bei INWX gehostet wird. Liefert eine sortierte Liste der NS-Hostnamen oder None bei Fehler. """ res = call_api(api, 'domain.info', {'domain': domain}) if res['code'] != 1000: return None ns = res['resData'].get('ns') or [] return sorted(n.rstrip('.') for n in ns) def load_owners(): """Liest die Zuordnung Ziffer -> Klarname aus der Datei domain_owner. Format pro Zeile: , wobei als Trenner beliebiger Whitespace dient (ein oder mehrere Tabs/Leerzeichen). Der Klarname darf selbst Leerzeichen enthalten. Leere Zeilen und Kommentarzeilen (#) werden ignoriert. """ owners = {} if not os.path.exists(OWNER_FILE): print(f"⚠️ Datei {OWNER_FILE} nicht gefunden – Gruppen werden ohne Klarnamen angezeigt.") return owners try: with open(OWNER_FILE, "r") as f: for line in f: line = line.strip() if not line or line.startswith("#"): continue parts = line.split(None, 1) # an erstem Whitespace (Tab/Space) trennen if len(parts) == 2: ziffer, klarname = parts owners[ziffer.strip()] = klarname.strip() except Exception as e: print(f"⚠️ Konnte {OWNER_FILE} nicht lesen: {e}") return owners def get_group(domain): """Ermittelt die Gruppen-Ziffer aus dem citeq:-TXT-Eintrag einer Domain. Nutzt eine echte DNS-Abfrage (dig +short TXT) und funktioniert daher unabhängig davon, wo die DNS-Zone gehostet wird. Liefert die Ziffer als String oder None, wenn kein passender TXT-Eintrag gefunden wird. """ try: out = subprocess.run( ["dig", "+short", "TXT", domain], capture_output=True, text=True, timeout=10, ) except FileNotFoundError: print("⚠️ 'dig' nicht gefunden – Gruppen können nicht ermittelt werden.") return None except subprocess.TimeoutExpired: return None if out.returncode != 0: return None for line in out.stdout.splitlines(): # dig liefert TXT-Werte in Anführungszeichen, lange Werte ggf. in # mehreren Teilen ("teil1" "teil2") -> zusammenfügen und entkleiden. content = line.replace('" "', '').strip().strip('"') if content.startswith(TXT_PREFIX): return content[len(TXT_PREFIX):].strip() return None def main(): api = get_client() print("Rufe Domain-Liste ab...") domains = fetch_all_domains(api) if not domains: print("ℹ️ Keine Domains gefunden.") return # Zuordnung Ziffer -> Klarname laden owners = load_owners() # Alphabetisch sortieren (Groß-/Kleinschreibung ignorieren) domains.sort(key=lambda d: d['domain'].lower()) print(f"\n{'DOMAIN':<35} {'STATUS':<8} {'GRUPPE':<18} {'NAMESERVER'}") print("-" * 100) for d in domains: name = d.get('domain', '') status = d.get('status', '') # Gruppen-Ziffer aus citeq:-TXT (per DNS) ermitteln und Klarnamen zuordnen ziffer = get_group(name) if ziffer is None: group = "-" elif ziffer in owners: group = f"{ziffer} ({owners[ziffer]})" else: group = f"{ziffer} (?)" # Zuständige Nameserver pro Domain ermitteln ns_list = get_nameservers(api, name) if ns_list is None: ns = "(nicht abrufbar)" elif not ns_list: ns = "(keine NS-Einträge gefunden)" else: ns = ", ".join(ns_list) print(f"{name:<35} {status:<8} {group:<18} {ns}") print(f"\nGesamt: {len(domains)} Domain(s)") # Kein logout() -> die Session bleibt gültig und kann wiederverwendet werden. if __name__ == "__main__": main()