Build comprehensive privacy suite and hardened browser controls.
Adds VPN-aware leak handling, chain testing UX improvements, hardened Firefox launch/profile management, privacy/device hardening modules, and tray/status upgrades so the app is production-ready as the new baseline. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
112
proxy_chain_manager/dns_leak.py
Normal file
112
proxy_chain_manager/dns_leak.py
Normal file
@@ -0,0 +1,112 @@
|
||||
"""DNS leak checks and cache flush."""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import subprocess
|
||||
from dataclasses import dataclass
|
||||
|
||||
import httpx
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
_DOH_GOOGLE = "https://dns.google/resolve?name=whoami.dnsleaktest.com&type=A"
|
||||
|
||||
|
||||
@dataclass
|
||||
class DnsLeakResult:
|
||||
ok: bool
|
||||
system_resolvers: list[str]
|
||||
message: str
|
||||
doh_ip: str | None = None
|
||||
|
||||
|
||||
def get_system_dns_servers() -> list[str]:
|
||||
try:
|
||||
r = subprocess.run(
|
||||
["powershell", "-NoProfile", "-Command",
|
||||
"(Get-DnsClientServerAddress -AddressFamily IPv4 | "
|
||||
"Where-Object { $_.ServerAddresses } | "
|
||||
"Select-Object -ExpandProperty ServerAddresses) -join ','"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=12,
|
||||
creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0),
|
||||
)
|
||||
raw = (r.stdout or "").strip()
|
||||
if not raw:
|
||||
return []
|
||||
return [x.strip() for x in raw.replace(";", ",").split(",") if x.strip()]
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
|
||||
def flush_dns_cache() -> tuple[bool, str]:
|
||||
try:
|
||||
r = subprocess.run(
|
||||
["ipconfig", "/flushdns"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=15,
|
||||
creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0),
|
||||
)
|
||||
msg = (r.stdout or r.stderr or "").strip().splitlines()[-1] if r.returncode == 0 else "flush failed"
|
||||
return r.returncode == 0, msg
|
||||
except Exception as e:
|
||||
return False, str(e)
|
||||
|
||||
|
||||
def check_dns_leak_hint(local_proxy: str | None = None) -> DnsLeakResult:
|
||||
"""Heuristic: list configured DNS servers; note if any are public ISP resolvers.
|
||||
|
||||
Full DNS leak testing needs OS-level routing; this flags obvious misconfig.
|
||||
"""
|
||||
resolvers = get_system_dns_servers()
|
||||
private_prefixes = ("127.", "10.", "192.168.", "172.16.", "172.17.", "172.18.",
|
||||
"172.19.", "172.2", "172.30.", "172.31.", "0.0.0.0")
|
||||
public = [r for r in resolvers if not any(r.startswith(p) for p in private_prefixes)]
|
||||
|
||||
doh_ip: str | None = None
|
||||
try:
|
||||
with httpx.Client(timeout=8.0, verify=True) as c:
|
||||
r = c.get(_DOH_GOOGLE)
|
||||
if r.status_code == 200:
|
||||
data = r.json()
|
||||
answers = data.get("Answer") or []
|
||||
if answers:
|
||||
doh_ip = str(answers[0].get("data", ""))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if not resolvers:
|
||||
return DnsLeakResult(
|
||||
ok=True,
|
||||
system_resolvers=[],
|
||||
message="No IPv4 DNS servers reported (DHCP may assign later).",
|
||||
doh_ip=doh_ip,
|
||||
)
|
||||
|
||||
if public and local_proxy:
|
||||
return DnsLeakResult(
|
||||
ok=False,
|
||||
system_resolvers=resolvers,
|
||||
message=(
|
||||
f"DNS may bypass proxy chain: public resolvers {', '.join(public)}. "
|
||||
"Use kill-switch + VPN, or set DNS to localhost when hardened."
|
||||
),
|
||||
doh_ip=doh_ip,
|
||||
)
|
||||
|
||||
if public and not local_proxy:
|
||||
return DnsLeakResult(
|
||||
ok=False,
|
||||
system_resolvers=resolvers,
|
||||
message=f"Public DNS resolvers active: {', '.join(public)}",
|
||||
doh_ip=doh_ip,
|
||||
)
|
||||
|
||||
return DnsLeakResult(
|
||||
ok=True,
|
||||
system_resolvers=resolvers,
|
||||
message=f"DNS servers: {', '.join(resolvers)}",
|
||||
doh_ip=doh_ip,
|
||||
)
|
||||
Reference in New Issue
Block a user