# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
rust-doh is a fast DNS-over-HTTPS (DoH), DNS-over-QUIC (DoQ), and Oblivious DoH (ODoH) proxy server written in Rust. It acts as a translation layer between DNS and HTTPS/QUIC protocols, not performing DNS resolution itself but adding DoH/DoQ/ODoH support to existing DNS resolvers.
## Build and Development Commands
### Building
```bash
# Build with default features (includes TLS support)
cargo build --release
# Build without TLS support
cargo build --release --no-default-features
# Check code without building
cargo check
# Run clippy for linting (use before committing changes)
cargo clippy --all-targets --all-features -- -D warnings
# Format code
cargo fmt
# Check formatting
cargo fmt -- --check
```
### Testing
```bash
# Run all tests
cargo test
# Run tests with verbose output
cargo test -- --nocapture
# Run tests for a specific module
cargo test dns
cargo test odoh
# Run a specific test function
cargo test test_function_name
# Manual testing with test.sh (requires server running on localhost:3000)
./test.sh # Tests DNS queries using curl and drill
```
### Running the Server
```bash
# Basic usage (behind reverse proxy)
cargo run -- -H 'doh.example.com' -u 127.0.0.1:53
# With built-in TLS
cargo run -- -H 'doh.example.com' -u 127.0.0.1:53 -l 0.0.0.0:443 \
-i /path/to/cert.pem -I /path/to/key.pem
# With DNS-over-QUIC (DoQ)
cargo run -- -H 'doh.example.com' -u 127.0.0.1:53 \
-i /path/to/cert.pem -I /path/to/key.pem --enable-doq
# With EDNS Client Subnet
cargo run -- -H 'doh.example.com' -u 127.0.0.1:53 --enable-ecs
# Enable ODoH support
cargo run -- -H 'doh.example.com' -u 127.0.0.1:53 --allow-odoh-post
```
## Architecture
### Project Structure
```
├── src/
│ ├── main.rs # Entry point
│ ├── config.rs # CLI argument parsing
│ ├── constants.rs # Default configuration values
│ ├── utils.rs # Utility functions
│ └── libdoh/src/ # Core DoH library
│ ├── lib.rs # HTTP server and request routing
│ ├── globals.rs # Shared configuration state
│ ├── dns.rs # DNS packet parsing/building
│ ├── dns_json.rs # JSON API support
│ ├── doq.rs # DNS-over-QUIC protocol
│ ├── edns_ecs.rs # EDNS Client Subnet
│ ├── odoh.rs # Oblivious DoH protocol
│ ├── tls.rs # TLS certificate handling
│ ├── errors.rs # Error types
│ └── constants.rs # Library-level constants
```
### Core Components
1. **Main Binary (`src/main.rs`)**
- Entry point that sets up Tokio runtime
- Parses command-line arguments via `src/config.rs`
- Initializes `Globals` struct with configuration
- Creates and runs the DoH server
2. **libdoh Library (`src/libdoh/`)**
- **`lib.rs`**: Core DoH server implementation
- HTTP request handling and routing
- TLS server setup (when feature enabled)
- Connection management and client tracking
- **`dns.rs`**: DNS packet parsing and building
- **`dns_json.rs`**: Google DNS-over-HTTPS JSON API support
- **`doq.rs`**: DNS-over-QUIC (RFC 9250) implementation
- QUIC server with quiche library
- Stream-based DNS message handling
- Connection pooling and management
- **`edns_ecs.rs`**: EDNS Client Subnet implementation
- **`odoh.rs`**: Oblivious DoH protocol support
- **`tls.rs`**: TLS certificate loading and validation
- **`globals.rs`**: Shared global configuration state
- **`errors.rs`**: Error types and handling
3. **Configuration (`src/config.rs`)**
- Command-line argument parsing using clap
- DNS stamp generation for clients
- Certificate validation and path handling
### Key Data Flow
**DoH (DNS-over-HTTPS):**
1. HTTP/HTTPS requests arrive at `/dns-query` (or custom path)
2. DoH server validates request (POST with DNS wire format or GET with base64)
3. For JSON API: handles `Accept: application/dns-json` specially
4. Optional: Adds EDNS Client Subnet if enabled
5. Forwards DNS query to upstream resolver via UDP/TCP
6. Returns DNS response wrapped in HTTP response
**DoQ (DNS-over-QUIC):**
1. QUIC connection established on UDP port 853
2. DNS queries sent as individual QUIC streams
3. 2-byte length prefix added to DNS messages (RFC 9250)
4. Query forwarded to upstream resolver
5. Response sent back on same QUIC stream
6. Connection reused for multiple queries
### Features and Conditional Compilation
- **`tls` feature** (default): Enables built-in TLS support
- Without it, must run behind reverse proxy
- Controlled by `--no-default-features` flag
### Global State (`Globals` struct)
Shared configuration including:
- Server addresses and ports
- TLS certificate paths
- TTL bounds (min/max/error)
- Client limits and timeouts
- EDNS Client Subnet settings
- ODoH configuration
- Runtime handle for async operations
## Important Implementation Details
1. **Memory Allocator**: Uses mimalloc for better performance (configured in `src/main.rs`)
2. **Async Runtime**: Tokio multi-threaded runtime with all CPUs by default
3. **Client Tracking**: Arc<AtomicUsize> for concurrent client counting
4. **ODoH Keys**: Currently stored only in memory (requires sticky sessions in load-balanced setups)
5. **Certificate Reloading**: Automatic without restart when files change (uses ArcSwap)
6. **HTTP/2 Support**: Enabled when using built-in TLS
7. **DNS Packet Size**: Max 4096 bytes, configurable via constants
8. **Connection Limits**: Per-client connection pooling to prevent DoS
## Common Development Tasks
### Adding New Command-Line Options
1. Add field to `Globals` struct in `src/libdoh/src/globals.rs`
2. Add argument parsing in `src/config.rs`
3. Update default values in `src/constants.rs` if needed
4. Use the field in relevant code paths
### Modifying DNS Handling
- Core DNS logic is in `src/libdoh/src/dns.rs`
- EDNS extensions in `src/libdoh/src/edns_ecs.rs`
- JSON API translation in `src/libdoh/src/dns_json.rs`
### Working with ODoH
- Protocol implementation in `src/libdoh/src/odoh.rs`
- Key rotation handled by `ODoHRotator`
- Requires `--allow-odoh-post` flag to accept POST queries
## Testing Approach
The project uses minimal automated testing. Primary testing is done through:
1. Unit tests in individual modules (run with `cargo test`)
2. Manual testing using `test.sh` script (requires server running)
3. Integration testing with actual DNS queries
4. Test DNS queries can be sent with curl or drill:
```bash
curl "http://localhost:3000/dns-query?dns=<base64_dns_query>"
curl -H "Accept: application/dns-json" \
"http://localhost:3000/dns-query?name=example.com&type=1"
```
## Key Files to Understand
- `src/libdoh/src/lib.rs:DoHServer::entrypoint()` - Main HTTP request handler
- `src/libdoh/src/dns.rs:proxy_dns_query()` - Core DNS forwarding logic
- `src/libdoh/src/globals.rs:Globals` - All runtime configuration
- `src/config.rs:parse_opts()` - Command-line argument processing
## Deployment Notes
1. **Reverse Proxy Deployment** (Recommended):
- Run on localhost:3000
- Let nginx/Caddy/HAProxy handle TLS
- Better for sharing port 443
2. **Standalone Deployment**:
- Requires TLS certificates in PEM/PKCS#8 format
- ECDSA keys need conversion to PKCS#8
- Certificates auto-reload on change
3. **Performance Settings**:
- `--max-clients`: Concurrent client limit
- `--max-concurrent`: Streams per client
- `--timeout`: Query timeout in seconds