nadzoring.dns_lookup.poisoning module¶
DNS poisoning detection and analysis functionality.
This module provides tools to detect DNS cache poisoning, censorship, and manipulation by comparing responses from multiple DNS resolvers across different geographic locations and providers.
- nadzoring.dns_lookup.poisoning.CDN_NETWORKS: dict[str, list[str]] = {'Akamai': ['23.32.0.0/11', '23.64.0.0/14', '23.72.0.0/13', '23.192.0.0/11', '23.224.0.0/13', '23.248.0.0/14', '2.16.0.0/13', '2.20.0.0/14', '2.22.0.0/15', '2.23.0.0/16', '69.192.0.0/16', '95.100.0.0/15', '96.6.0.0/15', '104.64.0.0/10'], 'Amazon AWS': ['13.32.0.0/15', '13.224.0.0/14', '52.84.0.0/15', '54.182.0.0/16', '54.192.0.0/16', '54.230.0.0/16', '54.239.128.0/18', '99.84.0.0/15', '143.204.0.0/16', '144.220.0.0/16', '13.248.0.0/14', '15.248.0.0/16', '18.64.0.0/14', '52.124.0.0/14', '52.222.0.0/15', '54.230.0.0/16', '54.239.128.0/18'], 'Azure CDN': ['13.73.0.0/16', '13.80.0.0/15', '13.88.0.0/16', '13.104.0.0/14', '13.107.128.0/22', '40.90.0.0/15', '40.126.0.0/18', '52.168.0.0/14', '52.224.0.0/14', '52.239.0.0/15'], 'CloudFront': ['13.32.0.0/15', '13.224.0.0/14', '13.249.0.0/16', '18.64.0.0/14', '18.154.0.0/15', '52.84.0.0/15', '54.182.0.0/16', '54.192.0.0/16', '54.230.0.0/16', '54.239.128.0/18', '99.84.0.0/15', '143.204.0.0/16', '144.220.0.0/16', '146.254.0.0/16'], 'Cloudflare': ['1.1.1.0/24', '1.0.0.0/24', '104.16.0.0/12', '172.64.0.0/13', '141.101.64.0/18', '108.162.192.0/18', '190.93.240.0/20', '188.114.96.0/20', '197.234.240.0/22', '198.41.128.0/17', '162.158.0.0/15', '173.245.48.0/20', '103.21.244.0/22', '103.22.200.0/22', '103.31.4.0/22', '141.101.64.0/18', '108.162.192.0/18', '190.93.240.0/20', '188.114.96.0/20', '197.234.240.0/22', '198.41.128.0/17'], 'Facebook': ['31.13.24.0/21', '31.13.64.0/18', '45.64.40.0/22', '66.220.144.0/20', '69.63.176.0/20', '69.171.224.0/19', '74.119.76.0/22', '102.132.96.0/20', '103.4.96.0/22', '129.134.0.0/17', '157.240.0.0/17', '173.252.64.0/18', '179.60.192.0/22', '185.60.216.0/22', '204.15.20.0/22'], 'Fastly': ['23.235.32.0/20', '104.156.80.0/20', '151.101.0.0/16', '157.52.64.0/18', '172.111.64.0/18', '185.31.16.0/22', '199.27.72.0/21', '199.232.0.0/16'], 'Google': ['8.8.8.0/24', '8.8.4.0/24', '64.233.160.0/19', '66.102.0.0/20', '66.249.64.0/19', '72.14.192.0/18', '74.125.0.0/16', '108.177.8.0/21', '142.250.0.0/15', '172.217.0.0/16', '173.194.0.0/16', '207.126.144.0/20', '209.85.128.0/17', '216.58.192.0/19', '216.239.32.0/19'], 'Mail.ru': ['94.100.176.0/20', '95.163.0.0/16', '185.5.128.0/22', '185.30.176.0/22', '185.86.176.0/22', '217.69.128.0/20'], 'Microsoft': ['13.64.0.0/11', '13.96.0.0/13', '13.104.0.0/14', '20.0.0.0/8', '40.64.0.0/10', '52.96.0.0/14', '52.112.0.0/14', '52.120.0.0/14', '104.40.0.0/13', '104.208.0.0/13'], 'Netflix': ['3.160.0.0/12', '23.192.0.0/11', '34.192.0.0/10', '52.48.0.0/12', '54.144.0.0/12', '108.128.0.0/12', '184.72.0.0/14', '185.2.220.0/22', '185.48.244.0/22'], 'Twitter': ['104.244.40.0/21', '199.16.156.0/22', '199.59.148.0/22', '192.133.76.0/22', '209.237.192.0/19', '69.195.160.0/19'], 'Yandex': ['5.45.192.0/18', '37.9.64.0/18', '77.88.0.0/18', '84.252.128.0/17', '87.250.224.0/19', '93.158.128.0/18', '95.108.128.0/17', '141.8.128.0/18', '199.21.96.0/22', '213.180.192.0/19']}¶
Known CDN and cloud provider network ranges for IP ownership detection.
- class nadzoring.dns_lookup.poisoning.IPAnalysisResult[source]¶
Bases:
TypedDictResult of IP address pattern analysis.
Contains detailed statistics and classifications for a set of IP addresses returned by DNS resolvers.
- count¶
Total number of IP addresses analyzed.
- Type:
int
- unique¶
Number of unique IP addresses in the set.
- Type:
int
- ipv4¶
Count of IPv4 addresses.
- Type:
int
- ipv6¶
Count of IPv6 addresses.
- Type:
int
- private¶
Count of private (RFC 1918) IP addresses.
- Type:
int
- reserved¶
Count of reserved IP addresses.
- Type:
int
- owners¶
List of inferred owners for each IP (CDN/provider names).
- Type:
list[str]
- countries¶
List of inferred countries for each IP.
- Type:
list[str]
- count: int¶
- countries: list[str]¶
- ipv4: int¶
- ipv6: int¶
- owners: list[str]¶
- private: int¶
- reserved: int¶
- unique: int¶
- class nadzoring.dns_lookup.poisoning.InconsistencyDetail[source]¶
Bases:
TypedDictDetailed information about a DNS response inconsistency.
- server¶
IP address of the DNS server that returned inconsistent results.
- Type:
str
- server_name¶
Provider name of the server (from SERVER_NAMES).
- Type:
str
- server_country¶
Country code of the server (from SERVER_COUNTRIES).
- Type:
str
- type¶
Type of inconsistency (‘error_mismatch’, ‘record_mismatch’, ‘cdn_variation’, ‘ttl_mismatch’).
- Type:
Literal[‘error_mismatch’, ‘record_mismatch’, ‘cdn_variation’, ‘ttl_mismatch’]
- severity¶
Impact severity (‘high’, ‘medium’, ‘low’, ‘info’).
- Type:
Literal[‘high’, ‘medium’, ‘low’, ‘info’]
- control_error¶
Error from control server (if any).
- Type:
str | None
- test_error¶
Error from test server (if any).
- Type:
str | None
- control_records¶
Records from control server.
- Type:
list[str]
- test_records¶
Records from test server.
- Type:
list[str]
- control_ttl¶
TTL from control server.
- Type:
int | None
- test_ttl¶
TTL from test server.
- Type:
int | None
- diff¶
Difference description or value.
- Type:
str | int | None
- common_records¶
Records common to both responses (optional).
- Type:
list[str] | None
- control_analysis¶
IP analysis for control records.
- test_analysis¶
IP analysis for test records.
- owner¶
Common owner if applicable (optional).
- Type:
str | None
- control_owner¶
Owner of control records (optional).
- Type:
str | None
- test_owner¶
Owner of test records (optional).
- Type:
str | None
- common_records: list[str] | None¶
- control_analysis: IPAnalysisResult¶
- control_error: str | None¶
- control_owner: str | None¶
- control_records: list[str]¶
- control_ttl: int | None¶
- diff: str | int | None¶
- owner: str | None¶
- server: str¶
- server_country: str¶
- server_name: str¶
- severity: Literal['high', 'medium', 'low', 'info']¶
- test_analysis: IPAnalysisResult¶
- test_error: str | None¶
- test_owner: str | None¶
- test_records: list[str]¶
- test_ttl: int | None¶
- type: Literal['error_mismatch', 'record_mismatch', 'cdn_variation', 'ttl_mismatch']¶
- class nadzoring.dns_lookup.poisoning.MetricsResult[source]¶
Bases:
TypedDictCalculated metrics from DNS poisoning test.
- total_tested¶
Number of test servers queried.
- Type:
int
- poisoned¶
Boolean indicating if poisoning was detected.
- Type:
bool
- confidence¶
Confidence score (0-100) of poisoning detection.
- Type:
float
- mismatches¶
Count of record mismatches found.
- Type:
int
- cdn_variations¶
Count of CDN variations found.
- Type:
int
- cdn_detected¶
Whether CDN usage was detected.
- Type:
bool
- cdn_owner¶
Name of detected CDN provider.
- Type:
str
- cdn_percentage¶
Percentage of IPs belonging to CDN.
- Type:
float
- unique_ips_seen¶
Number of unique IPs across all test results.
- Type:
int
- ip_diversity¶
Number of IPs not in control results.
- Type:
int
- control_ip_count¶
Number of IPs in control results.
- Type:
int
- consensus_top¶
Top 3 most common IPs and their counts.
- Type:
list[tuple[str, int]]
- consensus_rate¶
Percentage of servers returning the most common IP.
- Type:
float
- geo_diversity¶
Number of unique countries among test servers.
- Type:
int
- anycast_likely¶
Whether anycast routing is likely.
- Type:
bool
- cdn_likely¶
Whether CDN usage is likely.
- Type:
bool
- poisoning_likely¶
Whether poisoning is likely.
- Type:
bool
- anycast_likely: bool¶
- cdn_detected: bool¶
- cdn_likely: bool¶
- cdn_owner: str¶
- cdn_percentage: float¶
- cdn_variations: int¶
- confidence: float¶
- consensus_rate: float¶
- consensus_top: list[tuple[str, int]]¶
- control_ip_count: int¶
- geo_diversity: int¶
- ip_diversity: int¶
- mismatches: int¶
- poisoned: bool¶
- poisoning_likely: bool¶
- total_tested: int¶
- unique_ips_seen: int¶
- nadzoring.dns_lookup.poisoning.SERVER_COUNTRIES: dict[str, str] = {'1.0.0.1': 'AU', '1.1.1.1': 'AU', '149.112.112.112': 'CH', '185.228.168.9': 'CA', '185.228.169.9': 'CA', '208.67.220.220': 'US', '208.67.222.222': 'US', '64.6.64.6': 'US', '64.6.65.6': 'US', '76.76.19.19': 'CA', '8.8.4.4': 'US', '8.8.8.8': 'US', '9.9.9.9': 'CH', '94.140.14.14': 'CY', '94.140.15.15': 'CY'}¶
Mapping of public DNS server IP addresses to their country codes.
- nadzoring.dns_lookup.poisoning.SERVER_NAMES: dict[str, str] = {'1.0.0.1': 'Cloudflare', '1.1.1.1': 'Cloudflare', '149.112.112.112': 'Quad9', '185.228.168.9': 'CleanBrowsing', '185.228.169.9': 'CleanBrowsing', '208.67.220.220': 'OpenDNS', '208.67.222.222': 'OpenDNS', '64.6.64.6': 'Verisign', '64.6.65.6': 'Verisign', '76.76.19.19': 'ControlD', '8.8.4.4': 'Google', '8.8.8.8': 'Google', '9.9.9.9': 'Quad9', '94.140.14.14': 'AdGuard', '94.140.15.15': 'AdGuard'}¶
Mapping of public DNS server IP addresses to their provider names.
- nadzoring.dns_lookup.poisoning._analyze_ip_patterns(records: list[str]) IPAnalysisResult[source]¶
Analyze IP address patterns for anomalies and characteristics.
Performs detailed analysis of IP addresses returned in DNS responses, classifying them by type, ownership, and geographic patterns.
- Parameters:
records – List of IP address strings to analyze.
- Returns:
- Dictionary containing analysis results:
count: Total number of records analyzed
unique: Number of unique IPs
ipv4: Count of IPv4 addresses
ipv6: Count of IPv6 addresses
private: Count of private IPs (RFC 1918)
reserved: Count of reserved IPs
owners: List of inferred owners for each IP
countries: List of inferred countries for each IP
- Return type:
Notes
Returns empty dict for empty input.
Country inference is simplified based on IP prefixes.
Exceptions during analysis are logged and skipped.
- nadzoring.dns_lookup.poisoning._build_result(domain: str, record_type: str, control_server: str, control_result: DNSResult, test_results: dict[str, DNSResult], additional_results: dict[str, DNSResult] | None, inconsistencies: list[InconsistencyDetail], mismatches: int, cdn_variations: int, metrics: MetricsResult, poisoning_level: str) PoisoningCheckResult[source]¶
Build the final poisoning check result dictionary.
Constructs the comprehensive PoisoningCheckResult by combining all collected data, analyses, and metrics.
- Parameters:
domain – Tested domain name.
record_type – DNS record type queried.
control_server – Control server IP address.
control_result – DNSResult from control server.
test_results – Dict of test server results.
additional_results – Optional additional record type results.
inconsistencies – List of detected inconsistencies.
mismatches – Count of record mismatches.
cdn_variations – Count of CDN variations.
metrics – Calculated metrics from _calculate_metrics.
poisoning_level – Determined poisoning level string.
- Returns:
- Complete poisoning check result with all fields
populated according to the type definition.
- Return type:
- nadzoring.dns_lookup.poisoning._calculate_metrics(test_results: dict[str, DNSResult], control_result: DNSResult, mismatches: int, cdn_variations: int) MetricsResult[source]¶
Calculate various metrics from test results for poisoning analysis.
Computes statistical measures including confidence scores, diversity metrics, and pattern detection for the poisoning check.
- Parameters:
test_results – Dict mapping server IPs to their DNSResults.
control_result – DNSResult from the control server.
mismatches – Count of record mismatches found.
cdn_variations – Count of CDN variations found.
- Returns:
- Comprehensive metrics including confidence scores,
diversity measures, and detection flags.
- Return type:
- nadzoring.dns_lookup.poisoning._compare_results(control: DNSResult, test: DNSResult, server: str, domain: str) InconsistencyDetail | None[source]¶
Compare control and test DNS results for inconsistencies.
Performs detailed comparison between a trusted control resolver and a test resolver, identifying various types of discrepancies.
- Parameters:
control – DNSResult from the control (trusted) resolver.
test – DNSResult from the test resolver to compare.
server – IP address of the test server.
domain – Domain name being tested (for context).
- Returns:
- Detailed inconsistency information if
discrepancies are found, None if results are consistent.
- Return type:
Optional[InconsistencyDetail]
- Types of inconsistencies detected:
error_mismatch: Different error states between resolvers
record_mismatch: Different record sets returned
cdn_variation: Different but CDN-related records (informational)
ttl_mismatch: Significant TTL differences (>1 hour)
Notes
Severity levels: high (potential poisoning), medium (suspicious), low (minor), info (informational)
CDN variations are flagged as “info” severity
Error mismatches with NXDOMAIN are considered high severity
- nadzoring.dns_lookup.poisoning._count_severities(inconsistencies: list[InconsistencyDetail]) dict[str, int][source]¶
Count inconsistencies by severity level.
Aggregates the count of inconsistencies for each severity category.
- Parameters:
inconsistencies – List of inconsistency details.
- Returns:
- Dictionary mapping severity levels to their counts:
”high”: Critical issues
”medium”: Suspicious issues
”low”: Minor issues
”info”: Informational items
- Return type:
Dict[str, int]
- nadzoring.dns_lookup.poisoning._determine_poisoning_level(confidence: float, *, poisoned: bool, cdn_detected: bool) str[source]¶
Determine the poisoning level based on confidence and CDN detection.
Maps numerical confidence and detection flags to a human-readable poisoning severity level.
- Parameters:
confidence – Confidence score (0-100).
poisoned – Whether poisoning was detected.
cdn_detected – Whether CDN usage was detected.
- Returns:
- Poisoning level classification:
”NONE”: No poisoning detected
”LOW”: Low confidence poisoning or with CDN
”MEDIUM”: Moderate confidence poisoning
”HIGH”: High confidence poisoning
”CRITICAL”: Very high confidence poisoning
”SUSPICIOUS”: High confidence but CDN detected
- Return type:
str
Notes
Returns “NONE” if not poisoned
CDN detection downgrades severity to “SUSPICIOUS” for high confidence
- nadzoring.dns_lookup.poisoning._get_additional_records(domain: str, additional_types: list[str] | None, control_server: str) dict[str, DNSResult] | None[source]¶
Retrieve additional DNS record types from the control server.
Queries the control server for supplementary record types to provide additional context for poisoning analysis.
- Parameters:
domain – Domain name to query.
additional_types – List of record types to query (e.g., [“MX”, “TXT”]).
control_server – IP address of the control DNS server.
- Returns:
- Dictionary mapping record types to
their DNS results, or None if no additional types requested.
- Return type:
Optional[Dict[str, DNSResult]]
- nadzoring.dns_lookup.poisoning._test_dns_servers(domain: str, record_type: str, test_servers: list[str], control_result: DNSResult) tuple[dict[str, DNSResult], list[InconsistencyDetail], int, int][source]¶
Test all DNS servers and collect results and inconsistencies.
Queries each test server and compares results against the control server to identify discrepancies.
- Parameters:
domain – Domain name to query.
record_type – DNS record type to query.
test_servers – List of test server IP addresses.
control_result – DNSResult from the control server for comparison.
- Returns:
test_results: Dict mapping server IPs to their DNSResults
inconsistencies: List of detected inconsistencies
mismatches: Count of record mismatches found
cdn_variations: Count of CDN variations found
- Return type:
Tuple containing
- nadzoring.dns_lookup.poisoning.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.poisoning.get_ip_owner(ip: str) str[source]¶
Determine the owner/provider of an IP address based on known network ranges.
Checks if the IP address falls within any known CDN or cloud provider network ranges defined in CDN_NETWORKS.
- Parameters:
ip – IP address string to check (IPv4 or IPv6).
- Returns:
- Name of the owner/provider if found in known networks,
otherwise returns “Unknown”.
- Return type:
str
Examples
>>> get_ip_owner("8.8.8.8") 'Google' >>> get_ip_owner("1.1.1.1") 'Cloudflare' >>> get_ip_owner("192.168.1.1") 'Unknown'
Notes
Only IPv4 networks are currently supported in CDN_NETWORKS.
IPv6 addresses will always return “Unknown” with current data.
Exceptions during IP parsing are logged and result in “Unknown”.
- nadzoring.dns_lookup.poisoning.is_likely_cdn(ips: list[str]) tuple[bool, str, float][source]¶
Determine if a set of IP addresses likely belongs to a CDN.
Analyzes IP addresses to detect patterns consistent with Content Delivery Network (CDN) usage, such as multiple IPs from the same provider.
- Parameters:
ips – List of IP address strings to analyze.
- Returns:
- A tuple containing:
is_cdn: True if more than 50% of IPs belong to the same known CDN.
owner: Name of the most common CDN owner found.
percentage: Percentage of IPs belonging to that owner (0-100).
- Return type:
Tuple[bool, str, float]
Examples
>>> ips = ["1.1.1.1", "1.0.0.1", "8.8.8.8"] >>> is_likely_cdn(ips) (True, 'Cloudflare', 66.7)
>>> ips = ["192.168.1.1", "10.0.0.1"] >>> is_likely_cdn(ips) (False, 'Unknown', 0.0)
Notes
Returns (False, “Unknown”, 0.0) for empty input.
The 50% threshold is used to determine CDN likelihood.
Only considers IPs from known CDN networks.