diff --git a/main.js b/main.js index ba830e1b..3543f9af 100644 --- a/main.js +++ b/main.js @@ -240,6 +240,27 @@ function dnsRecordsCached(name) { const c = dnsRecordsCache.get(name); return c && Date.now() - c.at < DNS_RECORDS_TTL ? c.value : undefined; } +// The routing half of a name's signed manifest for ONE host, verified by the +// gateway (which already holds that code) and cached per host. Only consulted +// for a subdomain that Theseus serves itself — an `ip` or inline `h` record — +// because anything going through the gateway's /bns/ mount already had the +// rule applied there. KEEP IN STEP with public-gateway.mjs serve(). +const hostActionCache = new Map(); +async function fetchHostAction(host) { + const c = hostActionCache.get(host); + if (c && Date.now() - c.at < DNS_RECORDS_TTL) return c.value; + let value = null; + try { + const r = await fetch(`${GATEWAY}/api/dns/${encodeURIComponent(host)}`, { signal: AbortSignal.timeout(3000), cache: "no-store" }); + if (r.ok) { + const j = await r.json(); + const a = j?.host_action; + if (a && typeof a === "object" && typeof a.kind === "string") value = a; + } + } catch { /* offline or no manifest — the name keeps its chain behaviour */ } + hostActionCache.set(host, { value, at: Date.now() }); + return value; +} function fetchDnsRecords(name) { const c = dnsRecordsCache.get(name); if (c?.pending) return c.pending; @@ -1696,6 +1717,14 @@ async function serveBns(request) { return new Response(body, { status: up.status, headers: { "content-type": ct } }); }; try { + // A subdomain the owner has ruled on: blocked, or sent elsewhere. The + // gateway applies this for anything it serves, so this only covers the + // paths Theseus takes on its own — `ip` and inline `h`. + if (isSubdomain && (r.ip || r.h)) { + const act = await fetchHostAction(host); + if (act?.kind === "block") return new Response("not found", { status: 404 }); + if (act?.kind === "redirect" && act.url) return Response.redirect(act.url, 302); + } if (isSubdomain && r.ip) return await serveIp(); if (r.h) { if (reqPath === "/") return new Response(r.h, { headers: { "content-type": "text/html; charset=utf-8" } }); return new Response("not found", { status: 404 }); } if (r.s3) {