Spaces:
Paused
Paused
File size: 4,842 Bytes
0790cf1 e18cd83 0790cf1 e18cd83 0790cf1 e18cd83 0790cf1 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | #!/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()
|