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: TypedDict

Result 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: TypedDict

Detailed 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.

Type:

nadzoring.dns_lookup.poisoning.IPAnalysisResult

test_analysis

IP analysis for test records.

Type:

nadzoring.dns_lookup.poisoning.IPAnalysisResult

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: TypedDict

Calculated 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:

IPAnalysisResult

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:

PoisoningCheckResult

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:

MetricsResult

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:

PoisoningCheckResult

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.