Skip to content

Commit 51be4eb

Browse files
fix(dns): refuse a --proxy host name instead of NODATA'ing every live name (#298)
A host name like `localhost` passes proxyReachable() (connect resolves it) but isIP() cannot place it in an A/AAAA answer, so proxyAddress ends up {v4:null, v6:null}. The mode then prints "proxying every live name ..." and answers every live name with NODATA — the exact silent outage the reachability gate exists to refuse. Reject a non-IP --proxy host up front with a clear message, before any probe, for the same reason the gate does. Adds a regression test (fails before / passes after). Co-authored-by: clawedassistant26 <307253840+clawedassistant26@users.noreply.github.com>
1 parent 29c8f34 commit 51be4eb

2 files changed

Lines changed: 36 additions & 0 deletions

File tree

src/dns.mjs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2231,6 +2231,17 @@ export async function dnsCommand(args = [], out = console.log, deps = {}) {
22312231
if (proxyIndex >= 0) {
22322232
const given = rest[proxyIndex + 1];
22332233
const host = given && !given.startsWith("-") ? given : null;
2234+
// A host name passes the reachability probe (connect resolves it) but a
2235+
// DNS answer can only carry an address — isIP would leave both families
2236+
// null, so the mode would announce success and then NODATA every live
2237+
// name. That is the very outage the gate below exists to refuse, so it is
2238+
// refused here for the same reason rather than warned about.
2239+
if (host && !isIP(host)) {
2240+
out(`! --proxy needs an IP address, not a host name like "${host}"`);
2241+
out(" a name here answers every live Moshpit name with nothing, which reads");
2242+
out(" as a total outage — pass the proxy's address (127.0.0.1 or ::1) instead.");
2243+
return 1;
2244+
}
22342245
const candidates = host ? [host] : ["127.0.0.1", "::1"];
22352246
const reachable = [];
22362247
for (const candidate of candidates) {

test/dns-proxy-mode.test.mjs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,3 +189,28 @@ test("an explicit --proxy host is the only one probed", async () => {
189189
await held.release();
190190
}
191191
});
192+
193+
test("--proxy with a host name refuses instead of NODATA'ing every live name", async () => {
194+
// A name like `localhost` passes the reachability probe (connect resolves it)
195+
// but cannot go in an A/AAAA answer, so the mode would announce success and
196+
// then answer every live name with nothing — the outage the gate exists for.
197+
// Hold the resolver port so that even without the fix `start` cannot bind and
198+
// sit on the loop — the assertion is about the refusal, not the bind.
199+
const held = await holdUdp();
200+
try {
201+
const lines = [];
202+
let probed = false;
203+
const code = await dnsCommand(["start", "--proxy", "localhost", "--port", String(held.port)], (l) => lines.push(l), {
204+
tlds: async () => ["eggs"],
205+
proxyReachableImpl: async () => { probed = true; return true; },
206+
});
207+
208+
assert.equal(code, 1);
209+
assert.equal(probed, false, "a host name is rejected before anything is probed");
210+
const text = lines.join("\n");
211+
assert.match(text, /needs an IP address/);
212+
assert.doesNotMatch(text, /proxying every live name/, "it must not claim to have started proxying");
213+
} finally {
214+
await held.release();
215+
}
216+
});

0 commit comments

Comments
 (0)