DNS协议分析

上一篇文章从工作原理角度分析了DNS的作用与意义,这次来看看DNS到底是以什么形式进行通信的。

DNS报文格式如下所示:

DNS报文由12字节长的首部和4个长度可变的字段组成。

1.标识,由主机端设置,为的是唯一标识当前DNS报文。

2.16bit的标志字段被划分为若干子字段。

a.第1位为query或response,0为请求,1为响应

b.opcode是操作码,0是标准查询(大部分情况),1是反向查询,2是服务器状态请求

c.AA是1 bit标志,表示"授权回答 (authoritative answer)",默认是0。

d.TC表示是否可截断,用UDP时,表示应答长度超过512字节时,只返回前512字节

e.RD表示"期望递归"请求,与RA对应,一般是请求端发送。该标志为1,说明服务器必须对该请求进行处理。若为0,且请求的DNS没有一个授权回答,就返回一个能够解答这种查询的其他DNS列表,这种方式成为迭代查询。

f.RA表示"可用递归",是RD的响应。大部分DNS都可响应RD,除了少部分根服务器

g.后面三位必须是0

h.rcode通常只有两种情况,0为没有差错,3为名字有错,表示查询中指定的域名不存在。

3.随后的 4个16 bit字段说明最后 4个变长字段中包含的条目数。对于查询报文,问题数通常是1,而其他3项则均为0。对于应答报文,回答数至少是 1,剩下的两项可以是0或非0。

4.DNS查询报文中的问题部分(通常一个DNS报文只有一个问题)

a.查询名是要查找的名字,它是一个或多个标识符的序列。每个标识符以首字节的计数值来说明随后标识符的字节长度,每个名字以最后字节为 0结束,长度为0的标识符是根标识符。

计数字节的值必须是0-63的数,因为标识符的最大长度仅为63

上图中的00001011是第一个标识符的计数值,1011代表11,说明后面11个字节为一个标识符,对应的是chenjingjiu这11个字符,后面的点被省略

然后是这个00000010,说明后面的标识符只有两个字节。

00000000表示根标识符,结束查询名字段。

这里要注意,从问题部分的图片可以看出,查询名字段虽然画成了整4字节的倍数的样子,但其实是可以随意变长的。可以是11字节,也可以是17字节,不一定是4字节的整倍数。

b.每个问题有一个查询类型,而每个响应也有一个类型。大约有2 0个不同的类型值,其中的一些目前已经过时。下图中是目前常用的类型记录。

最常用的查询类型是A类型,表示期望获得查询名的IP地址。一个PTR查询则请求获得一个IP地址对应的域名,这是一个指针查询。

c.查询类通常是1,指互联网地址(某些站点也支持其他非 I P地址)。
 

下面是用wireshark抓取到的访问我的个人博客产生的DNS记录。

该DNS报文标识符为首先来看第一条数据0x290a,标志位第一位为0表示是一个请求,opcode是0表示该请求为标准请求,AA标志为0,TC标志为0说明未截断,RD为1表示请求递归,后面是保留位,最后4bit为0表示未出错。该DNS报文为一个请求报文,所以问题数为1,其余为0.再往后请求为chenjingjiu.cn,类型为A类型,为一个InterNet请求。

5.DNS响应报文中的资源记录部分

DNS报文中最后的三个字段,回答字段、授权字段和附加信息字段,均采用一种称为资源记录RR(Resource Record)的相同格式。

a.域名是记录中资源数据对应的名字。它的格式和前面介绍的查询名字段格式相同

b.类型说明RR的类型码。它的值和前面介绍的查询类型值是一样的。通常是1,指InterNet数据

c.生存时间字段是客户程序保留该资源记录的秒数。资源记录通常的生存时间值为2天。

d.资源数据长度说明资源数据的数量。该数据的格式依赖于类型字段的值。对于A类型资源,该值为4

e.对于A类型资源,资源数据是4字节的IP地址

 

接下来看第一个DNS请求的响应报文

可以看出前面与请求报文是相同的,只有后面不同。但是后面最有意思的是Name字段仅占用2字节,而请求时的报文可是用了16字节呢,这种差距不可谓不大了。这是DNS报文设计之初就考虑到每个报文中可能有很多重复的Name字段,因此设计了一种压缩表示法。前两bit为11表示压缩格式,而后面跟的14bit表示的是Name所在的位置相对于DNS首部的偏移值。本例中1100转换为12,说明相对于DNS首部偏移值是12字节,正好是DNS首部的定长字段。而第13字节就是请求的Name。所以该字段值一般都是12.

后面指示的类型2字节,类别2字节,TTL是4字节,数据长度2字节,地址占用4字节。返回博客IP地址188.131.238.222.

来看一下报文的长度,请求报文74字节,响应报文90字节。做一个简单的计算:

请求报文(74)=Ether(14)+IP(20)+UDP(8)+DNS(32)=Ether[D_MAC(6)+S_MAC(6)+IP_type(2)]+IP[Version(0.5)+Header_len(0.5)+Service_type(1)+Total_len(2)+Identity(2)+Flag(3bit)+Offset(13bit)+TTL(1)+Protocol(1)+Checksum(2)+S_IP(4)+D_IP(4)]+UDP[S_port(2)+D_port(2)+UDP_Len(2)+Checksum(2)]+DNS[TID(2)+Flag(2)+Question(2)+Ans_RR(2)+Auth_RR(2)+Add_RR(2)+Name(16)+DNS_type(2)+Class(2)]

响应报文(90)=请求报文(74)+DNS_response[Name(2)+Type(2)+Class(2)+TTL(4)+Data_len(2)+Address(4)]

 

最后来考虑一下DNS到底是用UDP还是TCP?

DNS名字服务器使用的熟知端口号无论对UDP还是TCP都是53,这意味着DNS均支持UDP和TCP访问。但从刚才wireshark抓出的包可以看出,基本上DNS都是通过UDP传输的,那么什么时候用TCP呢?

前面介绍过一个TC位,当DNS发出一个查询请求,并且返回响应中的TC位为1,说明该DNS响应长度超过512字节,后面的响应有缺失,因此此时需要利用TCP进行重传。TCP可以分段传输任意长度的报文。

本文就到这里,后面还会接触到一个叫DNS2TCP的隧道,并且利用这个隧道来实现上网。

路漫漫其修远兮,吾将上下而求索。

点赞

发表评论

[2;3Rer>