DNS报文格式介绍
最近想做个关于DNS的工具,就当是学Rust练手。关于DNS报文格式,网络上有很多资料(比如:DNS Message Format 、 RFC1035 和 DNS报文格式(RFC1035)),本文主要是作为上述资料的例子补充。
简介
简单总结DNS报文,一次DNS查询包括请求和响应。请求报文和响应报文是一段字节流,具有相同格式,按顺序包括:头、问题、应答、授权应答、附加信息。头有头的格式,问题有问题的格式,应答、授权应答、附加信息具有相同的格式(Resource record格式)。需要注意的两点:
- 域名是以label格式编码的,即
www.google.com
会被编码为3www6google3com
- 为了压缩报文大小,域名中有重复的字符会用2字节的指针来指向前面的字符替代
例子
比如我们查询www.baidu.com
,
请求部分
0 - 1 : 11001101 01110000 : "\u{370}"
2 - 3 : 00000001 00000000 : "\u{1}\u{0}"
4 - 5 : 00000000 00000001 : "\u{0}\u{1}"
6 - 7 : 00000000 00000000 : "\u{0}\u{0}"
8 - 9 : 00000000 00000000 : "\u{0}\u{0}"
10 - 11 : 00000000 00000000 : "\u{0}\u{1}"
12 - 13 : 00000011 01110111 : "\u{3}w"
14 - 15 : 01110111 01110111 : "ww"
16 - 17 : 00000101 01100010 : "\u{5}b"
18 - 19 : 01100001 01101001 : "ai"
20 - 21 : 01100100 01110101 : "du"
22 - 23 : 00000011 01100011 : "\u{3}c"
24 - 25 : 01101111 01101101 : "om"
26 - 27 : 00000000 00000000 : "\u{0}\u{0}"
28 - 29 : 00000001 00000000 : "\u{1}\u{0}"
30 : 00000001 : "\u{1}"
左边序号表示字节号,中间部分就是实际传输的字节流,右边是对应ascii码。
头部分
前12个字节(0-11)是报文的头部分,其中可以得到报文的id等信息,每个字段都由两个连续的字节组成,从上面我们可以读出:id为52592,为请求,标准查询,期望递归,问题数目为1,应答数为0,授权应答数为0,附加数为0。
问题部分
编号12-30,其中12-26存有请求的域名,即3www5baidu3com0
,接下来27-30表示QTYPE为1,QCLASS为1。
剩余部分
由于头部分应答数、授权应答数、附加数均为0,故无剩余部分
响应部分
0 - 1 : 11001101 01110000 : "\u{370}"
2 - 3 : 10000001 10000000 : "@"
4 - 5 : 00000000 00000001 : "\u{0}\u{1}"
6 - 7 : 00000000 00000011 : "\u{0}\u{3}"
8 - 9 : 00000000 00000000 : "\u{0}\u{0}"
10 - 11 : 00000000 00000000 : "\u{0}\u{0}"
12 - 13 : 00000011 01110111 : "\u{3}w"
14 - 15 : 01110111 01110111 : "ww"
16 - 17 : 00000101 01100010 : "\u{5}b"
18 - 19 : 01100001 01101001 : "ai"
20 - 21 : 01100100 01110101 : "du"
22 - 23 : 00000011 01100011 : "\u{3}c"
24 - 25 : 01101111 01101101 : "om"
26 - 27 : 00000000 00000000 : "\u{0}\u{0}"
28 - 29 : 00000001 00000000 : "\u{1}\u{0}"
30 - 31 : 00000001 11000000 : "\u{1}\u{0}"
32 - 33 : 00001100 00000000 : "\u{c}\u{0}"
34 - 35 : 00000101 00000000 : "\u{5}\u{0}"
36 - 37 : 00000001 00000000 : "\u{1}\u{0}"
38 - 39 : 00000000 00000001 : "\u{0}\u{1}"
40 - 41 : 11010101 00000000 : "\u{540}"
42 - 43 : 00001111 00000011 : "\u{f}\u{3}"
44 - 45 : 01110111 01110111 : "ww"
46 - 47 : 01110111 00000001 : "w\u{1}"
48 - 49 : 01100001 00000110 : "a\u{6}"
50 - 51 : 01110011 01101000 : "sh"
52 - 53 : 01101001 01100110 : "if"
54 - 55 : 01100101 01101110 : "en"
56 - 57 : 11000000 00010110 : "\u{16}"
58 - 59 : 11000000 00101011 : "+"
60 - 61 : 00000000 00000001 : "\u{0}\u{1}"
62 - 63 : 00000000 00000001 : "\u{0}\u{1}"
64 - 65 : 00000000 00000000 : "\u{0}\u{0}"
66 - 67 : 00000000 10101111 : "\u{0}\u{3c0}"
68 - 69 : 00000000 00000100 : "\u{0}\u{4}"
70 - 71 : 01110011 11101111 : "s\u{f000}"
72 - 73 : 11010010 00011011 : "\u{49b}"
74 - 75 : 11000000 00101011 : "+"
76 - 77 : 00000000 00000001 : "\u{0}\u{1}"
78 - 79 : 00000000 00000001 : "\u{0}\u{1}"
80 - 81 : 00000000 00000000 : "\u{0}\u{0}"
82 - 83 : 00000000 10101111 : "\u{0}\u{3c0}"
84 - 85 : 00000000 00000100 : "\u{0}\u{4}"
86 - 87 : 01110011 11101111 : "s\u{f000}"
88 - 89 : 11010011 01110000 : "\u{4f0}"
头部分
0-11仍是报文的头部分,只是把编号2的字节首比特置为1表示应答,把编号3的字节首比特置为1表示服务器支持递归查询,并把ANCOUNT设置为3表示回答数为3个。
问题部分
问题部分与请求相同。
剩余部分
由于回答数为3个,所以接下来从31开始都为Resource record格式的回答。其中第一个回答从31到57,第二个回答从58到73,第三个回答从74到89。
简单来看看回答部分,其中31-32字节11000000 00001100
为Resource record格式的NAME段,以11
开头表示是指针,指向编号12的字节,即就是问题部分的3www5baidu3com0
,接下来是TYPE为5,CLASS为1,TTL为469,RDLENGTH为15,由于TYPE为5,所以RDATA为CNAME,是域名,由15个字节组成,从43到57为止,为www.a.shifen.com
,需要注意的是最后两个字节,即56-57也为指针,指向前面com
。
剩下的两个回答都类似,只是TYPE为1,表示RDATA为A类地址,即ipv4地址。比如71-74:01110011 11101111 11010010 00011011
表示为十进制为115.239.210.27
。
TCP协议
上述是针对udp协议而言的,DNS查询可以通过tcp协议传输,报文的区别仅在于tcp协议的报文的最开始两个字节存放udp协议报文的长度,其他均不变,例如对于最开始的请求报文,只需在报文最前面插入00000000 00011111
(即十进制31)即可