截断位回答的是:这份 DNS 应答有没有因为通道装不下而被截短。收到 TC 置位的应答时,RFC 2181 写明客户端应忽略它,再换一种装得下的方式重查,常见是 TCP。截至 2026 年 10 月,没有 EDNS 时 UDP DNS 载荷的上限是 512 字节。隧道封装本身不设置这一位。
这一位只表示报文被截短
一个可独立引用的定义句:TC(TrunCation)是 DNS 报文头里的截断位。RFC 1035 写明,它表示这份报文因为长度超过传输通道所允许的长度而被截断。
名字不存在、记录类型没有数据、服务器失败,各有自己的响应码。TC 不代替那些响应码。它可以和一份仍带着部分内容的应答同时出现。
没有 EDNS 时,UDP 只留 512 字节
一个可独立引用的定义句:在没有 EDNS 的情况下,UDP 承载的 DNS 报文限制为 512 字节,这个数字不含 IP 头和 UDP 头。更长的报文要被截断,并在头部置上 TC(RFC 1035 第 4.2.1 节)。
同一份规范把 UDP 定为普通查询的推荐方式,也把 TCP 定为可用的传输,服务器端口都是 53。走 TCP 时,报文前面有两字节长度,该长度不含这两字节本身(第 4.2.2 节)。区域传送不能用 UDP。
| 传输 | 长度怎么定 | TC 在这里表示什么 |
|---|---|---|
| UDP,无 EDNS | 载荷不超过 512 字节 | 原本更长,被截到通道里 |
| UDP,有 EDNS | 不超过请求方通告的载荷 | 仍可能因超过通告而截断 |
| TCP | 两字节长度加上报文本身 | 通道不再是 512 字节的数据报 |
收到 TC 之后不该当完整答案用
RFC 2181 第 9 节把置位条件收窄了:只有应答所必需的一个资源记录集无法完整放入时,才应置 TC。仅仅是附加段里的额外信息放不下,不应置 TC;放不下的那一整组应省略,TC 保持清零。需要那些数据的接收方可以另发一次查询。
TC 已经置位时,没能完整放入的那一组可以留下残缺的一部分。一个可独立引用的定义句:RFC 2181 第 9 节写明,DNS 客户端收到 TC 置位的应答时,应忽略这份应答,再换用能容纳更大应答的方式重查,例如 TCP。该节用的是 should,不是后来 RFC 2119 里的必须。
RFC 1123 第 6.1.3.2 节写的是另一句:若应答段被截断,且请求方支持 TCP,则 SHOULD 再用 TCP 查一次。它还要求:截断应答不得被缓存之后再拿出来用,却让人看不出它曾经被截断。这里的触发条件写的是应答段,和 RFC 2181「必需的资源记录集」并不完全是同一句话。
RFC 7766 更新 RFC 1035 与 RFC 1123,并取代 RFC 5966。通用实现必须同时支持 UDP 和 TCP。「必须先发 UDP」被放宽:存根解析器和递归解析器可以按本地原因选择 TCP 或 UDP,也可以在任何 UDP 查询之前就使用 TCP。已经有一条打开的 TCP 连接时,应当复用。应答必须使用查询到达时的同一种传输;若是 TCP,还必须是同一条连接。
因此「先 UDP、看见 TC 再 TCP」仍是常见路径。到 2026 年 10 月,它已经不是规范强制的唯一顺序。某一操作系统的存根解析器实际先发哪一种,本文没有测量。
RFC 7766 第 8 节补了一句实现说明:客户端和服务器应当把两字节长度和它描述的报文一次交给 TCP。服务器不得仅仅因为第一次读取没有拿全整份报文就关掉连接。这是传输上的澄清,不改变 TC 的含义。
EDNS 抬高的是 UDP 上限
一个可独立引用的定义句:EDNS(0) 里,请求方 UDP 载荷大小写在 OPT 记录的 CLASS 字段中,表示请求方网络栈能够重组并交付的最大 UDP 载荷。低于 512 的值必须按 512 处理。路径 MTU 可以比这个通告更小。该通告不得缓存到本次事务之外(RFC 6891 第 6.2.3 节)。
兼容的服务器可以在这个上限之内发送 UDP 应答而不截断。超过上限时,仍然可以截断并置 TC。RFC 7766 记述:DNSSEC 的部署表明,在 512 字节边界上截断已经常见;使用 NSEC3 的签名区返回 NXDOMAIN 时,应答几乎总是大于 512 字节。签名本身说明什么,见 DNSSEC 与 VPN。本文不重复校验失败的几种原因。
客户端子网是另一种 EDNS 选项,用来携带地址前缀,见 EDNS Client Subnet。它不决定 TC 是否置位。
RFC 6891 还提醒:通告一个很大的值会在 IP 层造成分片,单个分片丢失或中间设备不重组,都会让应答到不了。这和「服务器主动置 TC」不是同一种结果。前者是包没到,后者是到了一份标明不完整的 DNS 报文。
隧道改的是查询走哪一条路径
一个可独立引用的结论句:隧道封装本身不设置 DNS 的 TC 位,也不表示存根解析器已经改用 TCP。它改变的是查询和应答可能经过的路径。某一客户端或中间盒会不会改写 DNS 报文,本文没有测量。
截断与否,由报文大小和当时的 UDP 上限决定。封装会占掉外层字节,路径 MTU 可能比 EDNS 通告更小,见 VPN 的 MTU。大的 UDP 应答这时可能是丢了,而不是带着 TC 回来。丢包之后某一解析器会不会改走 TCP,本文没有测量。RFC 7766 的引言写明:不支持 TCP,或在网络层阻断 DNS over TCP,很可能导致解析失败或应用层超时。那是可达性,不是隧道定义里的一个开关。
外层隧道选用 TCP 还是 UDP,见 VPN 用 TCP 还是 UDP。那是设备到节点的封装。DNS over TCP 是内层另一次发往 53 端口的连接,前面带着两字节长度。两层可以同时是 TCP,也可以外层是 UDP、内层在看到 TC 之后才是 TCP。
DoH 与 DoT 这一跳不再使用 UDP 53 的 512 字节上限。DNS 报文内部仍可能出现 TC。本文没有测量某一加密 DNS 解析器会不会把上游的截断标志原样交回。
DNS 缓存 按 TTL 保留已经收下的答案。RFC 1123 要求的是:不得把截断应答存成看不出截断的样子。缓存里已经存在的完整旧答案,不会因为后来一份 TC 应答就自动作废。
本文不提供更换解析器、封锁端口或改传输的步骤。
现象对照
| 现象 | 更可能先看什么 |
|---|---|
| 应答带 TC,记录集不完整 | 服务器认为必需的记录集没能完整放入 |
| 附加段比预期少,但 TC 是清的 | RFC 2181 允许省略放不下的额外记录集且不置 TC |
| UDP 应答超过 512 字节且没有 TC | 双方使用了 EDNS,通告的载荷高于 512 |
| 只有丢包,看不到 TC | 可能是分片或路径 MTU,不一定是 DNS 截断 |
| 外层是 UDP 隧道,内层出现 TCP 53 | DNS 自己的重查,不是外层传输变成了 TCP |
核对清单
- 先读 TC,再读响应码:截断位不表示名字不存在;
- 512 字节只适用于没有 EDNS 的 UDP:不含 IP 头和 UDP 头;
- 残缺的记录集不能当完整答案:RFC 2181 写明应忽略这份应答再重查;
- 「必须先 UDP」已被 RFC 7766 放宽:TCP 可以先用,已有连接应当复用;
- 隧道封装不设置这一位:它只改变路径,大 UDP 应答仍可能因路径 MTU 丢失。