doh-proxy 0.9.15

A DNS-over-HTTPS (DoH) and ODoH (Oblivious DoH) proxy
# 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
   # Test with curl (GET request)
   curl "http://localhost:3000/dns-query?dns=<base64_dns_query>"

   # Test with JSON API
   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