feat(ednsdiag): add DoQ/DoH3/DNSCrypt transports, proxy support, probe & compare
This commit is contained in:
@@ -15,9 +15,9 @@ URLs, TLS sessions, or DNS wire messages themselves.
|
||||
| --- | --- | --- |
|
||||
| DNS over HTTPS (DoH) | Available (GET and POST) | [RFC 8484](https://www.rfc-editor.org/rfc/rfc8484.html) |
|
||||
| DNS over TLS (DoT) | Available (strict authentication) | [RFC 7858](https://www.rfc-editor.org/rfc/rfc7858.html), [RFC 8310](https://www.rfc-editor.org/rfc/rfc8310.html) |
|
||||
| DNS over QUIC (DoQ) | Planned | [RFC 9250](https://www.rfc-editor.org/rfc/rfc9250.html) |
|
||||
| DoH over HTTP/3 (DoH3) | Planned | RFC 8484 over HTTP/3 |
|
||||
| DNSCrypt | Planned | [DNSCrypt protocol specification](https://github.com/DNSCrypt/dnscrypt-protocol) |
|
||||
| DNS over QUIC (DoQ) | Available | [RFC 9250](https://www.rfc-editor.org/rfc/rfc9250.html) |
|
||||
| DoH over HTTP/3 (DoH3) | Available (GET and POST) | RFC 8484 over HTTP/3 |
|
||||
| DNSCrypt | Available (v2 over UDP) | [DNSCrypt protocol specification](https://github.com/DNSCrypt/dnscrypt-protocol) |
|
||||
| Oblivious DoH (ODoH) | Research | [RFC 9230](https://www.rfc-editor.org/rfc/rfc9230.html) |
|
||||
| Anonymized DNSCrypt | Research | [Anonymized DNSCrypt specification](https://github.com/DNSCrypt/dnscrypt-protocol/blob/master/ANONYMIZED-DNSCRYPT.txt) |
|
||||
|
||||
@@ -37,7 +37,7 @@ addresses returned in DNS answers.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Go 1.26 or later when running or building from source
|
||||
- Go 1.26.6 or later when running or building from source
|
||||
- Network access to the selected encrypted DNS resolver
|
||||
- A host that supports the [Agent Skills package format](https://agentskills.io/specification) when using the repository as a Skill
|
||||
|
||||
@@ -66,6 +66,14 @@ the repository root:
|
||||
go run ./cmd/ednsdiag capabilities
|
||||
go run ./cmd/ednsdiag query example.com A --protocol doh --provider cloudflare
|
||||
go run ./cmd/ednsdiag query gmail.com MX --protocol dot --provider google --timeout 5s
|
||||
HTTPS_PROXY=http://127.0.0.1:8080 go run ./cmd/ednsdiag query example.com A --protocol doh
|
||||
go run ./cmd/ednsdiag query example.com A --protocol dot --proxy http://127.0.0.1:8080
|
||||
go run ./cmd/ednsdiag query example.com AAAA --protocol doq --provider adguard
|
||||
go run ./cmd/ednsdiag query example.com HTTPS --protocol doh3 --provider cloudflare
|
||||
go run ./cmd/ednsdiag query example.com A --protocol dnscrypt --provider adguard
|
||||
go run ./cmd/ednsdiag probe example.com A --protocol dot --provider cloudflare
|
||||
go run ./cmd/ednsdiag compare example.com A \
|
||||
--target doh:cloudflare --target dot:google
|
||||
```
|
||||
|
||||
The first run may download the modules pinned in `go.mod` and `go.sum`.
|
||||
@@ -86,23 +94,51 @@ does not currently publish release binaries.
|
||||
ednsdiag capabilities
|
||||
ednsdiag version
|
||||
ednsdiag query <domain> [type] \
|
||||
[--protocol doh|dot] \
|
||||
[--protocol doh|dot|doq|doh3|dnscrypt|odoh|anonymized-dnscrypt] \
|
||||
[--provider cloudflare|google|quad9|adguard] \
|
||||
[--method post|get] \
|
||||
[--proxy http://host:port] \
|
||||
[--timeout 5s]
|
||||
ednsdiag probe <domain> [type] [query options]
|
||||
ednsdiag compare <domain> [type] \
|
||||
--target protocol:provider[:method] \
|
||||
--target protocol:provider[:method] \
|
||||
[--proxy http://host:port] \
|
||||
[--attempt-timeout 5s] [--timeout 30s] [--max-attempts 4]
|
||||
```
|
||||
|
||||
Defaults are `A`, `doh`, `cloudflare`, `post`, and `5s`. `--method` applies
|
||||
only to DoH. The timeout must be between `250ms` and `30s`.
|
||||
only to DoH and DoH3. The timeout must be between `250ms` and `30s`.
|
||||
Research protocols are accepted as inputs so automation receives a structured
|
||||
`unsupported` result and exit code `4`; they are never silently substituted.
|
||||
|
||||
DoH and DoT honor Go's standard `HTTPS_PROXY`/`https_proxy` and
|
||||
`NO_PROXY`/`no_proxy` environment variables. `--proxy` overrides environment
|
||||
selection and accepts an `http://` or `https://` proxy URL, including optional
|
||||
Basic-auth userinfo. DoT uses HTTP CONNECT before its resolver TLS handshake.
|
||||
DoH3, DoQ, and DNSCrypt are UDP/QUIC transports and cannot use this TCP CONNECT
|
||||
proxy; an explicit proxy combined with one of those protocols is rejected.
|
||||
The same `--proxy` is shared by every DoH/DoT target in a `compare` operation.
|
||||
See Go's official [`ProxyFromEnvironment` documentation](https://pkg.go.dev/net/http#ProxyFromEnvironment)
|
||||
for environment-variable and `NO_PROXY` matching rules.
|
||||
|
||||
`compare` accepts 2–8 unique, allowlisted targets, bounded by `--max-attempts`.
|
||||
Its total timeout is `250ms`–`60s`; each attempt timeout is `250ms`–`30s` and
|
||||
cannot exceed the total. Comparison targets start concurrently, results retain
|
||||
the requested target order, and answers are never merged.
|
||||
|
||||
Supported record types are `A`, `AAAA`, `CNAME`, `MX`, `TXT`, `NS`, `SOA`,
|
||||
`CAA`, `SRV`, `PTR`, `HTTPS`, and `SVCB`. For `PTR`, pass an IP address; the CLI
|
||||
constructs the reverse name. Other IP literals and local names remain blocked.
|
||||
|
||||
Built-in resolver profiles:
|
||||
|
||||
| Provider | Profile |
|
||||
| --- | --- |
|
||||
| Cloudflare | Unfiltered |
|
||||
| Google | Unfiltered |
|
||||
| Quad9 | Security-filtered |
|
||||
| AdGuard | Ad- and security-filtered |
|
||||
| Provider | Profile | DoH | DoT | DoQ | DoH3 | DNSCrypt |
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| Cloudflare | Unfiltered | Yes | Yes | No | Yes | No |
|
||||
| Google | Unfiltered | Yes | Yes | No | Yes | No |
|
||||
| Quad9 | Security-filtered | Yes | Yes | No | No | No |
|
||||
| AdGuard | Ad- and security-filtered | Yes | Yes | Yes | No | Yes |
|
||||
|
||||
Filtering policies can affect DNS answers. Results always identify the
|
||||
provider and profile used.
|
||||
@@ -122,11 +158,37 @@ Every query returns structured JSON compatible with
|
||||
it is not local DNSSEC validation.
|
||||
- `transport.bootstrap: system_resolver` means the operating system resolver
|
||||
was used to locate the encrypted resolver endpoint.
|
||||
- `transport.proxy`, when present, is the HTTP(S) proxy endpoint actually
|
||||
selected for DoH or DoT. Embedded credentials are never reported.
|
||||
- DNSCrypt reports `bootstrap: stamp_ip`, the authenticated provider name,
|
||||
resolver certificate serial, and selected crypto construction.
|
||||
- DoH and DoH3 subtract a valid HTTP `Age` value from returned answer TTLs and
|
||||
report it as `transport.http_age_seconds`.
|
||||
- Truncated or non-representable DNS answers are protocol failures rather than
|
||||
partial `completed: true` results.
|
||||
|
||||
Human-readable usage errors go to stderr. Machine-readable operational results
|
||||
go to stdout. Stable exit codes are:
|
||||
|
||||
| Code | Meaning |
|
||||
| --- | --- |
|
||||
| `0` | The requested encrypted DNS operation completed; inspect `dns.rcode`. |
|
||||
| `1` | Local or internal failure. |
|
||||
| `2` | Invalid input or CLI usage. |
|
||||
| `3` | Transport or DNS protocol failure. |
|
||||
| `4` | Known but unsupported capability or provider/protocol combination. |
|
||||
|
||||
See [`references/contracts.md`](references/contracts.md) for the complete v1
|
||||
command and result contract.
|
||||
|
||||
## Security Model
|
||||
|
||||
- DoH uses standard `application/dns-message` wire messages.
|
||||
- DoT verifies the PKIX certificate chain and configured authentication domain.
|
||||
- DoT advertises ALPN `dot`; an empty selection is accepted and reported, while
|
||||
selection of a different application protocol is rejected.
|
||||
- DNSCrypt validates the resolver stamp, Ed25519-signed certificate, validity
|
||||
interval, provider identity, and encrypted response before accepting DNS data.
|
||||
- Plaintext fallback is prohibited.
|
||||
- DNS errors are not retried through another protocol as transport failures.
|
||||
- Provider and protocol results remain separate.
|
||||
@@ -140,7 +202,19 @@ model and privacy boundaries.
|
||||
|
||||
```bash
|
||||
go test ./...
|
||||
go test -race ./...
|
||||
go vet ./...
|
||||
go run honnef.co/go/tools/cmd/staticcheck@v0.7.0 ./...
|
||||
go run golang.org/x/vuln/cmd/govulncheck@v1.6.0 ./...
|
||||
python3 scripts/validate_skill.py .
|
||||
```
|
||||
|
||||
Public endpoint interoperability tests are opt-in and skip cleanly when the
|
||||
host cannot reach the network:
|
||||
|
||||
```bash
|
||||
EDNSDIAG_PUBLIC_INTEROP=1 go test ./internal/edns \
|
||||
-run '^TestPublicCloudflareDo[HT]Interoperability$' -count=1 -v
|
||||
```
|
||||
|
||||
Protocol behavior must remain aligned with
|
||||
|
||||
Reference in New Issue
Block a user