fix: answer the address a name actually has - #15
Merged
Conversation
This bridge could not resolve most of the registry. Every name pointed at
a host came back as an authoritative NOERROR with no answers, which a
client is entitled to treat as final — in practice, `curl: (6) Could not
resolve host` for a name the registry holds perfectly well.
before ;; flags: qr aa rd; ANSWER: 0
after seo.rank. 30 IN CNAME dev.profullstack.com.
Two independent causes, both on the address path:
- RECORD_TYPES covered CNAME, MX and TXT only, so a published A or AAAA
record was never consulted for an address question. A name publishing
an AAAA in the registry still answered nothing.
- targetAddress() returns null for a hostname and nothing picked up
after it. Most of the registry points at a host, so this was the
common case rather than an edge one.
addressAnswer() replaces the answerPolicy-then-look-for-a-CNAME pair with
one plan: target address, published A/AAAA, published CNAME, then a CNAME
synthesised from a hostname target. The cheap question is still asked
first, so a name pointed at a bare IP costs one registry call and no
record fetch — the fast path every page load takes.
The chain is deliberately left dangling. Unlike a catch-all bridge, this
one is routed per-TLD, so the CNAME's target is not a name that comes
back here: the machine's own resolver — a full recursive one — chases it.
Completing it here would mean this bridge doing clearnet DNS to answer a
question the system already answers, which is the thing it does not do.
Ported from moshcode#269, which fixed the same two gaps in the copy that
does forward. The shared surface both sides pin stays unchanged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ThreatCrush Security Scan0 finding(s) No findings. |
ralyodio
marked this pull request as ready for review
August 4, 2026 01:09
ralyodio
added a commit
that referenced
this pull request
Aug 4, 2026
Ships the resolver fix from #15, which has been on main since it merged but was never published: the version stayed at 0.4.0, so `npm i @moshcoder/moshpit-dns` still gives you a bridge that answers `ANSWER: 0` for names that resolve perfectly well. That authoritative-empty answer is the worst shape available — a client treats it as final, `dig` reports the name exists, and nothing logs an error. The bug is invisible from the outside, which is why it survived a release. Same pattern as #14: a fix sitting behind a stale version is not shipped, it is just merged.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This bridge could not resolve most of the registry.
Both measured against the live registry, on
seo.rank, with only this diff between them.ANSWER: 0withaaset is an authoritative "the name exists and has nothing for you", which a client is entitled to treat as final. In practice it surfaces as:— for a name the registry holds perfectly well, on a machine whose DNS is otherwise fine.
digconfirms the name exists, nothing can reach it, and no log anywhere reports an error.Two causes, both on the address path
A published A/AAAA record was never read.
RECORD_TYPEScovered CNAME, MX and TXT; addresses came only fromtarget. A name publishing anAAAAin the registry answered nothing to an AAAA query.A hostname target produced nothing.
targetAddress()returns null for a host and nothing picked up after it. Most of the registry points at a host, so this was the common case, not an edge one.The change
addressAnswer()replaces theanswerPolicy-then-look-for-a-CNAME pair with a single plan: target address → published A/AAAA → published CNAME → a CNAME synthesised from a hostname target → NODATA.The cheap question is still asked first. A name pointed at a bare IP costs one registry call and no record fetch — the fast path every page load takes, and there is a test pinning that it stays that way.
targetHostname()is the other half oftargetAddress(), refusing anything a CNAME cannot carry: ports, paths, bare IPs, single labels. A target naming:8080stays NODATA rather than quietly sending the client to port 80 of the right host, which would be a wrong answer that looks right.Why the chain is left dangling here
buildChainResponse()can attach leaf addresses, and the tests cover that, but the server does not pass any.This bridge is routed per-TLD (
~rank,~hacker), so the CNAME's target is not a name that comes back to it — the machine's own resolver, a full recursive one, chases it. Resolving it here would mean this bridge doing clearnet DNS to answer a question the system already answers, which is precisely what it deliberately does not do.That is the one place this port differs from moshcode#269. That copy sits behind
Domains=~., where the target does come back to it and a dangling CNAME would strand the client, so it completes the chain from its upstreams.Tests
14 new in
test/dns-address-answer.test.mjs, reading answers back off the wire — a reply of the right shape with the wrong bytes is exactly the failure being fixed. They cover both owner names in a chain, a leaf of the wrong family being dropped rather than encoded as garbage, the fast path not fetching records, and NXDOMAIN surviving all of it.Full suite: 102 pass, 0 fail.
The surface shared with moshcode's copy is unchanged, so the drift test pinning them together still holds.