#!/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()