Spaces:
Paused
Paused
Your Name
Add real TCP reachability probe alongside DNS resolution (distinguish DNS-blocked from network-egress-blocked)
e18cd83 | #!/usr/bin/env python3 | |
| """ | |
| DNS-over-HTTPS resolver for HF Spaces. | |
| HF Spaces containers cannot resolve certain domains (e.g. api.telegram.org, | |
| web.whatsapp.com) via the default DNS resolver. This script resolves key | |
| domains using Cloudflare / Google DoH and writes results to a JSON file | |
| plus /etc/hosts so both Python and Node.js processes benefit. | |
| Usage: python3 dns-resolve.py [output-file] | |
| """ | |
| import json | |
| import socket | |
| import ssl | |
| import sys | |
| import urllib.request | |
| DOH_ENDPOINTS = [ | |
| "https://1.1.1.1/dns-query", | |
| "https://8.8.8.8/resolve", | |
| "https://dns.google/resolve", | |
| ] | |
| DOMAINS = [ | |
| # Telegram Bot API | |
| "api.telegram.org", | |
| # Discord gateway / API | |
| "discord.com", | |
| "gateway.discord.gg", | |
| "cdn.discordapp.com", | |
| # Slack | |
| "slack.com", | |
| "api.slack.com", | |
| # WhatsApp / Baileys | |
| "web.whatsapp.com", | |
| "g.whatsapp.net", | |
| "mmg.whatsapp.net", | |
| "pps.whatsapp.net", | |
| "static.whatsapp.net", | |
| "media.fmed1-1.fna.whatsapp.net", | |
| ] | |
| def resolve_via_doh(domain: str, endpoint: str, timeout: int = 10) -> list[str]: | |
| url = f"{endpoint}?name={domain}&type=A" | |
| req = urllib.request.Request(url, headers={"Accept": "application/dns-json"}) | |
| ctx = ssl.create_default_context() | |
| resp = urllib.request.urlopen(req, timeout=timeout, context=ctx) | |
| data = json.loads(resp.read().decode()) | |
| return [a["data"] for a in data.get("Answer", []) if a.get("type") == 1] | |
| def resolve_domain(domain: str) -> list[str]: | |
| for endpoint in DOH_ENDPOINTS: | |
| try: | |
| ips = resolve_via_doh(domain, endpoint) | |
| if ips: | |
| return ips | |
| except Exception: | |
| continue | |
| return [] | |
| def test_tcp_connect(ip: str, port: int = 443, timeout: int = 5) -> tuple[bool, str]: | |
| """Test actual TCP reachability to a resolved IP, not just DNS resolution. | |
| DNS resolving successfully does NOT mean the IP is reachable β HF Spaces | |
| (especially free CPU-basic, on shared cloud infra) can have specific IP | |
| ranges null-routed/blocked at the network layer even when DoH resolution | |
| works fine. This distinguishes "DNS blocked" (fixable by this script) | |
| from "network egress blocked" (not fixable from inside the container β | |
| needs an HF support ticket / different hardware tier / a proxy). | |
| """ | |
| try: | |
| with socket.create_connection((ip, port), timeout=timeout): | |
| return True, "connected" | |
| except Exception as e: | |
| return False, f"{type(e).__name__}: {e}" | |
| def main() -> None: | |
| output_file = sys.argv[1] if len(sys.argv) > 1 else "/tmp/dns-resolved.json" | |
| # If system DNS works for the most-commonly-blocked domain, DoH isn't needed. | |
| try: | |
| socket.getaddrinfo("api.telegram.org", 443, socket.AF_INET) | |
| print("[dns] System DNS works for api.telegram.org β DoH not needed") | |
| ok, detail = test_tcp_connect("api.telegram.org", 443) | |
| if ok: | |
| print("[dns] TCP reachability OK: api.telegram.org:443") | |
| else: | |
| print(f"[dns] WARNING: DNS resolves but TCP connect FAILED to " | |
| f"api.telegram.org:443 ({detail}) β this is a network " | |
| f"egress block, not a DNS problem. No DNS workaround can " | |
| f"fix this; it needs an HF support ticket or a different " | |
| f"Space hardware tier.") | |
| with open(output_file, "w") as f: | |
| json.dump({}, f) | |
| return | |
| except (socket.gaierror, OSError) as e: | |
| print(f"[dns] System DNS failed ({e}) β using DoH fallback") | |
| results = {} | |
| for domain in DOMAINS: | |
| ips = resolve_domain(domain) | |
| if ips: | |
| results[domain] = ips[0] | |
| print(f"[dns] {domain} -> {ips[0]}") | |
| ok, detail = test_tcp_connect(ips[0], 443) | |
| if ok: | |
| print(f"[dns] TCP reachability OK: {domain} ({ips[0]}:443)") | |
| else: | |
| print(f"[dns] WARNING: DoH resolved {domain} but TCP connect " | |
| f"FAILED ({detail}) β network egress to this IP is " | |
| f"blocked; no DNS fix can resolve this.") | |
| else: | |
| print(f"[dns] WARNING: could not resolve {domain}") | |
| with open(output_file, "w") as f: | |
| json.dump(results, f, indent=2) | |
| if results: | |
| try: | |
| with open("/etc/hosts", "a") as f: | |
| f.write("\n# === HermesFace DoH resolved domains ===\n") | |
| for domain, ip in results.items(): | |
| f.write(f"{ip} {domain}\n") | |
| print(f"[dns] Wrote {len(results)} entries to /etc/hosts") | |
| except PermissionError: | |
| print("[dns] WARNING: cannot write /etc/hosts (permission denied)") | |
| print(f"[dns] Resolved {len(results)}/{len(DOMAINS)} domains -> {output_file}") | |
| if __name__ == "__main__": | |
| main() | |