DNS 查询和响应以 12 字节标头开头,后面依次是 Question、Answer、Authority 和 Additional 区段。所有多八位组整数域均使用网络字节序。RFC 1035 定义了 基本格式;EDNS、DNSSEC 以及后续扩展在不改变基本标头的情况下增加记录和标头 语义。
标头 #
| 字段 | 位数 | 用途 |
|---|---|---|
| ID | 16 | 由客户端选择并在响应中回显的标识符 |
| QR | 1 | 查询(0)或响应(1) |
| Opcode | 4 | 操作;QUERY 为零 |
| AA | 1 | 响应中的权威回答指示 |
| TC | 1 | 消息已被截断 |
| RD | 1 | 客户端请求递归 |
| RA | 1 | 服务器提供递归 |
| Z | 3 | 保留的基本位;扩展使用 AD 和 CD |
| RCODE | 4 | 基本响应代码 |
| QDCOUNT | 16 | Question 数量 |
| ANCOUNT | 16 | Answer 记录数量 |
| NSCOUNT | 16 | Authority 记录数量 |
| ARCOUNT | 16 | Additional 记录数量 |
RD 请求接收服务器执行递归;RA 声明服务器能够执行递归。AA 表示响应
服务器对该回答具有权威性,并不表示消息中的每条记录都具有权威性。TC 告知
客户端,当截断的响应不足以完成处理时,必须通过 TCP 或其他受支持的传输方式
重试。
名称和 Question #
域名由一系列标签组成。每个普通标签以一个长度八位组开头,长度范围为 0 到 63; 长度为零的根标签终止该名称:
03 www 07 example 03 com 00完整的编码名称(包括长度八位组和根标签)限制为 255 个八位组。Question 包含
QNAME、16 位的 QTYPE 和 16 位的 QCLASS;Internet 类别(IN)的值为 1。
为避免重复的后缀,名称可以使用压缩指针。指针的前两位为 11,其余 14 位是
相对于消息起始位置的偏移量。指针只能指向更早的位置。由于 DNS 消息是不可信的
输入,解析器必须限制指针深度、验证偏移量并检测循环。
Resource Record #
Answer、Authority 和 Additional 区段承载 Resource Record:
NAME | TYPE | CLASS | TTL | RDLENGTH | RDATATTL 编码在 32 位字段中,并依据 RFC 2181 作为非负的缓存生存期处理,其有效
范围为 0 到 \(2^{31}-1\)。RDLENGTH 界定特定类型 RDATA 字节的范围。RRset
是具有相同 owner name、class 和 type 的记录集合;DNSSEC 为 RRset 签名,而不是
为单条记录签名。
常见的 RDATA 包括四字节 IPv4 地址(A)、16 字节 IPv6 地址(AAAA)、域名
(NS、CNAME、PTR),以及结构化值,例如 MX 的 preference 加 exchange
name,或 SOA 的计时器。RDATA 中的名称也可以使用压缩,但前提是适用的 RFC
允许这样做。
EDNS 使用 Additional 区段中的 OPT pseudo-record 来通告 UDP 负载大小、扩展的
RCODE 位、标志和选项。协商 EDNS 后,服务器不得假设 UDP DNS 受限于 512 字节;
但客户端仍必须处理截断并重试。
安全解析 #
将接收到的数据报或流帧视为唯一有效的字节范围。在读取每个定长字段、标签、指针 或 RDATA 值之前,验证整个字段仍位于该范围内。区段计数是不可信的;如果声明的 记录数量无法容纳,则停止解析。
名称解压需要两个位置:调用方下一个尚未读取的偏移量,以及用于重建名称的指针 跟随偏移量。统计指针跳转次数或跟踪已访问的偏移量以拒绝循环,拒绝保留的标签 前缀,强制执行 63 个八位组的标签限制和 255 个八位组的展开名称限制,并且绝不 解析消息范围之外的指针目标。
对于 TCP 和 TLS 传输,两个八位组的长度前缀为每条 DNS 消息提供帧边界,但它不是
DNS 标头的一部分。解析前必须准确读取指定长度的字节。对于 OPT 记录,将其 TTL
字段中的扩展 RCODE 与基本标头 RCODE 合并,并确保每个选项同时位于 RDLENGTH
和消息范围之内。
评论
在 GitHub 上阅读或评论(发表评论需要登录)