Skip to content

feat(codefull.gs): edge-cache DNS to skip tunnel-node round-trip#494

Merged
therealaleph merged 1 commit intotherealaleph:mainfrom
dazzling-no-more:feature/edge-dns-cache
Apr 29, 2026
Merged

feat(codefull.gs): edge-cache DNS to skip tunnel-node round-trip#494
therealaleph merged 1 commit intotherealaleph:mainfrom
dazzling-no-more:feature/edge-dns-cache

Conversation

@dazzling-no-more
Copy link
Copy Markdown
Contributor

Summary

  • Intercept udp_open/port=53 ops in _doTunnelBatch and serve them from CacheService (cache hit) or DoH (cache miss), so plain DNS no longer trans-Atlantics through the tunnel-node. Cache hits drop typical first-hop DNS latency from ~600–1200 ms to ~200–400 ms.
  • Pure server-side change in CodeFull.gs (full-tunnel mode only — apps_script mode has no UDP path). No Rust/client changes; the synthesized {sid, pkts, eof:true} shape matches what the tunnel-node returns today, so mux.udp_open consumes it transparently.
  • Default-on, opt-out via ENABLE_EDGE_DNS_CACHE. Every failure mode (parse error, resolver outage, key-too-long, cache.put rejection) returns null from _edgeDnsTry and falls through to the existing tunnel-node forward path — zero regression vs today on any failure.

What's in it

  • Wire-format helpers (_dnsParseQuestion, _dnsMinTtl, _dnsSkipName, _dnsRewriteTxid) — handle name-pointer compression in answers, RFC 2181 §8 TTL high-bit clamp, and txid rewrite per RFC 1035 §4.1.1 so cached replies match the request's transaction id.
  • DoH fallback chain: Cloudflare → Google → Quad9 over RFC 8484 GET (?dns=<base64url>). One resolver per attempt; failure cycles to next.
  • Cache key = edns:<qtype>:<lowercase qname>, guarded against the 250-char CacheService limit. Per-qtype keying keeps A and AAAA from colliding.
  • TTL handling: clamp min RR TTL to [30 s, 6 h]. NXDOMAIN/SERVFAIL get a 45 s negative cache; NODATA-with-SOA honors the SOA TTL per RFC 2308 §5.
  • Splice helper (_spliceTunnelResults) factored out so mixed batches (TCP connect/data + DNS udp_open) preserve original op-index ordering. Length-mismatch guard fails closed instead of routing TCP responses to UDP sids.
  • ANY (qtype 255) and multi-question queries (qdcount !== 1) bypass the cache and forward to the tunnel-node.

Why not Sheets (like #443)?

Sheets reads/writes from GAS run 100–500 ms per op — often slower than the DoH lookup we'd be caching, and a daily-quota hazard. They also persist a Drive-listed log of every domain users resolve, which is a real privacy regression for users in censorship contexts. CacheService is ~10 ms, volatile, free, and has no on-disk artifact. #443's Sheets pattern is correct for HTTP response caching (large bodies, longer TTLs, value as analytics surface); it's the wrong shape for DNS hot-path.

Tests

node assets/apps_script/tests/edge_dns_test.js — 11 pure-JS tests covering the parsers, txid non-mutation, TTL high-bit clamp, NXDOMAIN-with-SOA TTL extraction, malformed/truncated input rejection, and splice correctness for mixed batches.

Known v1 tradeoffs (deferred)

  • Cold-batch UrlFetchApp fanout: a batch with N cold-miss DNS queries currently issues N sequential DoH fetches plus 1 forward, versus 1 fetch today. Steady-state (warm cache) it's strictly fewer. Worth a v2 follow-up using UrlFetchApp.fetchAll for parallel misses; deferred because it forces an unrelated design call on resolver-fallback strategy under parallel fetches.
  • Geographic assumption: the latency win materializes when GAS→DoH is faster than GAS→tunnel-node→resolver, i.e. trans-regional deployments. Same-region deployments see smaller wins (still get the warm-cache UrlFetchApp savings).

@github-actions github-actions Bot added the type: feature feat: PR — auto-applied by release-drafter label Apr 29, 2026
@therealaleph therealaleph merged commit aad900e into therealaleph:main Apr 29, 2026
1 check passed
therealaleph added a commit that referenced this pull request Apr 29, 2026
…p + hotspot sharing

Three substantive PRs landed for this release plus an Iran-safe DoH default:

- #488 by @dazzling-no-more (with credit to @patterniha): fronting_groups
  config field generalizes the Google-edge SNI-rewrite trick to any
  multi-tenant CDN edge (Vercel, Fastly, etc.). Renames `mode = "google_only"`
  → `mode = "direct"` with a deprecated alias keeping existing configs working.
  This is the v1.9.0 headline — new top-level config field + public mode-string
  rename are minor-bump territory. xmux moves to v1.10.0.

- #494 by @dazzling-no-more: edge-cache DNS at Apps Script (CodeFull.gs)
  using CacheService. udp_open / port=53 ops served from cache or DoH
  fallback chain (Cloudflare → Google → Quad9). Cache hits drop typical
  first-hop DNS latency from 600-1200ms to 200-400ms. Default-on, opt-out
  via ENABLE_EDGE_DNS_CACHE; every failure mode falls through to existing
  tunnel-node forward path (zero regression).

- #483 by @yyoyoian-pixel: default listen_host from 127.0.0.1 to 0.0.0.0
  so an Android phone running the tunnel + an iPhone/laptop on the same
  hotspot can use it as proxy. Old configs with explicit 127.0.0.1 are
  honored (not overwritten).

Plus: default `tunnel_doh: true` (flipped from false in v1.8.x) per #468
— Iran ISPs filter direct connections to dns.google, chrome.cloudflare-dns.com,
and other pinned DoH hosts. The bypass-on default silently broke DNS for
the dominant Iranian userbase. The safe default keeps DoH inside the
tunnel; non-Iran users can opt back into the bypass for the latency win.
Backwards-compatible — any config with explicit tunnel_doh keeps its setting.

169 mhrv-rs lib tests + 33 tunnel-node tests + 11 edge-DNS JS tests all
passing. Clean release + UI builds.
@dazzling-no-more dazzling-no-more deleted the feature/edge-dns-cache branch April 29, 2026 16:33
@w0l4i
Copy link
Copy Markdown

w0l4i commented Apr 30, 2026

great commit keep it going champ 👍

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

Labels

type: feature feat: PR — auto-applied by release-drafter

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants