从零实现的 C++20 DNS 递归解析器——既能跑起来解析域名,又能在过程中真正理解 DNS 协议。
客户端 (dig / 浏览器)
│ UDP:53 / TCP:53
▼
┌─────────────────────────────────────┐
│ poll() 事件循环 (单线程收包) │
│ ├─ UDP: recvfrom → 线程池 │
│ └─ TCP: accept → handle → close │
└──────────────┬──────────────────────┘
│ 分发到 N 个工作线程
▼
┌─────────────────────────────────────┐
│ Resolver (每线程独立 CURL handle) │
│ ┌─ 1. 域名屏蔽 (BlockList) │
│ ├─ 2. hosts 覆盖 (HostsFile) │
│ ├─ 3. 缓存查询 (LRU + TTL) │
│ ├─ 4. 负缓存检查 │
│ ├─ 5. DoH 上游 (libcurl, HTTPS) │
│ └─ 6. 迭代解析 (根→TLD→权威, 回落) │
└─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ 共享 DnsCache (shared_mutex) │
│ Metrics HTTP :9191 (Prometheus) │
│ RateLimiter (令牌桶, 每 IP) │
│ SIGHUP 热加载配置 │
└─────────────────────────────────────┘
# 构建
cmake -S . -B build && cmake --build build
# 单元测试(54 个测试,185 个断言)
./build/test_message
# 启动服务器(端口 5300)
./build/dns_server dns_server.conf
# 测试查询
./scripts/dns_query.py example.com
./scripts/dns_query.py -a # 预设域名列表
# 一键集成测试
./scripts/test_all.shport 5300
hosts_file /etc/hosts
upstream_dot 8.8.8.8 # DNS-over-HTTPS 上游
upstream_dot 1.1.1.1
block *.doubleclick.net # 域名屏蔽
rate_limit 100 # 限速 (qps/IP)
rate_burst 200 # 突发令牌| 功能 | 说明 |
|---|---|
| DNS 报文编解码 | Header/Question/RR 完整 encode/decode,含域名压缩指针 |
| UDP + TCP 双栈 | poll() 统一事件循环,TCP 带 2 字节长度前缀 |
| DNS-over-HTTPS | libcurl + 系统 CA 证书验证,绕过 DNS 劫持 |
| 迭代解析 | 13 个根服务器 → TLD → 权威,含 CNAME 追踪 |
| LRU + TTL 缓存 | 惰性过期 + 双重淘汰,多线程安全 (shared_mutex) |
| 负面缓存 | NXDOMAIN 结果按 SOA MINIMUM 缓存 |
| hosts 覆盖 | 标准 /etc/hosts 格式,IPv4 查询第一优先 |
| 域名屏蔽 | 精确匹配 + *.通配符,返回 0.0.0.0 |
| 线程池 | 可配置工作线程数,轮转分摊 CURL handle |
| 限速 | 令牌桶算法,每 IP 独立桶,超限静默丢弃 |
| Metrics | Prometheus 格式,/metrics + /health,端口 9191 |
| 热加载 | SIGHUP 重读配置,hosts/限速即时更新 |
| 优雅降级 | DoH 全挂自动回落迭代解析 |
| EDNS0 | OPT 伪记录,声明 4096 字节 UDP 载荷 |
| systemd | dns_server.service,CAP_NET_BIND_SERVICE 免 root |
$ ./scripts/dns_query.py example.com github.com google.com
example.com NOERROR A=104.20.23.154 (ttl=300s)
github.com NOERROR A=20.205.243.166 (ttl=60s)
google.com NOERROR A=142.250.204.46 (ttl=74s)$ curl http://127.0.0.1:9191/metrics
# HELP dns_udp_queries_total Total UDP queries received
# TYPE dns_udp_queries_total counter
dns_udp_queries_total 42
# HELP dns_cache_hits_total Cache hits
# TYPE dns_cache_hits_total counter
dns_cache_hits_total 28
# HELP dns_local_hit_rate Local hit rate (percent)
# TYPE dns_local_hit_rate gauge
dns_local_hit_rate 66.7sudo cp build/dns_server /usr/local/bin/
sudo cp dns_server.conf /etc/
sudo cp dns_server.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now dns_server- C++20 (GCC 16+ 或 Clang 19+)
- CMake ≥ 3.20
- libcurl ≥ 8.0
- Linux, POSIX socket
├── include/dns/ # 头文件
│ ├── message.hpp # DNS 报文类型
│ ├── resolver.hpp # 递归解析器
│ ├── cache.hpp # LRU+TTL 缓存
│ ├── hosts.hpp # hosts 文件 + 屏蔽
│ ├── dot_upstream.hpp # DoH 上游
│ ├── config.hpp # 配置解析
│ ├── stats.hpp # 统计
│ ├── thread_pool.hpp # 线程池
│ ├── rate_limiter.hpp # 令牌桶限速
│ └── metrics_server.hpp # Prometheus metrics
├── src/dns/ # 实现
├── src/main.cpp # 入口
├── tests/ # 54 个测试用例
├── scripts/ # 测试脚本
│ ├── test_all.sh # 一键集成测试
│ ├── dns_query.py # DNS 查询客户端
│ └── ...
└── dns_server.conf # 默认配置
MIT