Skip to content

fix(dns): fall back to public resolver when system DNS is unreachable - #15

Open
BabyBear7 wants to merge 2 commits into
dscalzi:masterfrom
BabyBear7:fix/srv-fallback-node22
Open

fix(dns): fall back to public resolver when system DNS is unreachable#15
BabyBear7 wants to merge 2 commits into
dscalzi:masterfrom
BabyBear7:fix/srv-fallback-node22

Conversation

@BabyBear7

@BabyBear7 BabyBear7 commented Apr 14, 2026

Copy link
Copy Markdown

Summary

checkSrv currently swallows every error and returns null, which means getServerStatus silently falls back to the default 25565 port whenever the system DNS resolver is broken. For servers that expose their Minecraft port only through SRV records, this makes them show as permanently offline even though the server is up.

This showed up in the wild with the Node.js 22 + Windows c-ares regression tracked in nodejs/node#62326 / #62347. Newer c-ares (bundled in Node 22.22.0, 24.13.0+, 25.x < 25.6.1) reports the loopback fallback with tcp_port/udp_port = 53 instead of 0. Node's glue layer still checks for port 0 to detect the fallback, misdetects, and ends up firing DNS queries at 127.0.0.1:53 which has no listener — hence ECONNREFUSED querySrv. The fix (nodejs/node#61453) is in 25.6.1 and 24.x but not yet backported to the 22 LTS line, so Electron apps on Electron 39 (which bundles Node 22) are currently exposed.

Change

When resolveSrv rejects with a network-layer error code (ECONNREFUSED, ECONNRESET, ETIMEDOUT, ESERVFAIL, EREFUSED), retry once against a user-independent public resolver (1.1.1.1, 8.8.8.8) using an explicit Resolver instance. Legitimate "no SRV record" responses (ENOTFOUND, ENODATA, etc.) still short-circuit to null, so the extra lookup only happens when the system resolver is actually broken.

A single logger.warn line identifies the failing error code so launcher operators can tell whether the fallback kicked in.

Reproduction before the patch

On a Windows host running Node 22.22.0:

> node -e "console.log(require('dns').getServers())"
[ '127.0.0.1' ]
> node -e "require('dns/promises').resolveSrv('_minecraft._tcp.<srv-host>').then(console.log, e => console.error(e.code))"
ECONNREFUSED

Same host on Node 20.18.1 returns the correct SRV record and the real system DNS servers. With this patch, checkSrv transparently recovers on Node 22 by retrying through the public resolver, and getServerStatus hits the correct port.

Test plan

  • npm run build — clean TypeScript compile
  • npm run lint — no new warnings
  • Existing ServerStatusAPITest (real-network test) runs unchanged in working environments (fast path untouched)
  • Manual verification on a Windows 22.22.0 host against an SRV-only server (passes through fallback path and returns status)

When the system DNS resolver is unreachable (e.g. the Node.js 22 +
Windows c-ares regression tracked in nodejs/node#62326, where c-ares
reports the loopback fallback with port 53 and Node misdetects it as a
real nameserver), resolveSrv rejects with ECONNREFUSED and getServerStatus
silently falls back to the default 25565 port. For servers configured
only through SRV records this makes them appear permanently offline.

Retry the SRV query against 1.1.1.1 / 8.8.8.8 with an explicit Resolver
when the first attempt fails with a network-layer error code. Legitimate
"no SRV record" responses (ENOTFOUND, ENODATA, etc.) still short-circuit
to null so this only costs an extra lookup in the broken-resolver case.
@BabyBear7
BabyBear7 force-pushed the fix/srv-fallback-node22 branch from 7af46e9 to a88ffd3 Compare April 14, 2026 14:42
Add an inline comment near the gating check in checkSrv explaining why
ESERVFAIL / EREFUSED are deliberately excluded from the fallback allowlist
(they are legitimate resolver outcomes including policy responses, so
retrying against a public resolver would silently bypass local DNS policy).

Runtime behavior unchanged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant