An experimental high-performance DNS query tool built in Rust with AF_XDP and eBPF for extremely fast bulk DNS lookups. pugDNS sends DNS packets from user space through AF_XDP and uses a selective XDP program to capture only the DNS replies it owns.
pugDNS is designed for security researchers, network administrators, and penetration testers who need DNS reconnaissance at scale. The current release is the Rust implementation at the repository root; the old Go implementation is no longer part of the published tree.
pugDNS can saturate a 1 Gbit/s link. Run it on a host where high packet rates
are acceptable and verify the interface, gateway MAC, memlock, and XDP setup
with --doctor before large scans.
pugDNS is designed to push bulk A-record lookups through AF_XDP with minimal kernel-network-stack overhead. The current high-throughput defaults use iterative DNS from root hints, a selective XDP reply filter, bounded authority fanout, paced authority hedging, and a large Rust worker pool.
The current promoted benchmark profile was measured on the 500,000-domain
corpus at pugrecon/bench/pugdns_ch_500k_input.txt on the AX42 Hetzner host
used during the optimization work. These are no-extra-knob default runs after
the Rust implementation was promoted:
| Run | Wall time | Domains/s | Successful | Failed | TX drops |
|---|---|---|---|---|---|
| 1 | 5.042s | 99,123 | 499,704 | 93 | 0 |
| 2 | 4.934s | 101,305 | 499,694 | 103 | 0 |
| 3 | 4.811s | 103,891 | 499,704 | 93 | 0 |
| 4 | 4.743s | 105,375 | 499,704 | 93 | 0 |
| 5 | 4.745s | 105,330 | 499,704 | 93 | 0 |
The first sample landed just under 100K domains/s; the next four consecutive
default runs cleared 100K domains/s with zero AF_XDP TX drops. See
PERF_NOTES.md for the full optimization log, rejected experiments, and
counter-level details.
- High-speed DNS query transmission via AF_XDP sockets
- Rust iterative resolver that walks root/TLD/authoritative delegations directly
- Selective eBPF/XDP DNS reply capture without stealing unrelated traffic
- Bounded authority fanout, scored authority ordering, and paced hedging
- Support for multiple nameservers and large domain lists via input files
- Automatic query retries for unanswered domains
- Kernel and user-space drop monitoring for observability
- Config-file access to advanced throughput and resolver tuning
- Plain progress output and compact final summaries
- Results saved in JSON format
- Support for different DNS record types (AAAA, MX, etc.)
- IPv6 support
- Dynamic rate limiting profiles
Usage:
sudo ./pugdns -domains domains.txt -nameservers resolvers.txt [-interface eth0]
Fast defaults are enabled: iterative AF_XDP root-hint walking, JSON output,
5 retries, 1s retry timeout, and the measured high-throughput Rust defaults.
Common flags:
-resolve-mode <mode> recursive or iterative. Iterative uses AF_XDP root-hint walking.
-iterative-cname <m> Iterative CNAME handling: stop or chase. Default: stop.
-domains <file> Domain list to query, one name per line.
-domain <name> Query one domain instead of a file.
-nameservers <file> Resolver list, one IPv4 resolver per line.
-interface <name> Network interface. Auto-discovered when omitted.
-xdp-mode <mode> generic, driver, or auto. Default: generic.
-doctor Run setup, input, resolver, AF_XDP, and BPF checks.
-max-retries <n> Maximum retries per domain. Default: 5.
-output <file> Result file. Default: results.jsonl. Empty disables output.
-output-mode <mode> json or names. Default: json.
-metrics-file <file> Write JSON runtime metrics and throughput counters.
-summary <mode> text, json, or none. Default: text.
-progress <mode> auto, plain, or none. Default: auto.
-config <file> Load YAML or JSON config.
-quiet Suppress progress and routine logs.
--version Show version information.
--generate-config <file>
Write a complete YAML config with advanced tuning fields.
Advanced tuning fields remain available through YAML or JSON config files. Use
./pugdns --generate-config config.yaml to start from the current defaults.
Example Usage:
# Query domains using the benchmarked fast defaults.
sudo ./pugdns -interface eth0 -domains domains.txt -nameservers resolvers.txt
# Capture runtime metrics while using the same defaults.
sudo ./pugdns -interface eth0 -domains domains.txt -nameservers resolvers.txt -metrics-file metrics.json
# Validate the environment before sending traffic.
sudo ./pugdns -doctor -interface eth0 -domains domains.txt -nameservers resolvers.txt
# Emit only a compact machine-readable final summary.
sudo ./pugdns -interface eth0 -domains domains.txt -nameservers resolvers.txt -quiet -summary json
# Resolve iteratively from root hints without a public resolver list.
sudo ./pugdns -resolve-mode iterative -interface eth0 -domains domains.txt -output results.jsonl -metrics-file metrics.json(Note: Running with sudo or appropriate capabilities (CAP_NET_ADMIN, CAP_NET_RAW, potentially CAP_SYS_ADMIN for memlock/BPF) is typically required for AF_XDP and eBPF operations.)
Iterative mode uses the AF_XDP/eBPF engine for UDP authority queries and ignores
configured resolver lists. It walks root/TLD/authoritative delegations directly,
caches zone cuts and NS addresses, parses referrals and in-bailiwick glue, uses
scored bounded authority fanout for each known zone cut, resolves no-glue NS
addresses in parallel, batches AF_XDP authority fanout with a shared response
channel, tracks sharded per-authority RTT/failure state, spreads equal-score
authority ties by query name, and stops at CNAME answers by default before
writing the same JSONL output format as recursive mode. Use -iterative-cname chase when the benchmark or workflow needs full CNAME target resolution. The
iterative authority timeout defaults to 250ms. Iterative tuning fields such
as authority fanout, hedge delay, referral thresholds, and timing attribution
are intentionally config-file settings instead of top-level CLI flags.
If you don’t want to build pugdns from source and just want to test it out, simply download the pre-compiled binary from our Releases page. It will be easier and faster.
If you really want to build from source, here's a rough guide on how to do so:
- Clone the repository:
git clone https://github.com/c3l3si4n/pugdns cd pugdns - Install Dependencies: Ensure you have Rust (>= 1.95) and Clang/LLVM (for eBPF compilation) installed. You may also need kernel headers (
linux-headers-$(uname -r)on Debian/Ubuntu).sudo apt install linux-headers-$(uname -r) llvm libbpf-dev clang pkg-config - Build:
This builds
./build.sh
pugdnsin release mode and copies the binary to./pugdns. - Run:
sudo ./pugdns [flags...]
- aya-rs/aya - eBPF library used for loading and interacting with BPF programs.
- netring - AF_XDP socket support for Rust.
- hickory-dns - DNS protocol parsing and message handling.
Feel free to open issues for bugs, feature requests, or questions! Contributions are welcome.