# DoH Forwarder — Project Plan ## Overview A lightweight dual-protocol DNS forwarder written in Rust. It accepts DNS queries over both traditional DNS (UDP/TCP) and DNS-over-HTTPS (RFC 8484), then forwards them upstream via DoH (HTTPS POST) to configurable resolvers. ## Goals - Accept DoH queries over HTTP (GET and POST per RFC 8484) - Accept traditional DNS queries over UDP and TCP - Forward all queries to upstream DoH resolvers via HTTPS POST - Support multiple upstream resolvers with round-robin or failover selection - TOML configuration with environment variable overrides - Graceful shutdown on SIGINT/SIGTERM - Health-check endpoint - Low resource footprint, single binary ## Architecture ``` ┌─────────────┐ ┌──────────────────┐ ┌─────────────────┐ │ DNS Client │─────▶│ DoH Forwarder │─────▶│ Upstream DoH │ │ (browser, │ HTTPS│ (this project) │ HTTPS│ (1.1.1.1, │ │ app) │◀─────│ /dns-query │◀─────│ 8.8.8.8, …) │ └─────────────┘ │ /health │ └─────────────────┘ │ └──────────────────┘ │ UDP/TCP ▲ │ (port 5353) │ └──────────────────────┘ System DNS (resolv.conf) ``` ## Tech Stack - **Language**: Rust - **HTTP server**: Axum - **DNS wire-format**: hickory-proto - **HTTP client**: reqwest (with rustls-tls) - **Async runtime**: Tokio - **Config**: TOML (config file) + env vars - **Logging**: tracing + tracing-subscriber (env-filter) - **Error handling**: thiserror, anyhow - **Encoding**: base64 ## Milestones ### M1 — Core Forwarding ✅ - [x] Project scaffold (Cargo.toml, src layout) - [x] HTTP server with `/dns-query` endpoint (GET + POST per RFC 8484) - [x] Parse incoming DNS wire-format queries (hickory-proto) - [x] Forward to upstream DoH resolver via HTTPS POST - [x] Return DNS wire-format response - [x] Health endpoint (`GET /health`) - [x] SERVFAIL response when upstream fails - [x] Integration tests (tests/integration.rs) ### M2 — Multiple Upstreams & Config ✅ - [x] TOML config file support (config/default.toml) - [x] Multiple upstream resolver support - [x] Upstream selection strategy (round-robin / failover) - [x] Automatic retry with next resolver on failure - [x] Environment variable overrides (DOH_LISTEN, DOH_UPSTREAM_STRATEGY, DOH_UPSTREAM_RESOLVERS) - [x] Graceful shutdown (SIGINT / SIGTERM) ### M3 — Local DNS Listener ✅ - [x] UDP listener on configurable port (default 5353) - [x] TCP listener on configurable port (default 5353) - [x] DNS over TCP with 2-byte length prefix (RFC 1035 §4.2.2) - [x] Forward through DoH pipeline (shared upstream client) - [x] Return DNS wire-format response over UDP/TCP - [x] Config: `[dns]` section with `listen` address - [ ] Integration test with UDP/TCP clients ### M4 — Caching - [ ] In-memory DNS response cache - [ ] Cache key = (query name, query type) - [ ] Respect DNS response TTL (with configurable max) - [ ] Cache eviction (TTL-based + LRU size cap) - [ ] Cache stats endpoint (`/cache/stats`) ### M5 — Observability - [ ] Request logging middleware (query name, type, latency, upstream used) - [ ] Metrics endpoint (Prometheus format) ### M6 — Blocking & Filtering - [ ] Domain blocklist (file-based, one domain per line) - [ ] Wildcard / suffix matching - [ ] Return NXDOMAIN for blocked queries - [ ] Hot-reload blocklist on SIGHUP ### M7 — Production Hardening - [ ] Rate limiting (per-IP) - [ ] Dockerfile (multi-stage, distroless) - [ ] Helm chart - [ ] CI pipeline (lint, test, build, image push) ## Directory Structure ``` doh-forwarder/ ├── Cargo.toml ├── config/ │ └── default.toml ├── src/ │ ├── main.rs # entrypoint, server setup, graceful shutdown │ ├── lib.rs # public API re-exports │ ├── config.rs # TOML config parsing + env var overrides │ ├── routes.rs # HTTP route handlers (/dns-query, /health) │ ├── dns/ │ │ ├── mod.rs │ │ ├── query.rs # DNS wire-format query parsing │ │ ├── response.rs # DNS response building (SERVFAIL) │ │ └── listener.rs # UDP/TCP DNS listener │ └── upstream/ │ ├── mod.rs │ └── doh_client.rs # DoH forwarding client (retry, strategies) └── tests/ └── integration.rs # HTTP integration tests ``` ## Non-Goals (for now) - DNS-over-TLS (DoT) listener - DNSSEC validation - Persistent cache (Redis / disk) - Web UI / dashboard