feat(ednsdiag): add DoQ/DoH3/DNSCrypt transports, proxy support, probe & compare

This commit is contained in:
windyboy
2026-08-14 18:17:26 +08:00
parent 8303d78caf
commit 70aea6cd72
21 changed files with 1665 additions and 236 deletions
@@ -6,12 +6,12 @@ below were verified on 2026-08-13.
## Candidate providers
| Provider | DoH endpoint | DoT endpoint / authentication name | Official documentation | Profile |
| --- | --- | --- | --- | --- |
| Cloudflare | `https://cloudflare-dns.com/dns-query` | `one.one.one.one:853` | [DoH](https://developers.cloudflare.com/1.1.1.1/encryption/dns-over-https/make-api-requests/) / [DoT](https://developers.cloudflare.com/1.1.1.1/encryption/dns-over-tls/) | Unfiltered |
| Google | `https://dns.google/dns-query` | `dns.google:853` | [DoH](https://developers.google.com/speed/public-dns/docs/doh) / [DoT](https://developers.google.com/speed/public-dns/docs/dns-over-tls) | Unfiltered |
| Quad9 | `https://dns.quad9.net/dns-query` | `dns.quad9.net:853` | [Quad9 services](https://docs.quad9.net/services/) | Security filtered; HTTP/2 required |
| AdGuard | `https://dns.adguard-dns.com/dns-query` | `dns.adguard-dns.com:853` | [AdGuard providers](https://adguard-dns.io/kb/general/dns-providers/) | Ads, tracking, and security filtered |
| Provider | DoH endpoint | DoT endpoint / authentication name | QUIC support | DNSCrypt | Official documentation | Profile |
| --- | --- | --- | --- | --- | --- | --- |
| Cloudflare | `https://cloudflare-dns.com/dns-query` | `one.one.one.one:853` | DoH3 at the DoH endpoint | No verified built-in stamp | [DoH and HTTP/3](https://developers.cloudflare.com/1.1.1.1/encryption/dns-over-https/make-api-requests/) / [DoT](https://developers.cloudflare.com/1.1.1.1/encryption/dns-over-tls/) | Unfiltered |
| Google | `https://dns.google/dns-query` | `dns.google:853` | DoH3 at the DoH endpoint | No verified built-in stamp | [Secure transports](https://developers.google.com/speed/public-dns/docs/secure-transports) | Unfiltered |
| Quad9 | `https://dns.quad9.net/dns-query` | `dns.quad9.net:853` | Not enabled without an official endpoint statement | No verified built-in stamp | [Quad9 services](https://docs.quad9.net/services/) | Security filtered; HTTP/2 required |
| AdGuard | `https://dns.adguard-dns.com/dns-query` | `dns.adguard-dns.com:853` | DoQ at `dns.adguard-dns.com:853` | Official stamp for `2.dnscrypt.default.ns1.adguard.com` at `94.140.14.14:5443` | [AdGuard dnsproxy example](https://github.com/AdguardTeam/dnsproxy#encrypted-upstreams) / [AdGuard public DNS](https://adguard-dns.io/kb/en/public-dns/overview/) | Ads, tracking, and security filtered |
## Registry requirements
@@ -25,6 +25,11 @@ Each built-in provider entry must include:
- official source URL;
- last verification date.
The runtime `Provider` registry stores the official source URL and verification
date alongside each endpoint. Update both fields whenever an endpoint or
capability is re-verified; the markdown table alone is not authoritative for
runtime metadata.
Do not infer one protocol endpoint from another. Do not treat filtering and
non-filtering services as interchangeable. Provider comparison results must
remain separate.
@@ -8,7 +8,7 @@ validation, fallback, or result claims.
1. Never silently downgrade to plaintext DNS.
2. Validate certificates and authentication domain names. DoT follows the
strict privacy profile in [RFC 8310](https://www.rfc-editor.org/rfc/rfc8310.html).
3. Treat certificate, hostname, SNI, and negotiated ALPN mismatches as hard
3. Treat certificate, hostname, SNI, and any non-empty negotiated ALPN mismatch as hard
failures, not fallback opportunities.
4. Bound response sizes, per-attempt timeouts, total time, redirects, and the
number of attempts.
@@ -19,12 +19,62 @@ validation, fallback, or result claims.
7. Do not enable AXFR, IXFR, or ANY queries.
8. Do not persist full query names or client identifiers by default.
## Proxy policy
DoH and DoT may use an explicit `--proxy` or the standard Go
[`ProxyFromEnvironment`](https://pkg.go.dev/net/http#ProxyFromEnvironment)
selection rules for `HTTPS_PROXY` and `NO_PROXY`. An explicit URL takes
precedence and must use `http` or `https`. DoT establishes an HTTP CONNECT
tunnel and then performs the normal resolver TLS handshake inside it; the
proxy never substitutes for resolver certificate, authentication-domain, SNI,
or ALPN validation.
Proxy credentials may be sent as HTTP Basic authentication when embedded in
the URL, but must never appear in result JSON or diagnostic errors. Result
metadata contains only a sanitized proxy endpoint. An HTTP(S) proxy can observe
the resolver destination, connection timing, and traffic volume even though it
cannot read the resolver TLS payload.
DoH3, DoQ, and the current DNSCrypt transport use UDP or QUIC and do not use a
TCP HTTP CONNECT proxy. Reject an explicit proxy for those protocols instead of
silently connecting directly. Proxy failures are transport failures and never
trigger plaintext DNS or an undisclosed direct connection.
## DoT ALPN policy
The client advertises the `dot` ALPN identifier. RFC 7858 and RFC 8310 do not
require a DoT server on its dedicated port to select an ALPN protocol, so an
empty negotiated ALPN is permitted and reported as empty. If a server selects
a non-empty protocol other than `dot`, abort before sending the DNS query.
The client advertises the IANA-registered `dot` ALPN identifier. An explicit
selection other than `dot` is a hard failure before the DNS query is sent. An
empty selection is permitted and reported because RFC 7858 and RFC 8310 do not
make ALPN negotiation part of DoT server authentication; the dedicated port,
PKIX chain, SNI, and configured authentication domain still identify the
service. This deliberately restores the policy from PR #9 and supersedes the
stricter empty-ALPN rejection introduced by PR #11.
## QUIC transport policy
DoQ requires TLS 1.3 and an exact `doq` ALPN selection. DoH3 requires TLS 1.3,
HTTP/3, and an exact `h3` ALPN selection. Certificate, authentication-domain,
SNI, and ALPN failures abort before a DNS query is sent. The initial
implementation does not send 0-RTT data or enable session resumption because
their replay and linkability properties require a separate policy decision.
Each DoQ query uses one client-initiated bidirectional stream, a two-octet
length prefix, DNS Message ID 0, and STREAM FIN. Truncated frames, extra
responses, non-zero response IDs, unexpected streams, and missing FIN are
protocol failures; they never trigger plaintext or cross-protocol fallback.
Reject DNS messages with the TC bit set on every encrypted transport. In
particular, the UDP-only DNSCrypt implementation must not expose a partial
answer as a completed lookup.
## DNSCrypt transport policy
Accept only allowlisted DNSCrypt v2 stamps. Validate the stamp type, provider
public key, provider name, resolver certificate signature, validity interval,
and encrypted response. Report the stamp IP bootstrap path, provider
authentication name, certificate serial, and crypto construction. Certificate
or response-authentication failures are hard failures and never trigger
plaintext or cross-protocol fallback. Anonymized DNSCrypt remains unavailable.
## Bootstrap transparency
@@ -7,16 +7,20 @@ standard, not on summaries or provider-specific JSON APIs.
| --- | --- | --- |
| Agent Skills package | [Agent Skills specification](https://agentskills.io/specification) | Required package format |
| OMP discovery | [OMP Skills documentation](https://github.com/can1357/oh-my-pi/blob/main/docs/skills.md) | Supported host |
| DoH | [RFC 8484](https://www.rfc-editor.org/rfc/rfc8484.html) | Planned |
| DoT | [RFC 7858](https://www.rfc-editor.org/rfc/rfc7858.html) | Planned |
| DoH | [RFC 8484](https://www.rfc-editor.org/rfc/rfc8484.html) | Implemented over HTTP/1.1 and HTTP/2 |
| DoH3 | [RFC 8484](https://www.rfc-editor.org/rfc/rfc8484.html) over [RFC 9114](https://www.rfc-editor.org/rfc/rfc9114.html) | Implemented |
| DoT | [RFC 7858](https://www.rfc-editor.org/rfc/rfc7858.html) | Implemented |
| DoT authentication profiles | [RFC 8310](https://www.rfc-editor.org/rfc/rfc8310.html) | Strict privacy only |
| DoQ | [RFC 9250](https://www.rfc-editor.org/rfc/rfc9250.html) | Planned |
| DoT ALPN identifier | [IANA TLS ALPN registry](https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids) | Advertise `dot`; reject a different selection; permit and report no selection |
| DoQ | [RFC 9250](https://www.rfc-editor.org/rfc/rfc9250.html) | Implemented for single-response queries |
| ODoH | [RFC 9230](https://www.rfc-editor.org/rfc/rfc9230.html) | Research until a maintained implementation is selected |
| DNS privacy operations | [RFC 8932](https://www.rfc-editor.org/rfc/rfc8932.html) | Security and privacy guidance |
| EDNS(0) padding | [RFC 7830](https://www.rfc-editor.org/rfc/rfc7830.html) and [RFC 8467](https://www.rfc-editor.org/rfc/rfc8467.html) | Evaluate per transport |
| DNSCrypt | [DNSCrypt protocol specification](https://github.com/DNSCrypt/dnscrypt-protocol) | Planned, non-IETF |
| DNSCrypt | [DNSCrypt protocol specification](https://github.com/DNSCrypt/dnscrypt-protocol) | DNSCrypt v2 implemented over UDP; non-IETF |
| Anonymized DNSCrypt | [Anonymized DNSCrypt specification](https://github.com/DNSCrypt/dnscrypt-protocol/blob/master/ANONYMIZED-DNSCRYPT.txt) | Research |
| Go DNS wire and IDNA support | [Go x/net module](https://pkg.go.dev/golang.org/x/net) | Pinned to v0.58.0; use `dnsmessage` and `idna` |
| Go QUIC and HTTP/3 support | [quic-go documentation](https://quic-go.net/docs/) | Pinned to v0.61.0 |
| Go DNSCrypt support | [ameshkov/dnscrypt](https://github.com/ameshkov/dnscrypt) | Pinned to v2.4.0; stamp, certificate, and protocol implementation |
## Deliberate exclusions
@@ -38,3 +42,13 @@ For DoH, accept and send `application/dns-message`. Keep HTTP status separate
from the DNS RCODE: a valid NXDOMAIN or SERVFAIL response still uses HTTP 2xx.
For DoT, use the strict privacy profile and verify both the PKIX chain and the
configured authentication domain name.
For DoQ, use ALPN `doq`, UDP port 853, a separate client-initiated bidirectional
stream per query, the two-octet DNS-over-TCP length field, STREAM FIN, and DNS
Message ID 0. DoH3 retains RFC 8484 message and HTTP semantics and requires
HTTP/3 with ALPN `h3`.
For DNSCrypt v2, parse only DNSCrypt stamps, fetch the provider TXT
certificate, validate its Ed25519 signature and validity interval against the
stamp key, and authenticate encrypted responses. The stamp-supplied provider
name and IP endpoint are part of the authenticated resolver identity.