Files
numa/README.md
Razvan Dimescu 3816cf644d gitignore CLAUDE.md and update README for async tokio
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 04:58:11 +02:00

124 lines
3.3 KiB
Markdown

# dns_fun
A DNS forwarding/caching proxy written from scratch in Rust. Parses and serializes DNS wire protocol (RFC 1035), serves local zone records from TOML config, caches upstream responses with TTL-aware expiration, and logs every query with structured output.
No DNS libraries — just `tokio::net::UdpSocket` and manual packet parsing. Each query is handled concurrently via `tokio::spawn`.
## Record Types
A, NS, CNAME, MX, AAAA
## Usage
```bash
# Run with default config (dns_fun.toml)
sudo cargo run
# Run with custom config path
sudo cargo run -- path/to/config.toml
# Test
dig @127.0.0.1 google.com
dig @127.0.0.1 mysite.local
```
Requires root/sudo for binding to port 53.
## Configuration
Edit `dns_fun.toml`:
```toml
[server]
bind_addr = "0.0.0.0:53"
[upstream]
address = "8.8.8.8"
port = 53
timeout_ms = 3000
[cache]
max_entries = 10000
min_ttl = 60 # floor: cache at least 60s
max_ttl = 86400 # ceiling: never cache longer than 24h
[[zones]]
domain = "mysite.local"
record_type = "A"
value = "127.0.0.1"
ttl = 60
[[zones]]
domain = "other.local"
record_type = "AAAA"
value = "::1"
ttl = 120
```
All sections are optional — sensible defaults are used if the config file is missing.
## Request Pipeline
```
Query -> Parse -> Local Zones -> Cache -> Upstream Forward -> Respond
```
1. **Local zones** — match against records defined in `[[zones]]`, respond immediately
2. **Cache** — return TTL-adjusted cached response if available
3. **Forward** — send query to upstream resolver, cache the response
4. **SERVFAIL** — returned to client on upstream failure
## Caching
- TTL derived from minimum TTL across answer records
- Clamped to configured `min_ttl`/`max_ttl` bounds
- TTLs in cached responses decrease over time (adjusted on serve)
- Lazy eviction on capacity overflow + periodic sweep every 1000 queries
## Logging
Controlled via `RUST_LOG` environment variable:
```bash
RUST_LOG=info sudo cargo run # default — one line per query
RUST_LOG=debug sudo cargo run # includes response details
RUST_LOG=warn sudo cargo run # errors only
```
Log output:
```
2026-03-10T14:23:01.123Z INFO 192.168.1.5:41234 | A google.com | FORWARD | NOERROR | 12ms
2026-03-10T14:23:01.456Z INFO 192.168.1.5:41235 | A mysite.local | LOCAL | NOERROR | 0ms
2026-03-10T14:23:02.789Z INFO 192.168.1.5:41236 | A google.com | CACHED | NOERROR | 0ms
```
Stats summary (total, forwarded, cached, local, blocked, errors) logged every 1000 queries.
## Project Structure
```
src/
main.rs # async startup, tokio event loop, ServerCtx, per-query task spawn
lib.rs # module declarations, Error/Result type aliases
buffer.rs # BytePacketBuffer — 512-byte DNS wire format read/write
header.rs # DnsHeader, ResultCode
question.rs # DnsQuestion, QueryType
record.rs # DnsRecord (A, NS, CNAME, MX, AAAA, UNKNOWN)
packet.rs # DnsPacket — full DNS message parse/serialize
config.rs # TOML config loading, zone map builder
cache.rs # TTL-aware DNS response cache with lazy eviction
forward.rs # async upstream forwarding
stats.rs # query counters and periodic summary
```
## Dependencies
```toml
tokio = { version = "1", features = ["rt-multi-thread", "macros", "net", "time"] }
toml = "0.8"
serde = { version = "1", features = ["derive"] }
log = "0.4"
env_logger = "0.11"
```