摘要

  • DNS 的 TC 位表示消息超过当前运输信道允许的长度。RFC 2181 后来划清语义:只有响应所必需的 RRset 无法完整放入时才应截断;客户端收到 TC 后应忽略这份响应,不能把露出来的记录当作全集。
  • TCP 曾承担更大响应的重试,却没有永远停留在例外位置。RFC 7766 要求通用 DNS 同时支持 UDP 与 TCP,也允许解析器直接选择 TCP;RFC 9210 进一步把 TCP 容量、放行和监测变成日常运维责任。

512 字节装不下的,不只是数据

RFC 1035 在 1987 年规定 DNS 可经 UDP 或 TCP 使用 53 端口。普通查询偏爱 UDP,因为无需先建连接,服务器与解析器承担的状态很少;代价是 UDP 消息限于 512 字节,不计 IP 与 UDP 头部。

超过上限的响应会被截断,头部 TC 位设为 1。若只看字节,这像一次简单的空间不足;若从证据看,问题严重得多。一个名字下若有五条同类记录,而包里只留下三条,接收方不能据此断言“答案就是三条”。运输容量不应改写命名空间的事实。

TC 因而不是错误码,也不是不存在证明。它只承认这条信道没能交付完整消息。服务器仍掌握答案,解析器得到的是对当前容器的否定判断。

RRset 是不能被随意切开的证据单位

1997 年的 RFC 2181 把边界说得更严谨:拥有相同所有者名、类别与类型的资源记录构成一个 RRset。查询这组数据时,整组才是有意义的回答单位。

只有响应必需的 RRset 无法全部装入,才应设置 TC。若只是 Additional 区里本可顺手附带的数据放不下,服务器可以把那整个额外 RRset 省略,并保持 TC 为 0;需要它的客户端可另发查询。

一旦 TC 为 1,处理方式完全不同。客户端应忽略该响应,用能容纳更大回复的机制重新查询,例如 TCP。包里即使残留部分 RR,也只是截断的物理痕迹,不能进入缓存成为“可用的一半”。

这条规则阻断了一条危险捷径:接收者不能把新收到的碎片与旧缓存拼接,也不能依据“看起来够用”挑选记录。答案不完整,就没有资格代表本次问题的事实。

一个比特只报告边界,不代替解析器决策

TC 告诉解析器“这次运输不够大”,并不替它发起下一条连接。典型流程是 UDP 查询收到 TC=1 后,再用 TCP 向同一服务器重复问题,或按策略尝试其他合适服务器。

TCP 用长度字段框定 DNS 消息,不受单个 UDP 数据报容量所限;同时也引入握手、连接表、并发与空闲超时。成本落在实际使用这些资源的节点上:解析器选择重试、复用或换服务器,服务端决定连接准入和回收策略。

这种安排很接近可靠制度的最小形态。低成本信道可以先工作,也可以坦白能力到此为止;消费证据的一方必须在知道“不完整”后主动取得完整版本。任何中间节点都没有因为搬运困难而改写答案的权力。

EDNS 扩大预算,却没有签发路径保证书

RFC 6891 让请求方在 EDNS(0) 中声明自己能接收的 UDP 负载大小,也给 DNS 留出扩展空间。512 字节不再是所有交换的固定天花板,更多答案可以留在无连接的数据报路径上。

声明可收多大,并不等于沿途每个链路、隧道、防火墙与主机都会交付同样大的数据报。超过路径 MTU 后的 IP 分片可能丢失、被过滤或被利用。DNSSEC 的签名与否定证明也会扩大响应,IPv6 与新记录用途继续增加大答案出现的机会。

EDNS 与 TC 因而回答不同问题。前者是请求方提出的容量预算;后者是响应方承认实际答案没有装进可用预算。预算提高不能消除溢出信号,也不能让 TCP 失去存在理由。

更棘手的是,大 UDP 包若在途中无声消失,解析器甚至收不到 TC。把 EDNS 数值误当成路径事实,会让超时与权威否定混在一起;把 EDNS 当作 TCP 的替代品,则会抹掉大答案的恢复路径。

TCP 从“偶尔备用”成为正式成员

很长时间里,人们把 DNS over TCP 简化成“区域传送或截断后才用”。这种叙述让防火墙和实现者把 TCP/53 当作可选负担:协议一边用 TC 要求更大信道,网络另一边却把那条路封死。

2016 年发布的 RFC 7766 纠正了这个错位。通用权威服务器、递归服务器、转发器与 stub resolver 都必须同时支持 UDP 和 TCP。旧有“非区域传送必须先发 UDP”的要求也被放宽;解析器可以根据本地运维理由先用 TCP,已有连接则应复用。

所以,写成“TC 永远命令 TCP 重试”仍不够准确。TC 常常触发 TCP,但 TCP 已是可直接选择的运输方式;后来的 DNS 加密运输也让可选路径更多。持久不变的不是某种协议名称,而是不完整响应不能被消费成完整事实。

可靠信道要靠连接纪律才能扩展

若每个问题都新建并立即关闭 TCP,握手延迟和状态成本会迅速放大。RFC 7766 因此鼓励复用连接与流水线:客户端不必等上一条回复才发送下一条问题,服务器并发处理,结果可以乱序返回。

这也改变了实现与观测方式。客户端必须按 DNS 标识和问题匹配响应,不能把流中的先后顺序当成对应关系。服务器必须按 DNS 的长度框架重组字节,不能假设一个 TCP segment 就是一条完整消息。

服务端仍可限制单个客户或网段的并发连接,客户端也应减少对同一服务器的连接数。空闲会话需要按策略关闭;超时应在收到完整 DNS 消息后重置,不能让缓慢滴入的字节无限占用资源。

标准没有假装 TCP 免费,而是把互操作义务和资源控制放在同一张账上。

运维必须看见 DNS 的两种运输

RFC 9210 把实现能力推进为运维合同:解析器与服务器必须服务 UDP、TCP,网络运营者通常也必须放行两者。过滤 TCP DNS 会毁掉大响应的恢复路线,其危害高于省下一类流量处理的便利。

这并不取消容量限制。服务器可以保护自身资源,却不能只因为某个查询“本可在另一种运输成功”就拒绝它。限制应针对成本与滥用,而非把某个运输降格成不可信渠道。

日志与监测也要覆盖 TCP。只看单个包会错过跨 segment 的 DNS 消息;忽略连接复用、流水线与乱序响应,会同时漏掉正常流量和攻击。可靠信道若成为可观测性的盲区,就无法证明它真的救回了截断答案。

分片脆弱性让旧信号继续有用

2025 年的 RFC 9715 建议 DNS over UDP 避免 IP 分片:响应大小应服从请求方声明、接口 MTU、运营者已知网络 MTU 与建议的 1400 字节上限中的最小值。分片不只容易丢失,也会扩大缓存投毒风险。

文档没有改变 TC 的因果关系。若响应方知道包太大,可以缩小响应或设置 TC;若分片在途中消失,请求方最终应尝试替代运输。这不是保证 TCP 永不失败——端口过滤、服务过载与路径故障都可能存在——而是拒绝用脆弱数据报的沉默替代明确恢复。

TC 从未证明什么

TC=1 不证明 DNSSEC 验证失败,不证明域名不存在,也不证明审查、攻击、所有权或配置错误。省略非必需的附加信息也不必然触发 TC。一个中间代理更不能利用它自行删改记录。

它证明的范围非常窄:这份响应在当前信道上没有完整交付。正因范围窄,它才可靠。服务器不用猜客户端意图,解析器不用猜缺失字节含义,各方只需守住“半份 RRset 不是答案”。

互联网没有要求第一只信封容纳一切。它要求信封装不下时,必须诚实承认。

来源与证据边界

TC 与原始 UDP 上限来自 RFC 1035,RRset 完整性与客户端行为来自 RFC 2181,EDNS 容量声明来自 RFC 6891,TCP 的实现和运维责任来自 RFC 7766 与 RFC 9210,分片规避来自 RFC 9715。这些规范不能证明当前全球 TC 比例、TCP 放行率、实现默认值或解析成功率。