DNS请求报文
本文内容参考了:DNS 请求报文详解 、DNS报文格式解析
1、DNS是什么
DNS(Domain Name System,缩写:DNS)域名系统,提供域名和IP映射。互联网访问使用ip地址进行通信,但是由于ip地址不好记而且不安全,所以使用域名进行互联网通信,但是域名只是一个字符串,无法进行网络通信,而DNS就是为了解决这一情况,我们将ip和域名进行绑定,将域名通过DNS服务器查找出真实的IP地址再进行通信。一般情况下,DNS使用UDP进行通信,当然也可以通过使用TCP,不过默认情况下,使用UDP,UDP是无状态的,且不想TCP那样需要进行握手连接,且一旦域名解析成功后,后续就不需要进行其他通信。域名解析端口一般为53。
2、DNS协议
互联网中信息的传输需要通过一定的协议,比如TCP/IP协议,HTTP协议等。
2.1 协议内容
2.1.1 一个完整的DNS报文协议包含内容:
-
报头(Header):包含一个ID和8个标志位
- ID:数据报标识,一个随机数字,2个字节,发送报文时携带,收到响应时携带,通过它可以区分 DNS 应答报文是对哪个请求进行响应的,如果发送和收到的ID相同则表示同一连接会话。
- Flags:数据报标志位,2个字节,表示报文的状态
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 QR OPCODE AA TC RD RA Z RCODE
标志 介绍 QR (Response):查询请求/响应的标志信息。1bit大小,查询请求时,值为 0;响应时,值为 1。 OPCODE 操作码。4bit大小,其中,0 表示标准查询(域名解析IP);1 表示反向查询;2 表示服务器状态请求。 AA (Authoritative):授权应答,1bit大小,该字段在响应报文中有效。值为 1 时, 表示名称服务器是权威服务器;值为 0 时,表示不是权威服务器。 TC (Truncated):表示是否被截断。1bit大小,值为 1 时,表示响应已超过 512 字节并已被截断,只返回前 512 个字节。(UDP传输最大支持512字节) RD 期望递归(迭代查询),1bit大小,该字段在请求报文中有效,1表示使用迭代查询,0表示不使用,应答时返回相同。 Z 保留字段,为0就行了 RCODE (ResponseCode)响应字段。 0 表示没有错误;1表示报文格式错误;2表示域名服务器失败;3表示名字错误,解析的域名不存在;4 表示查询类型不支持,即域名服务器不支持查询类型;5表示拒绝,一般是服务器由于设置的策略拒绝给出应答,如服务器不希望对某些请求者给出应答。其他为保留字。 - 查询请求问题的数量(QuestionCount):记录请求问题的数量,2个字节。
- 回答问题的数量(AnswerCount):记录响应请求的结果数量,2个字节
- 授权名称服务器数量(AuthorityCount):2个字节
- 附加资源数量(AdditionalCount):2个字节
-
查询请求问题(Question):不定长,请求时设置
-
回答(Answer):不定长,响应时设置
-
授权(Authority):不定长,响应时设置
-
附加(Additional):不定长,响应时设置
2.1.2 DNS报头结构
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ID | |||||||||||||||
| QR | OPCODE | AA | TC | RD | RA | Z | RCODE | ||||||||
| QuestionCount | |||||||||||||||
| AnswerCount | |||||||||||||||
| AuthorityCount | |||||||||||||||
| AdditinalCount | |||||||||||||||
public class DnsHeader {
public static final int DNS_HEADER_SIZE = 12;
/**
* 事务ID DNS 报文的 ID 标识。对于请求报文和其对应的应答报文,该字段的值是相同的。
* 通过它可以区分 DNS 应答报文是对哪个请求进行响应的。
*/
public short mTransactionId;
/**
* DNS 标志
* DNS 报文中的标志字段。
*/
public DnsFlags mDnsFlags;
/**
* 问题计数
* DNS 查询请求的数目。
*/
public short mQuestionCount;
/**
* 回答资源计数
* DNS 响应的数目。
*/
public short mAnswerCount;
/**
* 权威名称服务器计数
* 权威名称服务器的数目。
*/
public short mAuthorityCount;
/**
* 附加资源计数
* 额外的记录数目(权威名称服务器对应 IP 地址的数目)
*/
public short mAdditionalCount;
// ...
}
public class DnsFlags {
/**
* 1bit
* QR(Response):查询请求/响应的标志信息。查询请求时,值为 0;响应时,值为 1。
*/
public boolean mQr;
/**
* 4bit
* Opcode:操作码。其中,0 表示标准查询;1 表示反向查询;2 表示服务器状态请求。
*/
public int mOpCode;
/**
* 1bit
* AA(Authoritative):授权应答,该字段在响应报文中有效。值为 1 时,
* 表示名称服务器是权威服务器;值为 0 时,表示不是权威服务器。
*/
public boolean mAa;
/**
* 1bit
* TC(Truncated):表示是否被截断。值为 1 时,表示响应已超过 512 字节并已被截断,只返回前 512 个字节。
*/
public boolean mTc;
/**
* 1bit
* RD(Recursion Desired):期望递归。该字段能在一个查询中设置,并在响应中返回。
* 该标志告诉名称服务器必须处理这个查询,这种方式被称为一个递归查询。如果该位为 0,
* 且被请求的名称服务器没有一个授权回答,它将返回一个能解答该查询的其他名称服务器列表。
* 这种方式被称为迭代查询。
*/
public boolean mRd;
/**
* 1bit
* RA(Recursion Available):可用递归。该字段只出现在响应报文中。当值为 1 时,表示服务器支持递归查询。
*/
public boolean mRa;
/**
* 3bit
* Z:保留字段,在所有的请求和应答报文中,它的值必须为 0。
*/
public int mZ;
/**
* 4bit
* rCode(Reply code):返回码字段,表示响应的差错状态。
* 当值为 0 时,表示没有错误;
* 当值为 1 时,表示报文格式错误(Format error),服务器不能理解请求的报文;
* 当值为 2 时,表示域名服务器失败(Server failure),因为服务器的原因导致没办法处理这个请求;
* 当值为 3 时,表示名字错误(Name Error),只有对授权域名解析服务器有意义,指出解析的域名不存在;
* 当值为 4 时,表示查询类型不支持(Not Implemented),即域名服务器不支持查询类型;
* 当值为 5 时,表示拒绝(Refused),一般是服务器由于设置的策略拒绝给出应答,如服务器不希望对某些请求者给出应答。
*/
public int mrCode;
// ...
}
2.1.3 DNS报文结构
Question、Answer、Authority、Additinal 大小不固定,由Header头中其对应的*Count数量决定。
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Header | |||||||||||||||
| Question ... | |||||||||||||||
| Answer ... | |||||||||||||||
| Authority ... | |||||||||||||||
| Additinal ... | |||||||||||||||
DNS报文对象
public class DnsPacket {
/**
* DNS 报头部分
*/
public DnsHeader mHeader;
/**
* Question部分
*/
public Question[] mQuestions;
/**
* Answer部分
*/
public Resource[] mAnswers;
/**
* Authoritys部分
*/
public Resource[] mAuthoritys;
/**
* Additionals部分
*/
public Resource[] mAdditionals;
/**
* 报文大小
*/
public int mSize;
// ...
}
2.1.4 Question部分
问题部分指的是由客户端向DNS服务器发送的请求数据,这部分包含固定的格式,主要有主机名、查询类型、查询类三个部分。
- 主机名(domain):一般为要查询的域名,也可以是 IP 地址,表示反向查询。最大支持63个字节。域名策略
- 查询类型(queryType):DNS 查询请求的资源类型。通常查询类型为 A(1) 类型,表示由域名获取对应的 IP 地址。
- 1 -- A : 由域名获得IPv4地址
- 2 -- NS : 查询授权的域名服务器
- 5 -- CNAME : 查询规范名称(别名)
- 6 -- SOA : 开始授权
- 11 -- WKS : 熟知服务
- 12 -- PTR : 把IP地址转换成域名(指针记录,反向查询)
- 13 -- HINFO : 主机信息
- 15 -- MX : 邮件交换记录
- 28 -- AAAA : 由域名获得IPv6地址
- 252 -- AXFR : 对区域转换的请求,传送整个区的请求。
- 255 -- ANY : 对所有记录的请求 *
- 查询类(queryClass):地址类型,通常为互联网地址,值为 1(IN)。
域名策略: 计算到下一个“.”为止的字符长度,使用一个字节表示这个长度,并添加,依次解析十六进制...(往后策略如前),直到解析完毕,添加0x00作为结尾 如:www.baidu.com为例。 03 www 05 baidu 03 com 00(以00结束) ==> 03 | 77 77 77 | 05 | 62 61 69 64 75 | 03 | 63 6F 6D | 00
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| domain | |||||||||||||||
| queryType | |||||||||||||||
| queryClass | |||||||||||||||
Question
/**
* DNS 数据包的Queries区域
*/
public class Question {
/**
* 单个最大63字节
*查询名:一般为要查询的域名,有时也会是 IP 地址,用于反向查询。
* 例如:域名www.baidu.com
* 转成十六进制方式: 03 www 05 baidu 03 com 00(以00结束),字符需要转化为十六进制
*/
public String mQueryDomain;
/**
*查询类型:DNS 查询请求的资源类型。通常查询类型为 A 类型,表示由域名获取对应的 IP 地址。
* 1 - A : 由域名获得IPv4地址
* 2 - NS : 查询授权的域名服务器
* 5 - CNAME : 查询规范名称(别名)
* 6 - SOA : 开始授权
* 11 - WKS : 熟知服务
* 12 - PTR : 把IP地址转换成域名(指针记录,反向查询)
* 13 - HINFO : 主机信息
* 15 - MX : 邮件交换记录
* 28 - AAAA : 由域名获得IPv6地址
* 252 - AXFR : 对区域转换的请求,传送整个区的请求。
* 255 - ANY : 对所有记录的请求 *
*/
public short mQueryType;
/**
*查询类:地址类型,通常为互联网地址,值为 1。
*/
public short mQueryClass;
// ...
}
2.1.5 Answer、Authority、Additional部分
回答、授权、附加这三部分的格式内容是一样的,主要是DNS报文响应的数据。包含域名、查询类型、查询类、生存时间、数据长度、数据。
- 域名(domain):Quesion中的域名,域名解析 当响应中的Question中包含此域名,则此部分表示一个指针偏移,比如Question中包含域名:www.baidu.com ,在Answer等资源中也包含此域名,则可以使用两个字节来表示当前域名在Question中的偏移,如C00C,11 00000000001100,前两位为标识指针,表示Question域名在数据包中的偏移,后十四位表示,这个域名字符串的长度信息。
- 查询类型(queryType):Question的查询类型
- 查询类(queryClass):Question查询类
- 生存时间(ttl):生存时间,以秒为单位
- 数据长度(dataLength):数据长度
- 数据(data):数据
| Answer、Authority、Additional |
|---|
| domain |
| queryType |
| queryClass |
| ttl |
| dataLength |
| data |
Resource
/**
* DNS 数据包Resource区域
*/
public class Resource {
/**
* 域名,与Question区域的域名类似,
* 有一点不同就是,当报文中域名重复出现的时候,该字段使用2个字节的偏移指针来表示。
* 比如:在资源记录中,域名通常是查询问题部分的域名的重复,因此用2字节的指针来表示,
* 具体格式是最前面的两个高位是 11,用于识别指针。其余的14位从DNS报文的开始处计数
* (从0开始),指出该报文中的相应字节数。一个典型的例子,C00C(1100000000001100,
* 12正好是头部的长度,其正好指向Queries区域的查询名字字段)。
*/
public String mDomain;
/**
* 查询类型。与Question区域的类型类似,
*/
public short mQueryType;
/**
* 查询类CLASS。对于Internet信息,总是1,代表IN。表示RDATA的类
*/
public short mQueryClass;
/**
* 生存时间。4字节无符号整数表示资源记录可以缓存的时间。
* 以秒为单位,表示的是资源记录的生命周期,一般用于当地址解析程序取出资源记录后决定保存
* 及使用缓存数据的时间,它同时也可以表明该资源记录的稳定程度,极为稳定的信息会被分配一
* 个很大的值(比如86400,这是一天的秒数)。0代表只能被传输,但是不能被缓存。
*
* TTL 是 DNS 记录中的一个值,可决定对该记录所做的后续更改需要多少秒才会生效。网域的每条
* DNS 记录(如 MX 记录、CNAME 记录等)都有一个 TTL 值。一条记录目前所设的 TTL 决定了您
* 现在所做的任何更改需要多久才会生效。例如,如果一条记录的 TTL 为 86400 秒,则对该记录
* 的更改最多需要 24 小时才会生效
*
* 请注意,更改记录的 TTL 会影响之后的所有更改需要多久才会生效。我们建议将 TTL 值设置为 3600,
* 即让整个互联网中的服务器每小时检查一次该记录的更新情况。较短的 TTL 在之前的有效期到期后才
* 会生效。这意味着,下次更新该记录时,您的更改最多要一个小时才会生效。要使后续的更改更快生效
* (例如,如果您想快速还原一项更改),则可以设置较短的 TTL 值,如 300 秒(5 分钟)。正确配置
* 记录后,我们建议将 TTL 值设置为 86400,即让整个互联网中的服务器每 24 小时检查一次记录的更新情况。
*/
public int mTtl;
/**
* 资源数据长度。URDLENGT**H 2个字节无符号整数表示RDATA的长度
*/
public short mDataLength;
/**
*资源数据 RDATA该字段是一个可变长字段,不定长字符串来表示记录,格式与TYPE和CLASS有关。
* 比如,TYPE是A,CLASS 是 IN,那么RDATA就是一个4个字节的ARPA网络地址。表示按照查询段的
* 要求返回的相关资源记录的数据。可以是Address(表明查询报文想要的回应是一个IP地址)或者
* CNAME(表明查询报文想要的回应是一个规范主机名)等。
*/
public byte[] mrData;
// ...
}
3 DNS请求报文分析
上面解释了DNS请求报文的结构,那么在实际的DNS请求是怎么样的呢?通过Wireshark工具进行查看。以下是使用UDP请求发送的一个DNS报文,解析 www.baidu.com。
请求数据---WireShark
[图片]
响应数据---WireShark
[图片]
3.1 请求 www.baidu.com 的报文十六进制数据
// DNS 报文
09 32 00 00 00 01 00 00 00 00 00 00 03 77 77 77
05 62 61 69 64 75 03 63 6F 6D 00 00 01 00 01
// 逐步分析下
// Header 区域(12字节)
09 32 00 00 00 01 00 00 00 00 00 00
ID:09 32
Flag:00 00 // 请求没有设置各个标志
QuestionCount:00 01 // 包含一个请求问题
AnswerCount:00 00
AuthorityCount:00 00
AdditinalCOunt:00 00
// Question 区域
03 77 77 77 05 62 61 69 64 75 03 63 6F 6D 00 00
01 00 01
domain:03 77 77 77 05 62 61 69 64 75 03 63 6F 6D 00 // 域名的策略上面有介绍
queryType:00 01 // 表示域名解析成IP
queryClass:00 01 // 默认1
3.2 请求 www.baidu.com 的响应十六进制数据
- 请求部分,响应部分和请求部分也差不多,只有部分标志位有变化
09 32 80 80 00 01 00 03 00 00 00 00 03 77 77 77
05 62 61 69 64 75 03 63 6F 6D 00 00 01 00 01
- Answer部分
C0 0C 00 05 00 01 00 00 02 E8 00 0F 03 77 77 77
01 61 06 73 68 69 66 65 6E C0 16 C0 2B 00 05 00
01 00 00 00 1C 00 0E 03 77 77 77 07 77 73 68 69
66 65 6E C0 16 C0 46 00 01 00 01 00 00 00 1C 00
04 67 EB 2E 27
Answer - 1
C0 0C 00 05 00 01 00 00 02 E8 00 0F 03 77 77 77
01 61 06 73 68 69 66 65 6E C0 16
domain: C0 0C // 指向Question中的域名部分
type: 00 05 // 5 - CNAME : 查询规范名称(别名)
class: 00 01 // IN
ttl: 00 00 02 E8 // 744秒
dataLength: 00 0F // 长度15
cname: 03 77 77 77 01 61 06 73 68 69 66 65 6E C0 16 // 域名www.a.shifen.com
Answer - 2
C0 2B 00 05 00 01 00 00 00 1C 00 0E 03 77 77 77
07 77 73 68 69 66 65 6E C0 16
domain: C0 2B // 指向上一个Answer的Cname部分
type: 00 05 // 5 - CNAME : 查询规范名称(别名)
class: 00 01 // IN
ttl: 00 00 00 1C // 28秒
dataLength: 00 0E // 长度14
cname: 03 77 77 77 07 77 73 68 69 66 65 6E C0 16 // 域名www.wshifen.com
Answer - 3
C0 46 00 01 00 01 00 00 00 1C 00 04 67 EB 2E 27
domain: C0 46 // 指向上一个Answer的Cname部分
type: 00 01 // 5 - CNAME : 查询规范名称(别名)
class: 00 01 // IN
ttl: 00 00 00 1C // 28秒
dataLength: 00 0E // 长度14
address: 67 EB 2E 27 // ip 103.235.46.39
最后响应部分解析出了真实的IP地址(其中可能存在多个ip地址)
项目地址:github.com/zPengCode/H…
==代码在dns包中==