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: Literal['A', 'AAAA', 'CNAME', 'MX', 'NS', 'TXT', 'PTR', 'SOA', 'DNSKEY'] = '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 multiple DNS servers either in parallel or sequentially, measuring response times and success rates. Results are sorted by average response time for easy comparison.
- Parameters:
domain – Domain name to query for benchmarking. Defaults to “google.com”.
servers – List of DNS server IP addresses to benchmark. If None, uses get_public_dns_servers() for a comprehensive list of public DNS providers.
record_type – DNS record type to query. Defaults to “A” records.
queries – Number of queries to perform per server. Defaults to 10.
max_workers – Maximum number of parallel threads when parallel=True. Defaults to 5. Ignored when parallel=False.
progress_callback – Optional callback function called after each server completes benchmarking. Receives the server IP and the completion index (1-based). Useful for UI progress bars.
parallel – If True, benchmarks servers in parallel using multiple threads. If False, benchmarks sequentially. Defaults to True.
- Returns:
- List of benchmark results for each server,
sorted by average response time (fastest first). Each result contains the same fields as benchmark_single_server().
- Return type:
List[BenchmarkResult]
Examples
>>> # Basic parallel benchmark of all public DNS servers >>> results = benchmark_dns_servers() >>> for i, r in enumerate(results[:5], 1): ... print(f"{i}. {r['server']}: {r['avg_response_time']:.2f}ms")
>>> # Benchmark specific servers sequentially with progress tracking >>> def progress(server: str, index: int): ... print(f"[{index}] Completed {server}") >>> >>> results = benchmark_dns_servers( ... servers=["8.8.8.8", "1.1.1.1", "9.9.9.9"], ... parallel=False, ... progress_callback=progress, ... )
>>> # Custom benchmark with more queries >>> results = benchmark_dns_servers( ... domain="example.com", ... servers=["8.8.8.8", "1.1.1.1"], ... queries=20, ... max_workers=2, ... ) >>> fastest = results[0] >>> slowest = results[-1] >>> print( ... f"Fastest: {fastest['server']} ({fastest['avg_response_time']:.2f}ms)" ... ) >>> print( ... f"Slowest: {slowest['server']} ({slowest['avg_response_time']:.2f}ms)" ... )
Notes
Parallel benchmarking uses ThreadPoolExecutor for concurrent queries
Results are always sorted by average response time (fastest first)
Failed servers are still included in results with 0 response times
The progress callback receives both server IP and completion index
Logging at debug level captures individual server failures
Consider rate limiting when using parallel=True with many servers
- Default server list includes major public DNS providers:
Google, Cloudflare, OpenDNS, Quad9, Verisign, and others
- nadzoring.dns_lookup.check_dns(domain: str, nameserver: str | None = None, record_types: list[str] | None = None, *, validate_mx: bool = False, validate_txt: bool = False) DetailedCheckResult[source]¶
Perform a comprehensive DNS check with detailed per-record information.
Queries specified DNS record types for a domain and returns detailed information including actual records, response times, errors, and optional validation results for MX and TXT records.
- Parameters:
domain – Domain name to check (e.g., “example.com”).
nameserver – Optional specific nameserver IP to use for queries. If None, uses system default resolvers.
record_types – List of DNS record types to query. If None, defaults to [“A”, “AAAA”, “MX”, “NS”, “TXT”, “CNAME”].
validate_mx – If True, perform additional validation on MX records (checks for duplicate priorities).
validate_txt – If True, perform additional validation on TXT records (checks SPF and DKIM compliance).
- Returns:
- Dictionary containing detailed check results:
domain: The domain that was checked
records: Dict mapping record types to lists of resolved records
errors: Dict mapping record types to error messages (if any)
response_times: Dict mapping record types to response times in ms
- validations: Dict containing validation results:
mx: MX validation result (if validate_mx=True and MX records exist)
txt: TXT validation result (if validate_txt=True, TXT records exist)
- Return type:
Examples
>>> # Basic check with default record types >>> result = check_dns("example.com") >>> if "A" in result["records"]: ... print(f"A records: {result['records']['A']}")
>>> # Check specific record types with validation >>> result = check_dns( ... "example.com", ... record_types=["MX", "TXT"], ... validate_mx=True, ... validate_txt=True, ... ) >>> if "validations" in result: ... mx_valid = result["validations"].get("mx", {}) ... if not mx_valid.get("valid", True): ... print("MX issues:", mx_valid.get("issues", []))
>>> # Check with custom nameserver >>> result = check_dns( ... "example.com", nameserver="1.1.1.1", record_types=["A", "AAAA"] ... ) >>> for rtype, time in result["response_times"].items(): ... print(f"{rtype} resolved in {time}ms")
Notes
Response times are in milliseconds, rounded to 2 decimal places
Records without trailing dots for consistency
MX validation checks for duplicate priorities
TXT validation checks SPF for missing all and DKIM for missing public key
Errors are recorded per record type if resolution fails
- 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 signs of DNS poisoning, censorship, or manipulation.
Comprehensive DNS poisoning detection by comparing responses from multiple DNS resolvers against a trusted control resolver. Analyzes patterns, identifies inconsistencies, and provides confidence scoring.
- Parameters:
domain – Domain name to test for poisoning (e.g., “example.com”).
control_server – Trusted DNS server IP to use as baseline comparison. Defaults to Google DNS (8.8.8.8).
test_servers – List of DNS server IPs to test. If None, uses get_public_dns_servers() for a comprehensive list.
record_type – DNS record type to query for poisoning detection. Defaults to “A” records.
additional_types – Optional list of additional record types to query from the control server for extra context.
- Returns:
- Comprehensive poisoning analysis containing:
domain: The tested domain
record_type: The record type queried
control_server: IP of control server used
control_result: DNSResult from control server
test_results: Dict mapping test servers to their DNSResults
inconsistencies: List of detected inconsistencies
poisoned: Boolean indicating poisoning detection
- poisoning_level: Severity level (“NONE”, “LOW”, “MEDIUM”,
”HIGH”, “CRITICAL”, “SUSPICIOUS”)
confidence: Confidence score (0-100)
Additional metrics including CDN detection, geo-diversity, consensus analysis, and more
- Return type:
Examples
>>> # Basic poisoning check >>> result = check_dns_poisoning("example.com") >>> if result["poisoned"]: ... print(f"Poisoning detected! Confidence: {result['confidence']}%")
>>> # Check multiple record types >>> result = check_dns_poisoning("example.com", additional_types=["MX", "TXT"])
>>> # Custom test servers >>> result = check_dns_poisoning( ... "example.com", test_servers=["1.1.1.1", "9.9.9.9"] ... )
Notes
High confidence (>80%) with mismatches indicates probable poisoning
CDN variations are flagged as informational, not poisoning
Geographic diversity of test servers improves detection accuracy
Timeout errors are logged but don’t affect poisoning detection
- 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.
Queries multiple DNS servers for the same set of record types and identifies differences in responses. Uses the first server in the list as the baseline for comparison.
- Parameters:
domain – Domain name to query (e.g., “example.com”).
servers – List of DNS server IP addresses to compare. The first server in the list is used as the baseline.
record_types – List of DNS record types to query for each server (e.g., [“A”, “MX”, “TXT”]).
progress_callback – Optional callback function called after each successful query to report progress. Useful for UI progress bars.
- Returns:
- Dictionary containing comparison results:
domain: The domain that was queried
- servers: Nested dictionary mapping server IPs to their results:
- {
- “server_ip”: {
“A”: DNSResult, “MX”: DNSResult, …
}
- differences: List of detected differences, each containing:
server: IP of the differing server
type: Record type that differs
expected: Records from baseline server
got: Records from this server
ttl_difference: TTL difference if applicable
- Return type:
Examples
>>> # Basic comparison >>> result = compare_dns_servers( ... "example.com", ... servers=["8.8.8.8", "1.1.1.1", "9.9.9.9"], ... record_types=["A", "MX"], ... ) >>> for diff in result["differences"]: ... print(f"{diff['server']}: {diff['type']} differs") ... print(f" Expected: {diff['expected']}") ... print(f" Got: {diff['got']}")
>>> # With progress tracking >>> def update_progress(): ... print(".", end="", flush=True) >>> >>> result = compare_dns_servers( ... "example.com", ... servers=["8.8.8.8", "1.1.1.1", "9.9.9.9"], ... record_types=["A", "AAAA", "MX"], ... progress_callback=update_progress, ... )
>>> # Check if all servers agree >>> if not result["differences"]: ... print("All servers returned identical results") >>> else: ... print(f"Found {len(result['differences'])} differences")
Notes
The first server in the list serves as the baseline for all comparisons
TTL values are included in the DNSResult objects but not compared by default
Differences are recorded only when the actual records differ, not TTLs
Each query includes TTL information (include_ttl=True)
The progress callback is called after each successful query, allowing for accurate progress tracking in UIs
Empty responses (no records) are considered valid and compared normally
Error responses are included in the comparison and may cause differences
- nadzoring.dns_lookup.health_check_dns(domain: str, nameserver: str | None = None) HealthCheckResult[source]¶
Perform a comprehensive DNS health check with scoring.
Evaluates the health of a domain’s DNS configuration by checking multiple record types, calculating individual scores, and producing an overall health score and status.
- Parameters:
domain – Domain name to check (e.g., “example.com”).
nameserver – Optional specific nameserver IP to use for queries. If None, uses system default resolvers.
- Returns:
- Dictionary containing health check results:
domain: The domain that was checked
score: Overall health score (0-100)
status: Health status (‘healthy’, ‘degraded’, ‘unhealthy’)
issues: List of critical issues found during checks
warnings: List of non-critical warnings found
- record_scores: Dict with scores for each record type:
A: IPv4 address records score
AAAA: IPv6 address records score
MX: Mail exchange records score
NS: Nameserver records score
TXT: Text records score
CNAME: Canonical name records score (if applicable)
- Return type:
Examples
>>> # Basic health check >>> result = health_check_dns("example.com") >>> print(f"Health score: {result['score']} - {result['status']}") >>> for rtype, score in result["record_scores"].items(): ... print(f" {rtype}: {score}")
>>> # Using specific nameserver >>> result = health_check_dns("example.com", nameserver="8.8.8.8") >>> if result["issues"]: ... print("Issues found:", result["issues"])
Notes
Checks all major record types: A, AAAA, MX, NS, TXT, CNAME
CNAME records are only scored for subdomains (as per DNS standards)
Scores are calculated using validation rules from validation module
The final score is the average of all non-CNAME record scores
- Status is determined by the overall score:
>= 80: healthy
50-79: degraded
< 50: unhealthy
- nadzoring.dns_lookup.resolve_dns(domain: str, record_type: Literal['A', 'AAAA', 'CNAME', 'MX', 'NS', 'TXT', 'PTR', 'SOA', 'DNSKEY'] = 'A', nameserver: str | None = None, *, include_ttl: bool = False, timeout: float = 5.0, lifetime: float = 10.0) DNSResult¶
Perform DNS resolution with timing information and error handling.
Resolves a domain name for a specific record type, measuring response time and optionally capturing TTL information. Handles common DNS errors gracefully.
- Parameters:
domain – Domain name to resolve (e.g., “example.com”).
record_type – DNS record type to query (e.g., “A”, “MX”, “TXT”). Defaults to “A”.
nameserver – Optional specific nameserver IP to use for resolution. If None, uses system default.
include_ttl – Whether to include TTL (Time To Live) value in result. Defaults to False.
timeout – Query timeout in seconds for each nameserver. Defaults to 5.0.
lifetime – Total query lifetime in seconds, including retries. Defaults to 10.0.
- Returns:
- Dictionary containing resolution results with structure:
domain: The queried domain name
record_type: The queried record type
records: List of resolved records (empty if resolution failed)
ttl: TTL in seconds if include_ttl=True and available, else None
error: Error message string if resolution failed, else None
response_time: Query response time in milliseconds, rounded to 2 decimals
- Return type:
Example
>>> result = resolve_with_timer("example.com", "MX", include_ttl=True) >>> if not result["error"]: ... print(f"Records: {result['records']}, TTL: {result['ttl']}")
- 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 (Pointer) record for a given IP address to find the associated domain name. This is the reverse of a forward DNS lookup.
- Parameters:
ip_address – IPv4 or IPv6 address to look up (e.g., “8.8.8.8” or “2001:4860:4860::8888”).
nameserver – Optional specific nameserver IP address to use for the query. If None, uses the system default resolvers.
- Returns:
- A dictionary containing:
ip_address (str): The original IP address that was queried.
hostname (Optional[str]): The resolved hostname if found, with trailing dot removed. None if resolution failed.
error (Optional[str]): Error message if lookup failed, None for successful lookups.
response_time (Optional[float]): Query response time in milliseconds, rounded to 2 decimal places. None if the query failed before timing could be recorded.
- Return type:
Dict[str, Union[str, float, None]]
Examples
>>> # Successful reverse lookup >>> result = reverse_dns("8.8.8.8") >>> print(result["hostname"]) 'dns.google' >>> print(f"Resolved in {result['response_time']}ms")
>>> # Failed reverse lookup >>> result = reverse_dns("192.168.1.1") >>> print(result["error"]) 'No PTR record'
>>> # Using specific nameserver >>> result = reverse_dns("1.1.1.1", nameserver="9.9.9.9")
Notes
The function handles both IPv4 and IPv6 addresses automatically using dns.reversename.from_address().
- Common errors include:
“No PTR record”: IP exists but has no reverse DNS configured
“No reverse DNS”: IP range has no reverse delegation
“Query timeout”: DNS server didn’t respond in time
Trailing dots are automatically removed from hostnames for consistency with forward lookup formats.
Debug logs are generated for failed lookups to aid troubleshooting.
- nadzoring.dns_lookup.trace_dns(domain: str, nameserver: str | None = None) dict[str, Any][source]¶
Trace the complete DNS resolution path for a domain.
Performs a DNS trace following the delegation chain from root servers to authoritative nameservers, similar to dig +trace functionality.
- Parameters:
domain – Domain name to trace (e.g., “example.com”).
nameserver – Optional starting nameserver IP. If None, starts from root server (198.41.0.4 - a.root-servers.net).
- Returns:
- Trace result containing:
domain: The domain that was traced
- hops: List of hop dictionaries, each representing a nameserver
queried along the path
- final_answer: The hop dictionary containing the final answer
(None if resolution failed)
- Return type:
Dict[str, Any]
Example
>>> result = trace_dns("example.com") >>> for i, hop in enumerate(result["hops"]): ... print(f"Hop {i + 1}: {hop['nameserver']} ({hop['response_time']}ms)") >>> if result["final_answer"]: ... print(f"Final answer: {result['final_answer']['records']}")
Notes
Maximum hops limited to 30 to prevent infinite loops
Detects and reports loops in delegation chain
Tracks visited nameservers to avoid repetition
Gracefully handles delegation failures and errors
Submodules¶
- nadzoring.dns_lookup.benchmark module
- nadzoring.dns_lookup.compare module
- nadzoring.dns_lookup.health module
DetailedCheckResultHealthCheckResultHealthCheckResult.domainHealthCheckResult.scoreHealthCheckResult.statusHealthCheckResult.issuesHealthCheckResult.warningsHealthCheckResult.record_scoresHealthCheckResult.domainHealthCheckResult.issuesHealthCheckResult.record_scoresHealthCheckResult.scoreHealthCheckResult.statusHealthCheckResult.warnings
check_dns()health_check_dns()
- nadzoring.dns_lookup.poisoning module
CDN_NETWORKSIPAnalysisResultIPAnalysisResult.countIPAnalysisResult.uniqueIPAnalysisResult.ipv4IPAnalysisResult.ipv6IPAnalysisResult.privateIPAnalysisResult.reservedIPAnalysisResult.ownersIPAnalysisResult.countriesIPAnalysisResult.countIPAnalysisResult.countriesIPAnalysisResult.ipv4IPAnalysisResult.ipv6IPAnalysisResult.ownersIPAnalysisResult.privateIPAnalysisResult.reservedIPAnalysisResult.unique
InconsistencyDetailInconsistencyDetail.serverInconsistencyDetail.server_nameInconsistencyDetail.server_countryInconsistencyDetail.typeInconsistencyDetail.severityInconsistencyDetail.control_errorInconsistencyDetail.test_errorInconsistencyDetail.control_recordsInconsistencyDetail.test_recordsInconsistencyDetail.control_ttlInconsistencyDetail.test_ttlInconsistencyDetail.diffInconsistencyDetail.common_recordsInconsistencyDetail.control_analysisInconsistencyDetail.test_analysisInconsistencyDetail.ownerInconsistencyDetail.control_ownerInconsistencyDetail.test_ownerInconsistencyDetail.common_recordsInconsistencyDetail.control_analysisInconsistencyDetail.control_errorInconsistencyDetail.control_ownerInconsistencyDetail.control_recordsInconsistencyDetail.control_ttlInconsistencyDetail.diffInconsistencyDetail.ownerInconsistencyDetail.serverInconsistencyDetail.server_countryInconsistencyDetail.server_nameInconsistencyDetail.severityInconsistencyDetail.test_analysisInconsistencyDetail.test_errorInconsistencyDetail.test_ownerInconsistencyDetail.test_recordsInconsistencyDetail.test_ttlInconsistencyDetail.type
MetricsResultMetricsResult.total_testedMetricsResult.poisonedMetricsResult.confidenceMetricsResult.mismatchesMetricsResult.cdn_variationsMetricsResult.cdn_detectedMetricsResult.cdn_ownerMetricsResult.cdn_percentageMetricsResult.unique_ips_seenMetricsResult.ip_diversityMetricsResult.control_ip_countMetricsResult.consensus_topMetricsResult.consensus_rateMetricsResult.geo_diversityMetricsResult.anycast_likelyMetricsResult.cdn_likelyMetricsResult.poisoning_likelyMetricsResult.anycast_likelyMetricsResult.cdn_detectedMetricsResult.cdn_likelyMetricsResult.cdn_ownerMetricsResult.cdn_percentageMetricsResult.cdn_variationsMetricsResult.confidenceMetricsResult.consensus_rateMetricsResult.consensus_topMetricsResult.control_ip_countMetricsResult.geo_diversityMetricsResult.ip_diversityMetricsResult.mismatchesMetricsResult.poisonedMetricsResult.poisoning_likelyMetricsResult.total_testedMetricsResult.unique_ips_seen
SERVER_COUNTRIESSERVER_NAMES_analyze_ip_patterns()_build_result()_calculate_metrics()_compare_results()_count_severities()_determine_poisoning_level()_get_additional_records()_test_dns_servers()check_dns_poisoning()get_ip_owner()is_likely_cdn()
- nadzoring.dns_lookup.reverse module
- nadzoring.dns_lookup.trace module
- nadzoring.dns_lookup.types module
BenchmarkResultBenchmarkResult.serverBenchmarkResult.avg_response_timeBenchmarkResult.min_response_timeBenchmarkResult.max_response_timeBenchmarkResult.success_rateBenchmarkResult.total_queriesBenchmarkResult.failed_queriesBenchmarkResult.responsesBenchmarkResult.avg_response_timeBenchmarkResult.failed_queriesBenchmarkResult.max_response_timeBenchmarkResult.min_response_timeBenchmarkResult.responsesBenchmarkResult.serverBenchmarkResult.success_rateBenchmarkResult.total_queries
DNSResultPoisoningCheckResultPoisoningCheckResult.domainPoisoningCheckResult.control_resultPoisoningCheckResult.test_resultsPoisoningCheckResult.inconsistenciesPoisoningCheckResult.poisonedPoisoningCheckResult.confidencePoisoningCheckResult.avg_response_timePoisoningCheckResult.confidencePoisoningCheckResult.control_resultPoisoningCheckResult.domainPoisoningCheckResult.failed_queriesPoisoningCheckResult.inconsistenciesPoisoningCheckResult.max_response_timePoisoningCheckResult.min_response_timePoisoningCheckResult.poisonedPoisoningCheckResult.responsesPoisoningCheckResult.serverPoisoningCheckResult.success_ratePoisoningCheckResult.test_resultsPoisoningCheckResult.total_queries
RECORD_TYPESRecordType
- nadzoring.dns_lookup.utils module
- nadzoring.dns_lookup.validation module