feat: TCP fallback, query minimization, UDP auto-disable

Transport resilience for restrictive networks (ISPs blocking UDP:53):
- DNS-over-TCP fallback: UDP fail/truncation → automatic TCP retry
- UDP auto-disable: after 3 consecutive failures, switch to TCP-first
- IPv6 → TCP directly (UDP socket binds 0.0.0.0, can't reach IPv6)
- Network change resets UDP detection for re-probing
- Root hint rotation in TLD priming

Privacy:
- RFC 7816 query minimization: root servers see TLD only, not full name

Code quality:
- Merged find_starting_ns + find_starting_zone → find_closest_ns
- Extracted resolve_ns_addrs_from_glue shared helper
- Removed overall timeout wrapper (per-hop timeouts sufficient)
- forward_tcp for DNS-over-TCP (RFC 1035 §4.2.2)

Testing:
- Mock TCP-only DNS server for fallback tests (no network needed)
- tcp_fallback_resolves_when_udp_blocked
- tcp_only_iterative_resolution
- tcp_fallback_handles_nxdomain
- udp_auto_disable_resets
- Integration test suite (4 suites, 51 tests)
- Network probe script (tests/network-probe.sh)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Razvan Dimescu
2026-03-27 19:50:19 +02:00
parent 637b374d8b
commit 5b2cc874a1
11 changed files with 1372 additions and 103 deletions

View File

@@ -301,15 +301,16 @@ parsed and serialized by hand. It started as a weekend learning project,
became a side project I kept coming back to over 6 years, and eventually
turned into <a href="https://github.com/razvandimescu/numa">Numa</a>
which I now use as my actual system DNS.</p>
<p>A note on terminology before we go further: Numa is currently a
<em>forwarding</em> resolver — it parses and caches DNS packets, but
forwards queries to an upstream (Quad9, Cloudflare, or any DoH provider)
rather than walking the delegation chain from root servers itself. Think
of it as a smart proxy that does useful things with your DNS traffic
locally (caching, ad blocking, overrides, local service domains) before
forwarding what it cant answer. Full recursive resolution — where Numa
talks directly to root and authoritative nameservers — is on the
roadmap, along with DNSSEC validation.</p>
<p>A note on terminology: Numa supports two resolution modes.
<em>Forward</em> mode relays queries to an upstream (Quad9, Cloudflare,
or any DoH provider). <em>Recursive</em> mode walks the delegation chain
from root servers itself — iterative queries to root, TLD, and
authoritative nameservers, with full DNSSEC validation. In both modes,
Numa does useful things with your DNS traffic locally (caching, ad
blocking, overrides, local service domains) before resolving what it
cant answer. This post covers the wire protocol and forwarding path; <a
href="/blog/dnssec-from-scratch.html">the next post</a> covers recursive
resolution and DNSSEC.</p>
<p>Heres what surprised me along the way.</p>
<h2 id="what-does-a-dns-packet-actually-look-like">What does a DNS
packet actually look like?</h2>
@@ -619,24 +620,23 @@ resolver. The distinction matters to people who work with DNS
professionally, and being sloppy about it cost me credibility in my
first community posts.</p>
<h2 id="whats-next">Whats next</h2>
<p>Numa is at v0.5.0 with DNS forwarding, caching, ad blocking,
DNS-over-HTTPS, .numa local domains with auto TLS, and LAN service
discovery.</p>
<p>On the roadmap:</p>
<p><strong>Update (March 2026):</strong> Recursive resolution and DNSSEC
validation are now shipped. Numa resolves from root nameservers with
full chain-of-trust verification (RSA/SHA-256, ECDSA P-256, Ed25519) and
NSEC/NSEC3 authenticated denial of existence.</p>
<p><strong><a href="/blog/dnssec-from-scratch.html">Read the follow-up:
Implementing DNSSEC from Scratch in Rust →</a></strong></p>
<p>Still on the roadmap:</p>
<ul>
<li><strong>DoT (DNS-over-TLS)</strong> — DoH was first because it
passes through captive portals and corporate firewalls (port 443 vs
853). DoT has less framing overhead, so its faster. Both will be
available.</li>
<li><strong>Recursive resolution</strong> — walk the delegation chain
from root servers instead of forwarding. Combined with DNSSEC
validation, this removes the need to trust any upstream resolver.</li>
<li><strong><a href="https://github.com/pubky/pkarr">pkarr</a>
integration</strong> — self-sovereign DNS via the Mainline BitTorrent
DHT. Publish DNS records signed with your Ed25519 key, no registrar
needed.</li>
</ul>
<p>But those are rabbit holes for future posts.</p>
<p><a
href="https://github.com/razvandimescu/numa">github.com/razvandimescu/numa</a></p>
</article>