DNS 实战系列(一): 报文解析
本篇博客将会根据 RFC 的内容,实战分析在查询 google.com 时 DNS 报文中的每个字节。
准备工作
我们可以用 nc
命令来获取查询报文和响应报文的数据包
nc -u -l -p 1053 > query_packet.txt
# 新开一个终端,等待dig报错后,ctrl-c 掉 nc
dig +retry=0 -p 1053 @127.0.0.1 +noedns google.com
hexdump -C query_packet.txt
# 执行一两秒后断开
nc -u 208.67.222.123 5353 < query_packet.txt > response_packet.txt
hexdump -C response_packet.txt
你可以在这里下载本篇所用示例 query_packet.txt , response_packet.txt
在 RFC1035 中, DNS 报文格式如下。
在本篇博客的实例中,查询报文包含 Header 和 Question,响应报文包含 Header、Question 和 Answer。
Query Packet
我们使用 hexdump
来打印刚才生成的查询报文
hexdump -C query_packet.txt
00000000 85 57 01 20 00 01 00 00 00 00 00 00 06 67 6f 6f |.W. .........goo|
00000010 67 6c 65 03 63 6f 6d 00 00 01 00 01 |gle.com.....|
0000001c
根据DNS 报文格式,我们要分析的查询报文大致可以分为两个区域: Header 和 Question.
Header 的长度为 12 bytes, 剩余部分则全部为 Question 区域, 如下图:
Query Packet: Header Section
在 RFC1035 中, DNS 报文 Header 区域格式如下
在这里解释一下 headers 的 12 个 bytes:
ID
:85 57
一般以 10 进制 id 34135 表示,对于查询和应答使用相同的 id,长度 2 字节(16 bits)QR
到RCODE
占两个字节大小,他们的十六进制0x01 20
转换成二进制是0000 0001 0010 0000
QR
:0
,0 代表查询,1代表响应,长度 1 bitOPCODE
:0000
, 0代表标准查询,长度 4 bitsAA
:0
, 查询时可忽略,长度 1 bitTC
:0
, 查询时可忽略,长度 1 bitRD
:1
, 希望 DNS 服务器使用递归解析,长度 1 bitRA
:0
, 查询时可忽略,长度 1 bitZ
:010
, 本来是保留的3个bits,现在分为Z
/AD
/CD
三个 flag,这里知道是 dnssec 相关即可RCODE
:0000
, 查询时可忽略,长度 4 bits
QDCOUNT
:0x0001
->0000 0001
请求查询的问题数,一般为1,长度16 bits (2字节)ANCOUNT
:0x0000
->0000 0000
查询时可忽略,长度 16 bits (2字节)NSCOUNT
:0x0000
->0000 0000
查询时可忽略,长度 16 bits (2字节)ARCOUNT
:0x0000
->0000 0000
查询时可忽略,长度 16 bits (2字节)
我们可以发现,在查询报文的 header 中,ANCOUNT、NSCOUNT、ARCOUNT (第7到12字节) 总是0,这可以作为我们判断一个 DNS 报文是否为查询报文的依据;另一个依据是,查询报文比较短,到 Question 就结束了,没有 Answer 区域。
Query Packet: Question Section
根据 RFC1035, Question 区域格式如下
qname qtype qclass
hex 06 67 6f 6f 67 6c 65 03 63 6f 6d 00 00 01 00 01
ascii g o o g l e c o m
dec 6 3 0 1 1
我们可以看到查询 google.com
并没有用 .
来分割,而是用数字。
qname
的表现形式是: 长度 - ascii 码对应的十六进制 - 长度 - ascii 码对应的十六进制 - 0 ascii 码对应的十六进制非常容易分辨,我们都知道域名是由字母组成的,A 的为 41, z 为 7A,在 41 之后十六进制一般就是域名的组成部分,比较小的字节就是代表了域名分割段长度了。当然还会有额外情况,比如-
符号的十六进制为2D
。qtype
0001
1 代表请求A记录qclass
0001
1 代表向网络查询 (IN)
到此为止,查询报文就看完了。
Response Packet
同样使用 hexdump
来打印响应报文
hexdump -C response_packet.txt
00000000 85 57 81 80 00 01 00 01 00 00 00 00 06 67 6f 6f |.W...........goo|
00000010 67 6c 65 03 63 6f 6d 00 00 01 00 01 c0 0c 00 01 |gle.com.........|
00000020 00 01 00 00 01 2c 00 04 8e fb 2b 0e |.....,....+.|
0000002c
Response Packet: Header Section
响应报文的 header 部分格式与查询报文相同
查询报文的 header 部分为 85 57 01 20 00 01 00 00 00 00 00 00
响应报文的 header 部分为 85 57 81 80 00 01 00 01 00 00 00 00
只有第 3、4字节 (QR到RCODE) 和第8个字节(ANCOUNT)不同,我们可以详细对比一下他们的区别
query packet | answer packet | |
---|---|---|
QR 到 RCODE 两个字节 | 0x0120 -> 0000 0001 0010 0000 |
0x8180 -> 1000 0001 1000 0000 |
QR query/response | 0 代表查询 |
1 代表响应 |
OPCODE | 0000 代表标准查询 |
0000 代表标准查询 |
AA (Authoritative Answer) | 0 查询时可忽略 |
0 代表不是权威服务器返回的结果 |
TC (TrunCation) | 0 查询时可忽略 |
0 表示没有超出UDP报文长度 |
RD (Recursion Desired) | 1 希望 DNS 服务器使用递归解析 |
由查询报文设置,与其相同 |
RA (Recursion Available) | 0 查询时可忽略 |
1 DNS服务器使用了递归查询 |
Z | 010 dnssec 相关 |
000 DNS服务器没有响应 dnssec |
RCODE | 0000 查询时可忽略,长度 4 bits |
0000 代表了没有错误 |
ANCOUNT (answer resource records) | 0000 0000 查询时可忽略 |
0000 0001 有一个响应结果 |
Z
本来是保留的,总是置为0,现在分为 Z
/AD
/CD
三个 flag,这里知道是 dnssec 相关即可,不需要关心。如果你很在意,可以看 rfc2535 https://datatracker.ietf.org/doc/html/rfc2535#section-6.1
Response Packet: Question Section
question 部分和请求报文中完全相同,这里不再重复说明。
Response Packet: Answer Section
Answer 区域也叫资源记录(RR)区域,我们同样可以对照 RFC1035 来看
NAME TYPE CLASS TTL RDLENGTH RDATA
------ ------ ------ -------------- ------ --------------
HEX c0 0c 00 01 00 01 00 00 01 2c 00 04 8e fb 2b 0e
DEC 192 12 1 1 293 4 142 251 43 14
NAME
: 总是以 c00c
开头,我们可以看一下具体是怎么计算出来的:
c00c
的二进制表现形式为 1100000000001100
11
最高位总是1100000000001100
转为十六进制是 0x12,这代表了一个偏移量,或者说指针。当响应报文中重复出现一些域名,我们可以利用已经存在的数据来减少数据包大小。我们已经知道了 header 区域的指针是 0x0 ~ 0x11,因此可以从 0x12 开始计算,这正好是 question 区域的初始指针。
接下里看剩余的部分:
TYPE
:1
代表了 A类型CLASS
:1
代表了 IN (Internet)TTL
:0000012c
通常以十进制293
表示,单位是秒RDLENGTH
:0004
ip 占用的资源的长度,这里是 4字节RDATA
:8e fb 2b 0e
响应的数据,ipv4 一般是以十进制142 251 43 14
的形式表示
至此,请求报文和响应报文我们都已经手撕完成。
对你可能有帮助的资料
- DNS RFC1035: https://datatracker.ietf.org/doc/html/rfc1035#section-4.1.1
- DNSSEC RFC2535: https://datatracker.ietf.org/doc/html/rfc2535#section-6.1
- DNS 字段格式(华为文档): https://support.huawei.com/enterprise/zh/doc/EDOC1100174722/f917b5d7/dns
- DNS记录类型列表: https://zh.wikipedia.org/wiki/DNS%E8%AE%B0%E5%BD%95%E7%B1%BB%E5%9E%8B%E5%88%97%E8%A1%A8