nadzoring.dns_lookup package

DNS lookup module for domain name resolution and DNS record checking.

nadzoring.dns_lookup.benchmark_dns_servers(domain: str = 'google.com', servers: list[str] | None = None, record_type: RecordType = 'A', queries: int = 10, max_workers: int = 5, progress_callback: Callable[[str, int], None] | None = None, *, parallel: bool = True) list[BenchmarkResult][source]

Benchmark multiple DNS servers and compare their performance.

Tests each server either in parallel (via ThreadPoolExecutor) or sequentially, then sorts results by average response time.

Parameters:
  • domain – Domain to query. Defaults to "google.com".

  • servers – Server IPs to benchmark. None uses get_public_dns_servers().

  • record_type – DNS record type to query. Defaults to "A".

  • queries – Queries per server. Defaults to 10.

  • max_workers – Thread pool size when parallel is True. Defaults to 5.

  • progress_callback – Called after each server completes with (server_ip, 1-based_index).

  • parallel – Run benchmarks concurrently when True (default).

Returns:

List of BenchmarkResult dicts sorted by avg_response_time ascending (fastest first).

Examples

>>> results = benchmark_dns_servers(servers=["8.8.8.8", "1.1.1.1"])
>>> fastest = results[0]
>>> print(f"{fastest['server']}: {fastest['avg_response_time']:.2f}ms")
nadzoring.dns_lookup.check_dns(domain: str, nameserver: str | None = None, record_types: list[RecordType] | None = None, *, validate_mx: bool = False, validate_txt: bool = False) DetailedCheckResult[source]

Perform a comprehensive DNS check with detailed per-record information.

Queries the specified record types and optionally validates MX priorities and SPF/DKIM TXT records.

Parameters:
  • domain – Domain name to check (e.g. "example.com").

  • nameserver – Optional nameserver IP. None uses the system default.

  • record_types – Record types to query. Defaults to ["A", "AAAA", "MX", "NS", "TXT", "CNAME"].

  • validate_mx – Validate MX record priorities when True.

  • validate_txt – Validate SPF and DKIM in TXT records when True.

Returns:

DetailedCheckResult dict with domain, records, errors, response_times, and validations keys.

Examples

>>> result = check_dns(
...     "example.com",
...     record_types=["MX", "TXT"],
...     validate_mx=True,
...     validate_txt=True,
... )
>>> result["validations"].get("mx", {}).get("valid")
True
nadzoring.dns_lookup.check_dns_poisoning(domain: str, control_server: str = '8.8.8.8', test_servers: list[str] | None = None, record_type: str = 'A', additional_types: list[str] | None = None) PoisoningCheckResult[source]

Check for DNS poisoning, censorship, or manipulation.

Queries the trusted control_server and compares its response against multiple test servers. CDN and anycast patterns are classified separately from true poisoning.

Parameters:
  • domain – Domain to test (e.g. "example.com").

  • control_server – Trusted DNS server IP used as the baseline. Defaults to "8.8.8.8" (Google DNS).

  • test_servers – Test server IPs. None uses get_public_dns_servers().

  • record_type – Record type to query. Defaults to "A".

  • additional_types – Extra record types to query on the control server for additional context.

Returns:

PoisoningCheckResult with comprehensive analysis fields.

Examples

>>> result = check_dns_poisoning("example.com")
>>> if result["poisoned"]:
...     print(f"Confidence: {result['confidence']}%")
>>> result = check_dns_poisoning(
...     "example.com", test_servers=["1.1.1.1", "9.9.9.9"]
... )
nadzoring.dns_lookup.compare_dns_servers(domain: str, servers: list[str], record_types: list[str], progress_callback: Callable[[], None] | None = None) ServerComparisonResult[source]

Compare DNS responses from multiple servers for the same domain.

Uses the first server in servers as the baseline. Each subsequent server’s records are compared against the baseline; discrepancies are collected in differences.

Parameters:
  • domain – Domain name to query (e.g. "example.com").

  • servers – DNS server IPs to compare. The first entry is the baseline.

  • record_types – Record types to query on every server.

  • progress_callback – Called after each successful query. Useful for progress bars.

Returns:

ServerComparisonResult with domain, servers, and differences keys.

Examples

>>> result = compare_dns_servers(
...     "example.com",
...     servers=["8.8.8.8", "1.1.1.1"],
...     record_types=["A", "MX"],
... )
>>> result["differences"]
[]
nadzoring.dns_lookup.health_check_dns(domain: str, nameserver: str | None = None) HealthCheckResult[source]

Perform a comprehensive DNS health check with scoring.

Evaluates A, AAAA, MX, NS, TXT, and CNAME records, computes per-type scores, and derives an overall health score and status.

CNAME at the apex (non-subdomain) is stored as 100 but excluded from the score average, since the record type is only meaningful for subdomains.

Parameters:
  • domain – Domain name to check (e.g. "example.com").

  • nameserver – Optional nameserver IP. None uses the system default.

Returns:

HealthCheckResult dict with domain, score, status, issues, warnings, and record_scores keys.

Examples

>>> result = health_check_dns("example.com")
>>> print(result["score"], result["status"])
>>> for rtype, score in result["record_scores"].items():
...     print(f"  {rtype}: {score}")
nadzoring.dns_lookup.resolve_dns(domain: str, record_type: RecordType = 'A', nameserver: str | None = None, *, include_ttl: bool = False, timeout: float = 5.0, lifetime: float = 10.0) DNSResult

Perform DNS resolution with timing and structured error handling.

Resolves domain for record_type, measuring response time and optionally capturing TTL. All DNS errors are surfaced through the "error" field rather than raised as exceptions, making this safe to call in automated scripts without try/except.

Parameters:
  • domain – Domain name to resolve (e.g. "example.com").

  • record_type – DNS record type to query. Defaults to "A".

  • nameserver – Optional nameserver IP; None uses the system default.

  • include_ttl – Include TTL value in result. Defaults to False.

  • timeout – Per-nameserver query timeout in seconds. Defaults to 5.0.

  • lifetime – Total query lifetime in seconds. Defaults to 10.0.

Returns:

DNSResult dict. Always check result["error"] before using result["records"]:

result = resolve_with_timer("example.com", "A")
if result["error"]:
    # Possible values:
    # "Domain does not exist"  — NXDOMAIN
    # "No A records"           — NoAnswer
    # "Query timeout"          — Timeout
    # <arbitrary string>       — unexpected error
    print("DNS error:", result["error"])
else:
    print(result["records"])  # ['93.184.216.34']
    print(result["response_time"])  # e.g. 42.5

Examples

Basic A record lookup:

result = resolve_with_timer("example.com")
if not result["error"]:
    print(result["records"])

MX lookup with TTL:

result = resolve_with_timer("example.com", "MX", include_ttl=True)
if not result["error"]:
    print(result["records"], result["ttl"])

Using a custom nameserver:

result = resolve_with_timer("example.com", nameserver="1.1.1.1")
nadzoring.dns_lookup.reverse_dns(ip_address: str, nameserver: str | None = None) dict[str, str | float | None][source]

Perform a reverse DNS lookup to resolve an IP address to a hostname.

Queries the PTR record for ip_address using dns.reversename.from_address() for automatic in-addr.arpa / ip6.arpa name construction. Both IPv4 and IPv6 are supported.

The function never raises; all failures are returned in the "error" field so that callers can handle them uniformly:

result = reverse_dns("192.168.1.1")
hostname = result["hostname"] or f"[{result['error']}]"
Parameters:
  • ip_address – IPv4 or IPv6 address to look up (e.g. "8.8.8.8").

  • nameserver – Optional nameserver IP address. None uses the system default resolvers.

Returns:

ip_address

The original address queried (always present).

hostname

Resolved hostname with trailing dot stripped, or None when the lookup failed.

error

Error message string on failure; None on success. Possible values:

  • "No PTR record" — the IP has no reverse entry

  • "No reverse DNS" — NXDOMAIN on the reverse zone

  • "Query timeout" — resolver timed out

  • "Invalid IP address: …"ip_address is malformed

  • arbitrary string — unexpected resolver error

response_time

Query round-trip time in milliseconds (2 d.p.), or None when the query timed out.

Return type:

Dict with the following keys

Examples

Successful lookup:

result = reverse_dns("8.8.8.8")
assert result["hostname"] == "dns.google"
assert result["error"] is None

Missing PTR record:

result = reverse_dns("192.168.1.1")
assert result["hostname"] is None
assert result["error"] == "No PTR record"

IPv6 address:

result = reverse_dns("2001:4860:4860::8888")
print(result["hostname"])  # dns.google

Using a custom nameserver:

result = reverse_dns("8.8.8.8", nameserver="1.1.1.1")
nadzoring.dns_lookup.trace_dns(domain: str, nameserver: str | None = None) dict[str, Any][source]

Trace the complete DNS resolution path for domain.

Follows the delegation chain from the specified (or root) nameserver to the authoritative answer, similar to dig +trace. Loop detection and a maximum-hop limit prevent infinite recursion.

Parameters:
  • domain – Domain name to trace (e.g. "example.com").

  • nameserver – Starting nameserver IP. Defaults to a.root-servers.net (198.41.0.4) when None.

Returns:

Dict with domain, hops (list of hop dicts), and final_answer (the authoritative hop, or None).

Examples

>>> result = trace_dns("example.com")
>>> for hop in result["hops"]:
...     print(hop["nameserver"], hop["response_time"])

Submodules