mirror of
https://github.com/Horizontal42/netsleuth.git
synced 2026-11-11 01:02:10 +03:00
dns_leak: per-adapter resolver enumeration and echo probes
This commit is contained in:
parent
0999f85235
commit
8d38788671
1 changed files with 72 additions and 0 deletions
|
|
@ -76,3 +76,75 @@ def build_dns_leak(results: list[AdapterLeakResult], ecs_leaked: bool) -> DnsLea
|
||||||
parts.append("The resolver forwards your EDNS Client Subnet, exposing your network to authoritative servers.")
|
parts.append("The resolver forwards your EDNS Client Subnet, exposing your network to authoritative servers.")
|
||||||
parts.append(_LEAK_NOTE)
|
parts.append(_LEAK_NOTE)
|
||||||
return DnsLeak(per_adapter=results, ecs_leaked=ecs_leaked, note=" ".join(parts))
|
return DnsLeak(per_adapter=results, ecs_leaked=ecs_leaked, note=" ".join(parts))
|
||||||
|
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
import dns.asyncresolver
|
||||||
|
import dns.exception
|
||||||
|
|
||||||
|
from netcheck.bgp import parse_cymru_origin
|
||||||
|
from netcheck.models import LocalNet
|
||||||
|
|
||||||
|
MYADDR_NAME = "o-o.myaddr.l.google.com"
|
||||||
|
AKAHELP_NAME = "whoami.ds.akahelp.net"
|
||||||
|
|
||||||
|
|
||||||
|
async def _txt(resolver: dns.asyncresolver.Resolver, name: str) -> list[str]:
|
||||||
|
try:
|
||||||
|
answer = await resolver.resolve(name, "TXT")
|
||||||
|
except dns.exception.DNSException:
|
||||||
|
return []
|
||||||
|
return [b" ".join(record.strings).decode("utf-8", "replace") for record in answer]
|
||||||
|
|
||||||
|
|
||||||
|
def _resolver_for(server: str, timeout: float) -> dns.asyncresolver.Resolver:
|
||||||
|
resolver = dns.asyncresolver.Resolver(configure=False)
|
||||||
|
resolver.nameservers = [server]
|
||||||
|
resolver.lifetime = timeout
|
||||||
|
return resolver
|
||||||
|
|
||||||
|
|
||||||
|
async def echo_probe(resolver_ip: str, timeout: float) -> tuple[str | None, dict[str, str]]:
|
||||||
|
resolver = _resolver_for(resolver_ip, timeout)
|
||||||
|
myaddr, akahelp = await asyncio.gather(
|
||||||
|
_txt(resolver, MYADDR_NAME), _txt(resolver, AKAHELP_NAME)
|
||||||
|
)
|
||||||
|
parsed = parse_akahelp(akahelp)
|
||||||
|
return parse_myaddr(myaddr) or parsed.get("ns"), parsed
|
||||||
|
|
||||||
|
|
||||||
|
async def asn_for_ip(ip: str, zone: str, timeout: float) -> str | None:
|
||||||
|
try:
|
||||||
|
reversed_ip = ".".join(reversed(ip.split(".")))
|
||||||
|
except AttributeError:
|
||||||
|
return None
|
||||||
|
resolver = dns.asyncresolver.Resolver()
|
||||||
|
resolver.lifetime = timeout
|
||||||
|
records = await _txt(resolver, f"{reversed_ip}.{zone}")
|
||||||
|
for record in records:
|
||||||
|
parsed = parse_cymru_origin(record)
|
||||||
|
if parsed.get("asn"):
|
||||||
|
return parsed["asn"]
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
async def collect_dns_leak(
|
||||||
|
local: LocalNet,
|
||||||
|
egress_asn: str | None,
|
||||||
|
cymru_zone: str,
|
||||||
|
timeout: float,
|
||||||
|
) -> DnsLeak:
|
||||||
|
results: list[AdapterLeakResult] = []
|
||||||
|
ecs_leaked = False
|
||||||
|
for adapter, resolvers in local.dns_servers_per_adapter.items():
|
||||||
|
if not resolvers:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
echoed_ip, akahelp = await echo_probe(resolvers[0], timeout)
|
||||||
|
except dns.exception.DNSException:
|
||||||
|
echoed_ip, akahelp = None, {}
|
||||||
|
ecs_leaked = ecs_leaked or detect_ecs_leak(akahelp)
|
||||||
|
echoed_asn = await asn_for_ip(echoed_ip, cymru_zone, timeout) if echoed_ip else None
|
||||||
|
results.append(build_adapter_result(adapter, resolvers, echoed_ip, echoed_asn, egress_asn))
|
||||||
|
return build_dns_leak(results, ecs_leaked)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue