feat(odoh): ship ODoH client + self-hosted relay (RFC 9230)
Client (mode = "odoh"): URL-query target routing per RFC 9230 §5,
/.well-known/odohconfigs TTL cache with 60s backoff on failure, HPKE
seal/open via odoh-rs, strict-mode default that SERVFAILs on relay
failure instead of silently downgrading. Host-equality config
validation rejects same-operator relay/target pairs.
Relay (`numa relay [PORT]`): axum server with /relay + /health.
SSRF-hardened hostname validator (RFC 1035 ASCII + dot + dash),
4 KiB body cap at the axum layer, 5s full-transaction timeout, and
static 502 on target failure (reqwest internals logged, not leaked).
Aggregate counters only — no per-request logs.
Observability: new `UpstreamTransport { Udp, Doh, Dot, Odoh }`
orthogonal to `QueryPath`, so /stats can tally wire protocols
symmetrically. Recursive mode records `Some(Udp)` for honest
"bytes egressing in cleartext" accounting.
Tests: Suite 8 exercises the client end-to-end via Frank Denis's
public relay + Cloudflare target; Suite 9 exercises `numa relay`
forwarding + guards against Cloudflare as the real far end. Full
probe script at tests/probe-odoh-ecosystem.sh verifies the entire
public ODoH ecosystem (4 targets + 1 relay per DNSCrypt's curated
list — confirms deploying Numa's relay doubles global supply).
This commit is contained in:
15
src/api.rs
15
src/api.rs
@@ -170,6 +170,7 @@ struct StatsResponse {
|
||||
srtt: bool,
|
||||
queries: QueriesStats,
|
||||
transport: TransportStats,
|
||||
upstream_transport: UpstreamTransportStats,
|
||||
cache: CacheStats,
|
||||
overrides: OverrideStats,
|
||||
blocking: BlockingStatsResponse,
|
||||
@@ -186,6 +187,14 @@ struct TransportStats {
|
||||
doh: u64,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct UpstreamTransportStats {
|
||||
udp: u64,
|
||||
doh: u64,
|
||||
dot: u64,
|
||||
odoh: u64,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct MobileStatsResponse {
|
||||
enabled: bool,
|
||||
@@ -566,6 +575,12 @@ async fn stats(State(ctx): State<Arc<ServerCtx>>) -> Json<StatsResponse> {
|
||||
dot: snap.transport_dot,
|
||||
doh: snap.transport_doh,
|
||||
},
|
||||
upstream_transport: UpstreamTransportStats {
|
||||
udp: snap.upstream_transport_udp,
|
||||
doh: snap.upstream_transport_doh,
|
||||
dot: snap.upstream_transport_dot,
|
||||
odoh: snap.upstream_transport_odoh,
|
||||
},
|
||||
cache: CacheStats {
|
||||
entries: cache_len,
|
||||
max_entries: cache_max,
|
||||
|
||||
Reference in New Issue
Block a user