HuggingClaw / scripts /dns-fix.cjs
tao-shen
deploy: HuggingClaw with original Dockerfile
b19d31a
/**
* DNS fix preload script for HF Spaces.
*
* Patches Node.js dns.lookup to:
* 1. Check pre-resolved domains from /tmp/dns-resolved.json (populated by dns-resolve.py)
* 2. Fall back to DNS-over-HTTPS (Cloudflare) for any other unresolvable domain
*
* Loaded via: NODE_OPTIONS="--require /path/to/dns-fix.cjs"
*/
"use strict";
const dns = require("dns");
const https = require("https");
const fs = require("fs");
// ── Pre-resolved domains (populated by entrypoint.sh via dns-resolve.py) ──
let preResolved = {};
try {
const raw = fs.readFileSync("/tmp/dns-resolved.json", "utf8");
preResolved = JSON.parse(raw);
const count = Object.keys(preResolved).length;
if (count > 0) {
console.log(`[dns-fix] Loaded ${count} pre-resolved domains`);
}
} catch {
// File not found or parse error β€” proceed without pre-resolved cache
}
// ── In-memory cache for runtime DoH resolutions ──
const runtimeCache = new Map(); // hostname -> { ip, expiry }
// ── DNS-over-HTTPS resolver ──
function dohResolve(hostname, callback) {
// Check runtime cache
const cached = runtimeCache.get(hostname);
if (cached && cached.expiry > Date.now()) {
return callback(null, cached.ip);
}
const url = `https://1.1.1.1/dns-query?name=${encodeURIComponent(hostname)}&type=A`;
const req = https.get(
url,
{ headers: { Accept: "application/dns-json" }, timeout: 15000 },
(res) => {
let body = "";
res.on("data", (c) => (body += c));
res.on("end", () => {
try {
const data = JSON.parse(body);
const aRecords = (data.Answer || []).filter((a) => a.type === 1);
if (aRecords.length === 0) {
return callback(new Error(`DoH: no A record for ${hostname}`));
}
const ip = aRecords[0].data;
const ttl = Math.max((aRecords[0].TTL || 300) * 1000, 60000);
runtimeCache.set(hostname, { ip, expiry: Date.now() + ttl });
callback(null, ip);
} catch (e) {
callback(new Error(`DoH parse error: ${e.message}`));
}
});
}
);
req.on("error", (e) => callback(new Error(`DoH request failed: ${e.message}`)));
req.on("timeout", () => {
req.destroy();
callback(new Error("DoH request timed out"));
});
}
// ── Monkey-patch dns.lookup ──
const origLookup = dns.lookup;
dns.lookup = function patchedLookup(hostname, options, callback) {
// Normalize arguments (options is optional, can be number or object)
if (typeof options === "function") {
callback = options;
options = {};
}
if (typeof options === "number") {
options = { family: options };
}
options = options || {};
// Skip patching for localhost, IPs, and internal domains
if (
!hostname ||
hostname === "localhost" ||
hostname === "0.0.0.0" ||
hostname === "127.0.0.1" ||
hostname === "::1" ||
/^\d+\.\d+\.\d+\.\d+$/.test(hostname) ||
/^::/.test(hostname)
) {
return origLookup.call(dns, hostname, options, callback);
}
// 1) Check pre-resolved cache
if (preResolved[hostname]) {
const ip = preResolved[hostname];
if (options.all) {
return process.nextTick(() => callback(null, [{ address: ip, family: 4 }]));
}
return process.nextTick(() => callback(null, ip, 4));
}
// 2) Try system DNS
origLookup.call(dns, hostname, options, (err, address, family) => {
if (!err && address) {
return callback(null, address, family);
}
// 3) System DNS failed with ENOTFOUND β€” fall back to DoH
if (err && (err.code === "ENOTFOUND" || err.code === "EAI_AGAIN")) {
dohResolve(hostname, (dohErr, ip) => {
if (dohErr || !ip) {
return callback(err); // Return original error
}
if (options.all) {
return callback(null, [{ address: ip, family: 4 }]);
}
callback(null, ip, 4);
});
} else {
// Other DNS errors β€” pass through
callback(err, address, family);
}
});
};