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
11 changes: 11 additions & 0 deletions src/dns.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2231,6 +2231,17 @@ export async function dnsCommand(args = [], out = console.log, deps = {}) {
if (proxyIndex >= 0) {
const given = rest[proxyIndex + 1];
const host = given && !given.startsWith("-") ? given : null;
// A host name passes the reachability probe (connect resolves it) but a
// DNS answer can only carry an address — isIP would leave both families
// null, so the mode would announce success and then NODATA every live
// name. That is the very outage the gate below exists to refuse, so it is
// refused here for the same reason rather than warned about.
if (host && !isIP(host)) {
out(`! --proxy needs an IP address, not a host name like "${host}"`);
out(" a name here answers every live Moshpit name with nothing, which reads");
out(" as a total outage — pass the proxy's address (127.0.0.1 or ::1) instead.");
return 1;
}
const candidates = host ? [host] : ["127.0.0.1", "::1"];
const reachable = [];
for (const candidate of candidates) {
Expand Down
25 changes: 25 additions & 0 deletions test/dns-proxy-mode.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -189,3 +189,28 @@ test("an explicit --proxy host is the only one probed", async () => {
await held.release();
}
});

test("--proxy with a host name refuses instead of NODATA'ing every live name", async () => {
// A name like `localhost` passes the reachability probe (connect resolves it)
// but cannot go in an A/AAAA answer, so the mode would announce success and
// then answer every live name with nothing — the outage the gate exists for.
// Hold the resolver port so that even without the fix `start` cannot bind and
// sit on the loop — the assertion is about the refusal, not the bind.
const held = await holdUdp();
try {
const lines = [];
let probed = false;
const code = await dnsCommand(["start", "--proxy", "localhost", "--port", String(held.port)], (l) => lines.push(l), {
tlds: async () => ["eggs"],
proxyReachableImpl: async () => { probed = true; return true; },
});

assert.equal(code, 1);
assert.equal(probed, false, "a host name is rejected before anything is probed");
const text = lines.join("\n");
assert.match(text, /needs an IP address/);
assert.doesNotMatch(text, /proxying every live name/, "it must not claim to have started proxying");
} finally {
await held.release();
}
});
Loading