DNS查询的本质是客户端向DNS服务器发送符合协议规范的二进制消息,服务器解析该消息后返回对应的解析结果,整个交互过程完全基于二进制格式的报文传输,理解其消息结构是掌握DNS协议底层逻辑的核心。

DNS二进制消息的整体结构
DNS协议的二进制消息整体分为四个部分,按顺序依次为头部区、问题区、回答区、权威区和附加区,其中回答区、权威区、附加区在查询请求中通常不存在,仅在响应报文中出现。所有字段均采用网络字节序(大端序)进行编码。
| 区域名称 | 是否必选 | 出现场景 |
|---|---|---|
| 头部区 | 是 | 请求、响应均存在 |
| 问题区 | 是 | 请求、响应均存在 |
| 回答区 | 否 | 仅响应存在 |
| 权威区 | 否 | 仅响应存在 |
| 附加区 | 否 | 仅响应存在 |
头部区字段解析
头部区固定占12字节,每个字段的长度和含义都是固定的,是DNS消息解析的第一个入口,所有后续的解析逻辑都依赖头部区的字段信息。
头部区字段明细
- 事务ID:占2字节,由客户端生成,用于匹配请求和对应的响应报文,保证交互的一致性。
- 标志位:占2字节,包含QR、Opcode、AA、TC、RD、RA、Z、RCODE等子字段,用来标识报文类型、操作码、响应状态等信息。
- 问题数:占2字节,表示问题区包含的查询条目数量。
- 回答数:占2字节,表示回答区包含的资源记录数量。
- 权威数:占2字节,表示权威区包含的资源记录数量。
- 附加数:占2字节,表示附加区包含的资源记录数量。
问题区结构解析
问题区紧跟在头部区之后,由多个问题条目组成,每个问题条目对应一个DNS查询请求,结构包含查询域名、查询类型、查询类三个部分。
查询域名采用标签序列编码,每个标签前加1字节表示标签长度,域名末尾以0字节结束。例如查询域名www.ipipp.com,编码后结构为3www5ipipp3com0。
常见查询类型
- A:查询域名对应的IPv4地址
- AAAA:查询域名对应的IPv6地址
- CNAME:查询域名的别名记录
- MX:查询域名的邮件交换记录
响应报文的其他区域
响应报文在问题区之后会包含回答区、权威区、附加区,这三个区域的结构一致,每个条目都是一条资源记录,包含域名、类型、类、生存时间、数据长度、数据六个部分。
资源记录中的域名通常采用指针压缩的方式编码,用2字节表示,最高两位为11,剩余14位表示域名在报文中的偏移量,减少重复域名的存储开销。
完整DNS查询报文示例
以下是一个查询www.ipipp.com的A记录的二进制报文示例,以十六进制形式展示,附带解析注释:
// 事务ID:0x1234 12 34 // 标志位:标准查询,期望递归 0x0100 01 00 // 问题数:1 00 01 // 回答数:0 00 00 // 权威数:0 00 00 // 附加数:0 00 00 // 问题区:域名www.ipipp.com的编码 03 77 77 77 05 69 70 69 70 70 03 63 6f 6d 00 // 查询类型:A(0x0001) 00 01 // 查询类:IN(0x0001) 00 01
DNS消息解析的注意事项
在解析DNS二进制消息时,需要注意字节序的转换,所有多字节字段都需要从网络字节序转换为本地字节序后再处理。同时遇到指针压缩的域名时,需要先读取偏移量,再到对应位置解析完整的域名,避免解析错误。
另外DNS消息的长度最大为512字节,超过这个长度会被截断,此时头部区的TC标志位会被置为1,客户端需要改用TCP协议重新发送查询请求,获取完整的响应报文。