Files
maier e31bdeb59d Session-Caching, Domain-/Gruppen-Skripte; KeePass-DB aus Repo entfernen
- inwx_common.py: Login mit Session-Cache, KP_PW-Option
- inwx_list/add: Domain als Argument, gemeinsamer Helfer
- inwx_domains(_owner).py: Domains alphabetisch, NS, Gruppen via citeq-TXT (DNS)
- hosting.kdbx aus Tracking genommen und in .gitignore

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 15:57:10 +02:00

157 lines
5.1 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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: <ziffer><Whitespace><klarname>, 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()