指定了公共 DNS,为什么仍会收到代答?
指定了公共 DNS,为什么仍会收到代答?
在终端执行 nslookup www.youtube.com 8.8.8.8,看到 Server: 8.8.8.8,很容易以为答案来自 Google。但这个字段只说明查询目标,以及被接受的应答声称的来源。明文 UDP/53 没有认证这个来源。
2026 年 9 月 27 日,我在 iStoreOS 软路由上做了一次 WAN 抓包实验。最有价值的结果不是“六家 DNS 的答案不同”,而是:向一个不应提供公共 DNS 的文档保留地址查询,仍收到了匹配的 DNS 应答。
先确认查询真的走出了 WAN
家里的软路由运行 PassWall2。本机执行 nslookup 时,第一轮有正常结果,WAN 的 eth0 却抓不到对应 UDP/53。查询可能在出 WAN 前已被 TProxy 接管,因此这轮不能代表直连链路。
软路由本机查询 → OUTPUT/TProxy → eth0(WAN 抓包点)
↓
光猫与上游链路
↓
指定 DNS 目标后续仅对测试目标的本机 UDP/53 临时设置例外,利用现场已有的 mark 0xff 返回规则绕过 TProxy。批量查询后规则计数为 112,WAN 也抓到了请求。这是本环境已有规则的语义,不是通用 OpenWrt 配置,不适合照抄到其他设备。
显式指定 DNS 地址、甚至 ip route get 显示走 eth0,都不能替代出站报文证据。
112 次查询如何组成
每个目标查询 16 个域名,只取 A 记录。公共 DNS 共 96 次,负对照另有 16 次。
| 目标 | 与 Google DoH 对照无交集 | 有交集 |
|---|---|---|
Google 8.8.8.8 | 15/16 | 1/16 |
Cloudflare 1.1.1.1 | 15/16 | 1/16 |
Quad9 9.9.9.9 | 14/16 | 2/16 |
OpenDNS 208.67.222.222 | 14/16 | 2/16 |
AdGuard 94.140.14.14 | 14/16 | 2/16 |
Control D 76.76.2.0 | 14/16 | 2/16 |
| 合计 | 86/96 | 10/96 |
这里的“不同”仅表示 A 地址集合没有交集。CDN、缓存、地理位置和查询时间都能造成合法差异,86/96 不能换算成污染率。DoH 对照也只使用了 Google 一个解析器,并非所有服务商各自的基线。
保留地址负对照才是关键
负对照目标为 203.0.113.53。它属于 RFC 5737 的 TEST-NET-3 文档地址段,不应作为公共递归 DNS 使用。RFC 5737
16 次查询全部收到应答。下面是其中一组抓包的简化展示,客户端端口已省略:
192.168.1.4:<port> > 203.0.113.53:53 A? www.youtube.com.
203.0.113.53:53 > 192.168.1.4:<port> www.youtube.com. A 31.13.92.37事务 ID、问题名、目标端口匹配,这组往返约 8.51 ms。全部 pcap 共 224 帧,即 112 个问题包、112 个应答包,逐事务配对无遗漏。
这支持链路上存在透明代答或源地址伪造。它不能证明应答“抢在真实服务器之前”:抓包没有看到目标服务器一侧,也不能仅凭 WAN 口确定是光猫、接入设备还是更上游节点。
应答 TTL 都为 64,中位响应约 8.23 ms。这些可作为线索,不能用于定位责任设备。出站包的 bad udp cksum 还可能来自 TX checksum offload,不能当成污染证据。
用事务配对区分“收到回答”和“谁回答了”
DNS ID 只有 16 位,不能单独作为全局唯一键。实际配对应把客户端/目标 IP、UDP 端口、ID、问题名、类型以及时间窗口一起纳入:多条重复查询、重试或并发查询都可能复用其中一项。对于返回包,再核对 QR 标志、RCODE、回答类型、问题区和截断标志,而不是只提取一个 A 地址。
在分析机上保留这些字段,命令为取证参考,不会主动查询网络:
tshark -r dns-direct.pcap -Y dns -T fields \
-E header=y -E separator=, -E quote=d \
-e frame.number -e frame.time_epoch \
-e ip.src -e ip.dst -e udp.srcport -e udp.dstport \
-e dns.id -e dns.flags.response -e dns.flags.rcode \
-e dns.qry.name -e dns.qry.type -e dns.aA 集合相同、相交或完全不同是三种情况,不能只用显示顺序比较。CNAME 链、空回答、超时与错误 RCODE 也应单列,不能把它们都记作“不同”。同一事务多个应答尤其值得保留:一个客户端最终显示一个结果,不代表链路只回了一份包。这次 112 对记录没有给出第二份真实服务器应答,所以不能证明存在一场抢先到达的竞赛。
| 观察 | 下一步核对 | 结论边界 |
|---|---|---|
| 客户端有答案,WAN 没请求 | 本机 OUTPUT、代理与抓包位置 | 尚不能描述直连上游 |
| WAN 有请求且返回匹配包 | 事务、端口、问题区与时间 | 已观察到该边界的往返 |
| 文档地址目标也返回答案 | 全部保留地址事务、路由与本机规则 | 支持透明代答/源伪造;责任节点仍未知 |
| 真实目标与 DoH A 集合不同 | 同时查询、多解析器与完整响应 | 合法解析差异尚未排除 |
为下一轮实验保存可独立复算的对照
改用 DoH/DoT 可以认证到所信任解析服务的传输,但不能证明该服务的每个解析结果都是“唯一正确答案”。下一轮应保存 HTTPS 请求参数、状态码、响应 JSON、采集时间、所用解析器与链路出口,并在接近的时间窗口采集明文查询。HTTPS 成功与 JSON 内 DNS Status 成功也是不同判断。
本次历史缺少 DoH JSON,所以今天重查域名只会得到今天的解析结果。保留原始 pcap、逐条表和分析命令,才能让后来者重算 112 次配对;无法重算的历史比较应明确标记,而不是悄悄换成今天的基线。若扩大样本,先解决时间配对与数据留存,重复同一个缺失证据的实验并不会自动增强归因。
小样本复现与配对
在确认接口、路由及本机代理规则后,先抓一个真实目标和一个负对照。下面命令用于路由器本机,查询在另一会话执行:
tcpdump -i eth0 -nn -s0 -U -w /tmp/dns-direct.pcap \
'udp port 53 and (host 8.8.8.8 or host 203.0.113.53)'
nslookup -type=a www.youtube.com 8.8.8.8
nslookup -type=a www.youtube.com 203.0.113.53若 WAN 没有请求,先解决本机拦截或抓包位置问题。不要把 203.0.113.53 配成系统 DNS,也不要为了实验关闭整条生产代理。
在分析电脑上先查看应答,再按事务 ID、问题名、客户端 UDP 端口关联请求和应答:
tshark -r dns-direct.pcap -Y 'dns.flags.response == 1' \
-T fields -e ip.src -e ip.ttl -e dns.id -e dns.qry.name -e dns.a实验结束后移除准确匹配的临时规则,退出抓包进程,并确认原代理与 DNS 服务仍正常。
证据与局限
逐项解析结果 TSV 保留了 112 次查询的结果。原始 pcap 在本地归档中,SHA-256 为:
d8786dc025569fb5cb427bd8ac25abb90ca6499498a7a450fc8905fae271e9eapcap 没有直接公开:它含原始网络报文,本文提供复现方法和经检查的结果表。历史 DoH JSON 没有独立归档,所以不能用今天的查询精确复算当日 86/96;保留地址应答则可以从原始 pcap 独立验证。
这次实验改变了我的排障顺序:先证明包去了哪里,再判断应答能说明什么。下一步若要定位代答点,需要多个网络边界的同步取证,而不是继续比较 nslookup 的 Server 字段。
参考资料
本文日期采用主 KB 首次 Git 提交日期 2026-09-27(UTC+8),提交 907fcee。操作与评测时间在正文单独注明;写作期间未连接或修改生产设备。
