DNS报文格式介绍

Posted: Modified:

最近想做个关于DNS的工具,就当是学Rust练手。关于DNS报文格式,网络上有很多资料(比如:DNS Message FormatRFC1035DNS报文格式(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)即可