报文的字节:12 字节头与名字压缩
前一页把解析讲成「问谁、得到哪种应答」。这一页下沉一层:那些应答在线路上是什么样的字节。
报文的骨架自 1987 年的 RFC 1035 §4 起没有变过:12 字节定长头,加四个变长段(question、answer、authority、additional)。
1 · 头部的 16 位控制字段
12 字节头里有一个 16 位的 flag 字段,它是整套协议的控制面。其中七位有名字:QR(查询还是应答)、AA(应答者对该名字有权威)、TC(应答被截断)、RD(请对方代为递归)、RA(本服务器提供递归服务)、AD 与 CD(DNSSEC 用,见第 7 讲)。剩下的是 opcode(4 位)、rcode(4 位)与 1 位保留位 Z(恒为 0)。
图 1-1 能看清一件事:一次委任应答与一次权威应答在报文层面的区别极小——AA 这一位,加上 ANCOUNT 与 NSCOUNT 两个计数。没有专门的「referral」报文类型;「该去问别人」这个语义完全由「AA=0 且 ANSWER 空而 AUTHORITY 里有 NS」这个组合表达。
另一件是 QNAME 的形状:线路上不出现点。每个 label 前置一个长度字节,末尾一个 0 字节表示根。所以字节数 = 各 label 字节数之和 + label 数 + 1,mail.vega.dev. 占 15 字节。255 字节的名字上限管的是这个数,不是屏幕上的字符数。
2 · 名字压缩
DNS 应答里同一个后缀会反复出现:一次委任应答的每条 NS、每条 glue 都带着同一个 zone 名。RFC 1035 §4.1.4 为此定义了压缩指针——一个长度字节若高两位为 11,它与下一字节合成 14 位偏移,指向报文中先前出现过的同名后缀。
14 位这个宽度带来一条至今仍在咬人的限制:可寻址范围只到 16383 字节,超出该偏移的名字无法被指向。在 DNSSEC 把应答撑大之后,这个上限偶尔会真的碰到。
压缩还有一条容易被忽略的规则:只有 RFC 1035 本身定义的类型允许在 RDATA 内使用压缩。之后新增的类型(SRV、HTTPS 等)一律不许。理由是中间设备——不认识某个类型的转发器如果重写了报文,RDATA 里那个指针就会指向错误的位置。
警示 · 压缩指针给解码器带来一类必须处理的输入:环。两个指针可以互指,一串合法的、逐次前进的指针也可以把解码器拖到二次开销。写本页的解码器时,最初的防护是「指针只许指向更小的偏移」——看起来合理,因为编码器写出的指针总是向后指。但这条规则会把一类合法的报文误判:跳回一个较早的名字之后继续向前读,其后遇到的指针目标完全可能大于第一次跳转的目标。改成「每个指针目标只许访问一次」加「名字总长不超过 255 字节」两道闸才既终止又不误判——前者保证终止(偏移是有限集),后者拦不带指针、纯靠 label 堆出来的超长名。
3 · 512 字节与 EDNS(0)
RFC 1035 给 UDP 报文定了 512 字节上限。超出就置 TC 位,提问方改用 TCP 重问一次——多一个往返,还多一次三次握手。
这个上限在 1987 年是保守而合理的(当时的路径 MTU 假设),但它挡住了后来的一切扩展:DNSSEC 的签名、NSEC 的位图、以及带上 AAAA glue 之后的根 NS 应答都装不下(实测今日 dig +tcp . NS @198.41.0.4 为 811 字节,核对于 2026-08)。
EDNS(0)(RFC 6891)的解法是一个伪记录:在 additional 段放一条 type 为 OPT 的记录,借它的 CLASS 字段位置声明自己能接收的 UDP 载荷大小,借 TTL 字段位置放扩展 rcode 与 flag 位(其中 DO 位表示「我要 DNSSEC 数据」)。
这个设计的巧处在于它没有改动 12 字节头——不支持 EDNS 的老实现看到 additional 段里一条不认识的记录,按 RFC 3597 当不透明数据处理即可,不会崩。整个 DNS 的可扩展性都建立在「不认识也要能存能传」这一条上。
注 · OPT 与 TSIG、TKEY 同属只存在于报文、不出现在任何 zone 的 meta-RR,其中只有它承担协议扩展。它的 owner 恒为根、TTL 字段不是 TTL、CLASS 字段不是 class——三个字段全部被重新赋义。这在 DNS 里是孤例,代价是它无法被缓存也无法被签名,好处是不必动头部。
声明多大的载荷曾有过一段反复。早期常见 4096,后来发现大 UDP 报文在 IPv6 分片与部分中间设备上丢包率显著,DNS Flag Day 2020 之后的共识是 1232(IPv6 最小 MTU 1280 减去 IPv6 与 UDP 头)。这不是协议规定,而是一次跨实现的运维协调。
4 · 参考文献
- Mockapetris, P. (1987). Domain Names — Implementation and Specification (RFC 1035). IETF. §4 是报文格式,§4.1.4 是名字压缩,§2.3.4 是 512 / 255 / 63 三条上限。
- Damas, J., Graff, M., & Vixie, P. (2013). Extension Mechanisms for DNS (EDNS(0)) (RFC 6891). IETF.
- Gustafsson, A. (2003). Handling of Unknown DNS Resource Record (RR) Types (RFC 3597). IETF.
- DNS Flag Day contributors. (2020). DNS Flag Day 2020. https://dnsflagday.net/2020/(1232 这个数字的来历。)