Hugging8N / dns-fix.js
somratpro's picture
fix: use AsyncLocalStorage for robust recursion guard
d68b341
raw
history blame
3.91 kB
"use strict";
console.error("[DNS-FIX] Loaded — DoH resolver + tls/net logging active.");
const http = require("http");
const https = require("https");
const { PassThrough } = require("stream");
const blockedDomains = ["api.telegram.org", "discord.com", "discordapp.com", "web.whatsapp.com"];
function isBlocked(hostname) {
return hostname && blockedDomains.some((d) => hostname === d || hostname.endsWith(`.${d}`));
}
// Monkey-patch http.request and https.request
const _origHttpRequest = http.request;
const _origHttpsRequest = https.request;
const { AsyncLocalStorage } = require("async_hooks");
const asyncStorage = new AsyncLocalStorage();
function transparentProxyRequest(protocol, origFn, ...args) {
if (asyncStorage.getStore()) {
return origFn.apply(this, args);
}
let options = args[0];
let callback = args[1];
if (typeof options === "string") {
try {
options = new URL(options);
} catch {
return origFn.apply(this, args);
}
} else if (options instanceof URL) {
// Already a URL
} else {
// Object options
options = { ...options };
}
if (typeof callback !== "function" && typeof args[2] === "function") {
callback = args[2];
}
const hostname = options.hostname || options.host;
const path = options.path || options.pathname || "/";
if (isBlocked(hostname)) {
console.error(`[DNS-FIX] Transparently proxying ${protocol}//${hostname}${path} via fetch()`);
const requestUrl = `${protocol}//${hostname}${path}`;
const requestHeaders = { ...options.headers };
const reqStream = new PassThrough();
const resStream = new PassThrough();
let requestBody = Buffer.alloc(0);
reqStream.on("data", (chunk) => {
requestBody = Buffer.concat([requestBody, chunk]);
});
reqStream.on("finish", () => {
asyncStorage.run(true, async () => {
try {
const fetchRes = await fetch(requestUrl, {
method: options.method || "GET",
headers: requestHeaders,
body: options.method !== "GET" && options.method !== "HEAD" ? requestBody : undefined,
redirect: "manual",
});
resStream.statusCode = fetchRes.status;
resStream.statusMessage = fetchRes.statusText;
resStream.headers = Object.fromEntries(fetchRes.headers.entries());
resStream.rawHeaders = [];
for (const [k, v] of fetchRes.headers.entries()) {
resStream.rawHeaders.push(k, v);
}
if (callback) callback(resStream);
reqStream.emit("response", resStream);
if (fetchRes.body) {
const reader = fetchRes.body.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
resStream.write(value);
}
}
resStream.end();
} catch (err) {
console.error(`[DNS-FIX] Proxy error for ${requestUrl}: ${err.message}`);
reqStream.emit("error", err);
}
});
});
reqStream.abort = () => reqStream.destroy();
reqStream.end = (chunk) => {
if (chunk) reqStream.write(chunk);
reqStream.end();
};
reqStream.setTimeout = (ms, cb) => { if (cb) setTimeout(cb, ms); return reqStream; };
reqStream.setNoDelay = () => reqStream;
reqStream.setSocketKeepAlive = () => reqStream;
return reqStream;
}
return origFn.apply(this, args);
}
http.request = function (...args) {
return transparentProxyRequest("http:", _origHttpRequest, ...args);
};
https.request = function (...args) {
return transparentProxyRequest("https:", _origHttpsRequest, ...args);
};
http.get = function (...args) {
const req = http.request(...args);
req.end();
return req;
};
https.get = function (...args) {
const req = https.request(...args);
req.end();
return req;
};