Files
darkproxy/zildns/server.js
T
auto-ci ec3c2d92cf
Configuration validation / lint (push) Has been cancelled
Configuration validation / smoke (push) Has been cancelled
Add local zildns service and ARM smoke validation
2026-03-06 22:39:30 -05:00

226 lines
5.2 KiB
JavaScript

"use strict";
const dns2 = require("dns2");
const { default: Resolution } = require("@unstoppabledomains/resolution");
const LISTEN_HOST = process.env.ZILDNS_LISTEN_HOST || "0.0.0.0";
const LISTEN_PORT = Number(process.env.ZILDNS_LISTEN_PORT || "53");
const DEFAULT_TTL = Number(process.env.ZILDNS_TTL || "120");
const CACHE_SECONDS = Number(process.env.ZILDNS_CACHE_SECONDS || "30");
const ZNS_URL = process.env.ZILDNS_ZNS_URL || "https://api.zilliqa.com";
const ZNS_NETWORK = process.env.ZILDNS_ZNS_NETWORK || "mainnet";
const PREWARM_DOMAIN = normalizePrewarmDomain(process.env.ZILDNS_PREWARM_DOMAIN || "brad.zil");
const { Packet, createServer } = dns2;
const RCODE = {
NOERROR: 0,
SERVFAIL: 2,
NXDOMAIN: 3,
};
const resolution = new Resolution({
sourceConfig: {
zns: {
url: ZNS_URL,
network: ZNS_NETWORK,
},
},
});
const cache = new Map();
function normalizeName(name) {
return String(name || "").replace(/\.$/, "").toLowerCase();
}
function normalizePrewarmDomain(name) {
const normalized = normalizeName(name);
return normalized.endsWith(".zil") ? normalized : "";
}
function cacheGet(key) {
const entry = cache.get(key);
if (!entry) {
return null;
}
if (Date.now() > entry.expiresAt) {
cache.delete(key);
return null;
}
return entry.value;
}
function cacheSet(key, value) {
cache.set(key, {
value,
expiresAt: Date.now() + CACHE_SECONDS * 1000,
});
}
async function resolveZilDomain(domain) {
const cached = cacheGet(domain);
if (cached !== null) {
return cached;
}
const [zilResult, ipfsResult] = await Promise.allSettled([
resolution.addr(domain, "ZIL"),
resolution.ipfsHash(domain),
]);
const result = {
zilAddress: zilResult.status === "fulfilled" && zilResult.value ? zilResult.value : null,
ipfsHash: ipfsResult.status === "fulfilled" && ipfsResult.value ? ipfsResult.value : null,
};
const hasData = Boolean(result.zilAddress || result.ipfsHash);
const resolved = hasData ? result : null;
cacheSet(domain, resolved);
return resolved;
}
function makeResponse(request) {
const response = Packet.createResponseFromRequest(request);
response.header.aa = 1;
response.header.ra = 1;
return response;
}
function addTxtAnswers(response, name, resolved) {
if (resolved.zilAddress) {
response.answers.push({
name,
type: Packet.TYPE.TXT,
class: Packet.CLASS.IN,
ttl: DEFAULT_TTL,
data: `zil_address=${resolved.zilAddress}`,
});
}
if (resolved.ipfsHash) {
response.answers.push({
name,
type: Packet.TYPE.TXT,
class: Packet.CLASS.IN,
ttl: DEFAULT_TTL,
data: `ipfs_hash=${resolved.ipfsHash}`,
});
}
}
function addCnameAnswer(response, name, resolved) {
if (!resolved.ipfsHash) {
return;
}
response.answers.push({
name,
type: Packet.TYPE.CNAME,
class: Packet.CLASS.IN,
ttl: DEFAULT_TTL,
domain: `${resolved.ipfsHash}.ipfs.dweb.link.`,
});
}
async function handleRequest(request, send) {
const response = makeResponse(request);
const question = request.questions && request.questions[0];
if (!question) {
send(response);
return;
}
const qname = normalizeName(question.name);
const qtype = question.type;
const isType = (name) => qtype === name || qtype === Packet.TYPE[name];
if (!qname.endsWith(".zil")) {
response.header.rcode = RCODE.NXDOMAIN;
send(response);
return;
}
const resolved = await resolveZilDomain(qname);
if (!resolved) {
response.header.rcode = RCODE.NXDOMAIN;
send(response);
return;
}
if (isType("TXT") || isType("ANY")) {
addTxtAnswers(response, question.name, resolved);
}
if (isType("CNAME") || isType("A") || isType("AAAA") || isType("ANY")) {
addCnameAnswer(response, question.name, resolved);
}
send(response);
}
const server = createServer({
udp: true,
tcp: true,
handle: (request, send) => {
handleRequest(request, send).catch((err) => {
const response = makeResponse(request);
response.header.rcode = RCODE.SERVFAIL;
send(response);
console.error("[zildns] request failed:", err && err.message ? err.message : String(err));
});
},
});
server.on("listening", () => {
console.log(`[zildns] listening on ${LISTEN_HOST}:${LISTEN_PORT} (udp/tcp)`);
console.log(`[zildns] zns provider ${ZNS_URL} (${ZNS_NETWORK})`);
});
server.on("close", () => {
console.log("[zildns] server closed");
});
server.on("error", (err) => {
console.error("[zildns] server error:", err);
});
async function maybePrewarm() {
if (!PREWARM_DOMAIN) {
console.log("[zildns] prewarm disabled");
return;
}
const started = Date.now();
try {
await resolveZilDomain(PREWARM_DOMAIN);
console.log(`[zildns] prewarm complete for ${PREWARM_DOMAIN} in ${Date.now() - started}ms`);
} catch (err) {
console.warn(
`[zildns] prewarm failed for ${PREWARM_DOMAIN}:`,
err && err.message ? err.message : String(err),
);
}
}
async function start() {
await maybePrewarm();
server.listen({
udp: {
address: LISTEN_HOST,
port: LISTEN_PORT,
},
tcp: {
address: LISTEN_HOST,
port: LISTEN_PORT,
},
});
}
start().catch((err) => {
console.error("[zildns] startup failed:", err);
process.exit(1);
});