This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
advoid is a DNS-based ad blocker written in Rust. It acts as a DNS stub resolver that intercepts queries, checks them against a blocklist, and returns NXDOMAIN for blocked domains while forwarding allowed queries to an upstream DNS resolver.
# Build
cargo build
cargo build --release # LTO + single codegen unit + strip
# Test
cargo test
cargo test test_name # Run a single test by name
# Run (minimum required arguments)
cargo run -- --bind 127.0.0.1:53 --upstream 1.1.1.1:53 --exporter 127.0.0.1:3000 --block path/to/blocklist.txt
# Docker
docker build -t advoid .The project uses Rust edition 2024. Windows MSVC builds use static CRT linking (configured in .cargo/config.toml).
- DNS query arrives at UDP socket (bound via
--bind) - RFC 6303 check: if
block_local_zoneis enabled, PTR queries for private/local IP reverse zones return NXDOMAIN immediately - Blocklist check: domain is matched against blocklist using suffix matching (with a two-level allow/block cache)
- If blocked: return NXDOMAIN, increment
dns_requests_blockmetric - If allowed: forward to upstream resolver, return response, increment
dns_requests_forwardmetric - Request/response events are sent to configured sink (S3/Databricks/stub) via unbounded channels
- Background workers batch and upload events periodically
src/dns.rs — DNS request handler
StubRequestHandler: Implements hickory-server'sRequestHandlertraitis_rfc6303_zone(): Blocks PTR queries for private/local IP reverse zones per RFC 6303 (enabled by default, disable with--forward-local-zone)CheckedDomain: Two-level cache (separate allow/blockFxHashSets) to avoid repeated full blocklist scans- EDNS support, Prometheus metrics emission, event sink integration
src/event/ — Event sink system (module directory)
mod.rs:Sinktrait,Request/Responsetypes, re-exports all sink typesworker.rs:EventUploadertrait + genericinitialize_worker()— shared batching, NDJSON serialization, sleep/cancel, and graceful drain logic. New sinks only need to implementEventUploader.channel.rs:ChannelSink— sharedSinkimpl for channel-based sinks (UUID generation + channel send). Sinks that use background workers wrap this and delegate.stub.rs:StubSink(no-op)s3.rs:S3Sink,S3Uploader(implementsEventUploader),s3_key()databricks.rs:DatabricksSink,DatabricksUploader,DatabricksClient(OAuth token caching with automatic refresh)- To add a new sink: implement
EventUploader, create a struct wrappingChannelSink, useinitialize_worker()in the constructor
src/blocklist.rs — Loads blocklist from file path or HTTP(S) URL. Format: one domain per line, # comments, trailing dot normalized.
src/metrics.rs — Prometheus metrics server (axum, /metrics endpoint). Counters: dns_requests_total, dns_requests_block, dns_requests_forward.
src/trace.rs — Optional OpenTelemetry tracing via --otel. Falls back to stdout logging. OtelInitGuard ensures proper shutdown. Supports --otel-api-key (or OTEL_API_KEY env var) for backends requiring authentication (e.g., New Relic's api-key header). TLS is automatically enabled when the endpoint uses https://. service.name and service.version default to crate metadata but can be overridden via --otel-service-name and --otel-service-version.
src/main.rs — CLI (clap derive), component initialization, graceful shutdown via ctrl-c + cancellation tokens.
hickory-server/hickory-client: DNS protocoltokio: Async runtimeaxum: Metrics HTTP serveraws-sdk-s3: S3 event sinkreqwest: HTTP client (blocklist fetching, Databricks API)rustc-hash: FxHashSet for fast blocklist lookupsopentelemetry-otlp: Optional distributed tracing
The crate is split into a library (src/lib.rs re-exports all modules) and a binary (src/main.rs). Tests live in #[cfg(test)] modules within each source file.
docs/rfc-violations/ contains known RFC compliance issues, numbered and severity-tagged (e.g., 01-high-...). When a violation is fixed, rename the file to .done.md (e.g., 02-high-error-response-query-id-zero.done.md). Do not delete the file.
docs/hickory-dns/ contains notes on how to use the Hickory DNS library (hickory-server, hickory-client, hickory-proto). When investigating or implementing Hickory DNS APIs, document the findings there for future reference.
- The blocklist is loaded once at startup into an
FxHashSetand never reloaded - Event sinks use unbounded channels — be mindful of memory under high query loads
- Databricks credentials support both CLI args and environment variables (
DATABRICKS_HOST,DATABRICKS_CLIENT_ID,DATABRICKS_CLIENT_SECRET,DATABRICKS_VOLUME_PATH); CLI args take precedence - OTel API key can be set via
--otel-api-keyorOTEL_API_KEYenv var; HTTPS endpoints automatically get TLS configured via tonic'sClientTlsConfig - The release profile enables aggressive optimization: LTO, single codegen unit, binary stripping