DNS协议数据包解析

协议格式

引用于 message

1
2
3
4
5
6
7
8
9
10
11
+---------------------+
| Header |
+---------------------+
| Question | the question for the name server
+---------------------+
| Answer | RRs answering the question
+---------------------+
| Authority | RRs pointing toward an authority
+---------------------+
| Additional | RRs holding additional information
+---------------------+

Header格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
                                1  1  1  1  1  1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| ID |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|QR| Opcode |AA|TC|RD|RA| Z | RCODE |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| QDCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| ANCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| NSCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| ARCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

关键的几个字段

ID (dns.id)

ID是一个16bit长标识符,可以理解为一个2字节的无符号数。该字段由发起查询请求的客户端生成。服务端在生成响应报文时会原样复制这个值,这意味着相同ID的请求和响应报文是一对请求。因此客户端可以根据这个字段得知某个响应报文对应的是哪次查询请求。

QR (dns.flags.response)

1bit QR是flag位,值为0表示这是一次查询,1则表示该报文是响应报文。

Opcode (dns.flags.opcode)

4bit长的查询类型字段,这个值由查询发起者设置,在响应时原样复制。
其它值很少见,一般就是0

AA 权威应答标志位 (dns.flags.truncated)

1bit 权威应答标记,当响应报文由权威服务器发出时,该位置1,否则为0。

TC 截断标志位 (dns.flags.recdesired)

1 bit 当使用UDP传输时,若响应数据超过DNS标准限制(超过512B),数据包便会发生截断,超出部分被丢弃,此时该flag位被置1。
当客户端发现TC位被置1的响应数据包时应该选择使用TCP重新发送查询。因为TCP DNS报文不受512字节限制。

RD 递归查询期望标志位 (dns.flags.recavail)

1 bit 客户端希望服务器对此次查询进行递归查询时将该位置1,否则置0。响应时RD位会复制到响应报文内。

RA 递归查询可用标志位 (dns.flags.recdesired)

1 bit

Z 保留段 (dns.flags.z)

3 bit

RCODE 响应码 (dns.flags.rcode)

4 bit

0 没有错误。
1 Format error:格式错误,服务器不能理解请求的报文格式。
2 Server failure:服务器失败,因为服务器的原因导致没办法处理这个请求
3 Name Error:名字错误,该值只对权威应答有意义,它表示请求的域名不存在。
4 Not Implemented:未实现,域名服务器不支持该查询类型
5 Refused:拒绝服务,服务器由于设置的策略拒绝给出应答。

QDCOUNT,ANCOUNT,NSCOUNT,ARCOUNT

这四个字段都是一个16bit无符号整数,分别表示后面四个数据段内条目的个数。
dns.count.queries
dns.count.answers
dns.count.auth_rr
dns.count.add_rr

Question段

question部分存放的是向服务器查询的域名数据。它由QDCOUNT个“条目”(Entry)组成。一般情况下它只有一条Entry。
每个Entry的格式是相同的,如下所示:

1
2
3
4
5
6
7
8
9
10
11
                                1  1  1  1  1  1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| |
/ QNAME /
/ /
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| QTYPE |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| QCLASS |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

qname 查询的域名(dns.qry.name)

由labels序列构成的域名。QNAME的格式使用DNS标准名称表示法。

DNS标准名称表示法指分为普通label,压缩label。
前2bit为00时为普通,后6位表示这个label的长度。
前2bit为11时为普通,后6位+后一字节的8位为指针,相关于头部的偏移量。
这个后面举例
eg:
dig +x 8.8.8.8抓包,按dns.qry.name == 8.8.8.8.in-addr.arpa

QTYPE 域名的资源类型 (dns.qry.type)

常见的查A记录,NS
A 1 通过域名查IP
AAAA 28 查IPV6的地址
CNAME 5 域名别名
PTR 12 用IP解析域名

完整的类型见 iana

QCLASS 请求的类别 (dns.qry.class)

互联网请求时值为“IN”,对应值为0x0001

https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-2

Answer 段

dns.resp.name

Answer,Authority和Additional三个段的格式是完全相同的,都是由零至多条Resource Record(资源记录)构成。这些资源记录因为不同的用途而被分开存放。

RR(Resource Record)资源记录

资源记录是DNS系统中非常重要的一部分,它拥有一个变长的结构,具体格式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
                                1  1  1  1  1  1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| |
/ /
/ NAME /
| |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| TYPE |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| CLASS |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| TTL |
| |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| RDLENGTH |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
/ RDATA /
/ /
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

NAME

它指定该条记录对应的是哪个域名,格式使用DNS标准名称表示法

TYPE

资源记录的类型,见QTYPE

CLASS

请求的类别QCLASS

TTL(Time To Live)资源的有效期 (dns.resp.ttl)

表示你可以将该条RR缓存TLL秒,TTL为0表示该RR不能被缓存。

RDLENGTH 数据长度 (dns.resp.len)

一个两字节非负整数,用于指定RDATA部分的长度(字节数)。

RDATA

部分是一个长度和结构都可变的字段,它的具体结构取决于TYPE字段指定的资源类型。

dns.a
dns.cname

A记录

A就是Address的首字母,它用于解析一个域名对应的IPv4地址,这是生活中最常使用的记录
它长度固定为4字节,直接以二进制形式表示一个IPv4地址

test cmd
dig -t A xx.com

NS

域名服务器记录,用来指定该域名由哪个DNS服务器来进行解析。它的结果是一条新的域名,不过是权威服务器的。
NS的格式和RR里NAME字段的格式相同,它使用DNS标准名称表示法返回一条新域名。

dig -t NS xx.com

CNAME

CNAME是域名别名记录,它用于将多个域名指向同一个主机。它的解析结果通常是另一个域名。

dig -t CNAME xx.com

SOA

SOA基本上是最复杂的资源记录类型了.

详情

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/ MNAME /
/ /
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/ RNAME /
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| SERIAL |
| |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| REFRESH |
| |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| RETRY |
| |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| EXPIRE |
| |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| MINIMUM |
| |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

SOA即Start Of Zone,“区域开始记录”,这个记录主要用于权威DNS服务器进行主从同步。

  • MNAME (dns.soa.mname) 该区域的主名称服务器
  • RNAME (dns.soa.rname) 负责此区域的管理员的电子邮件地址。
  • SERIAL (dns.soa.serial_number) 区域序列号,32bit无符号整型,从服务器发现这个值更新的时候会启动一次同步。
  • REFRESH 从服务器从主服务器上获取SOA记录的周期
  • RETRY 重试时间,当从服务器获取SOA失败时会在该字段指定的时间内重新尝试获取,这个值应当小于REFRESH
  • EXPIRE 过期时间,如果从主服务器一直未响应,则在该时间后不再响应该区域的请求。这个值应该大于REFRESH+RETRY。
  • MINIMUM 该区域内所有记录TTL的下限。

dns服务器无法响应dns请求时,会默认响应soa记录。

比如 dig -t A [xasdfasd].com 一个不存在域名。则会返加SOA记录。
比如 dig -t CNAME xx.com 这个没有CNAME,也会返回SOA记录

SRV

OPT

报文传输

DNS工作在UDP和TCP上,都使用53端口。
在大多数情况下,DNS请求和响应都使用UDP数据包,此时UDP的payload便直接是DNS的报文。
当使用TCP时,整个DNS报文部分没有任何变化,但是由于TCP是一个流协议,你需要额外指定一个报文长度来进行报文划分。这个长度字段长2字节,在DNS报文之前发送。因此对于报文”Message”,使用TCP时,实际传输的数据是:
[2字节Message报文长度][Message具体内容]
同样的,这个长度字段使用网络序。

参考


DNS协议数据包解析
https://blog.fengcl.com/2021/03/19/dns-protocol/
作者
frank
发布于
2021年3月19日
许可协议