Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/cli-schema.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,7 @@ export const DNS_VERBS = [
{ name: "service", description: "install or remove the background service" },
{ name: "tlds", description: "list the endings claimed in the Pit" },
{ name: "resolve", description: "what a name resolves to, and why" },
{ name: "trust", description: "trust one name's certificate, after checking it against the registry pin" },
];

/** Sub-verb tables, by the name a command's `verbs` field refers to. */
Expand Down
47 changes: 43 additions & 4 deletions src/dns.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1930,7 +1930,7 @@ import { createParkingServer, DEFAULT_PARKING_HTTP_PORT } from "./parking-http.m
// use it without importing this one back.
export { pitNameUrl } from "./pit-url.mjs";
import { pitNameUrl } from "./pit-url.mjs";
import { applyTrust, verifyStockTls } from "./trust.mjs";
import { applyTrust, createAutoTrust, trustName, verifyStockTls } from "./trust.mjs";
import { readFile, writeFile } from "node:fs/promises";
import { existsSync } from "node:fs";
import { fileURLToPath } from "node:url";
Expand Down Expand Up @@ -2012,6 +2012,7 @@ export async function dnsCommand(args = [], out = console.log, deps = {}) {
verify = verifyResolution,
bridgeStatus = daemonStatus,
startBridge = startDaemon,
autoTrustImpl = createAutoTrust,
stopBridge = stopDaemon,
dropins = readDropins,
manifestFile = manifestPath(),
Expand Down Expand Up @@ -2045,6 +2046,10 @@ export async function dnsCommand(args = [], out = console.log, deps = {}) {
return 0;
}

if (sub === "trust") {
return trustName(rest.find((a) => !a.startsWith("-")) || "", out, { registryBase, ...deps });
}

if (sub === "resolve") {
const name = resolveArgument(rest);
if (!name) {
Expand Down Expand Up @@ -2156,6 +2161,18 @@ export async function dnsCommand(args = [], out = console.log, deps = {}) {
// so a busy port answered with a node:dgram stack trace. This one is fatal
// where the parking server's is not, so it ends the command rather than
// carrying on: the shape serve.mjs uses for a step it cannot complete.
// Trust every name as it resolves, rather than one command per name. Only
// useful as root — the trust store is not writable otherwise — so it says
// so once here instead of failing per name, forever, in the query log.
const wantsTrustAll = rest.includes("--trust-all");
if (wantsTrustAll && uid !== 0) {
out("! --trust-all needs root to write to the trust store — certificates will not be installed");
}
const autoTrust = wantsTrustAll && uid === 0
? autoTrustImpl({ registryBase, out, uid })
: null;
if (autoTrust) out("trusting names as they resolve — only where the registry publishes a matching pin");

let server;
try {
server = await createServer({
Expand All @@ -2164,7 +2181,13 @@ export async function dnsCommand(args = [], out = console.log, deps = {}) {
parkingAddress: park,
upstreams,
tldSet,
onQuery: ({ name, address }) => out(` ${name} → ${address || "NXDOMAIN"}`),
onQuery: ({ name, address, forwarded }) => {
out(` ${name} → ${address || "NXDOMAIN"}`);
// Only a name that actually resolved to something of ours. A forwarded
// clearnet name is not ours to trust, and NXDOMAIN has no origin to
// fetch a certificate from.
if (autoTrust && address && !forwarded) autoTrust.consider(name);
},
onError: (err) => out(`! resolver socket error — ${err?.message || err}`),
});
} catch (err) {
Expand Down Expand Up @@ -2228,12 +2251,17 @@ export async function dnsCommand(args = [], out = console.log, deps = {}) {
const wanted = requiredPort(platform, port);

let tlds = [];
let tldError = null;
try {
tlds = await fetchTldsImpl({ registryBase });
} catch {
} catch (err) {
// disable does not need the list on Linux, and on macOS a stale list is
// better than refusing to clean up because the registry is unreachable.
// enable does need it, and the reason it is empty is the whole difference
// between "nobody has claimed an ending" and "we could not ask" — see the
// refusal below, which used to report the second as the first.
tlds = [];
tldError = err?.message || String(err);
}

// Phase 1, and it runs before every other question is asked — including the
Expand Down Expand Up @@ -2296,7 +2324,18 @@ export async function dnsCommand(args = [], out = console.log, deps = {}) {
}

if (sub === "enable" && !tlds.length) {
out("no TLDs claimed yet — nothing to route");
// Two very different situations, and reporting the second as the first
// sends someone to claim an ending they already own. The registry holds
// thousands; a machine that sees none of them has almost certainly failed
// to ask rather than found an empty namespace.
if (tldError) {
out(`could not read the ending list from ${registryBase} — ${tldError}`);
out(" nothing has been changed. This is a failure to ask, not an empty registry:");
out(` check with curl -s '${registryBase}/api/moshpit/tlds?limit=5&offset=0'`);
} else {
out("the registry reports no claimed endings — nothing to route");
out(` that is the registry's answer, not a local failure: ${registryBase}/api/moshpit/tlds`);
}
return 1;
}

Expand Down
Loading
Loading