Chinaunix首页 | 论坛 | 博客
  • 博客访问: 335393
  • 博文数量: 104
  • 博客积分: 2815
  • 博客等级: 少校
  • 技术积分: 595
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-06 16:32
文章分类

全部博文(104)

文章存档

2013年(1)

2012年(2)

2011年(21)

2010年(80)

我的朋友

分类: 系统运维

2010-05-06 16:41:52

DNS 是域名解析协议,服务端实现很多,不过很多时候我需要的客户端程序。用dns来解析IP在很多地方都会,他很重要,但经常被刻意的忽略。这里记录一些我自 己实现dns客户端的一些资料和过程。

1、 rfc版本

我先附上主要参考的rfc版本。

2、研究过程

使用wireshark跟踪port 53端口,然后ping 记录下发送和接收的报文。

3、解码代码

$(wireshark_src)\epan\dissectors\packet-dns.c 中dns_get_name函数

所有的 DNS,不论是发送和接收到,都遵循相同的消息格式,如

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

其中消息头部的格式比较固定,如下:

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

在填充和解析报文的时候,必须注意,这里报文所有的数据 格式都是大端格式,而且协议描述的flag部分非常容易出错,必须予以强调。

假设QR=1,其他为0,那么这个字节的值为0x80;如果RD=1,而其他为0,那么这个字节的值为0x01。我们假 设QR字段到RCODE字段的这两个字节中,QR=1,RD=1,RC=1,其他部分都是0,那么这2个字节的值为0x81 , 0x80。

16bit的标志字段 如下:

QR:0 表示查询报文,1表示响应报文
Opcode:通常值为0(标准查询),其他值为1(反向查询)和2(服务器状态请求)。
AA:表示授权回 答(authoritative answer).
TC:表示可截断的(truncated)
RD:表示期望递归
RA:表示可用 递归
随后3bit必须为0
Rcode:返回码,通常为0(没有差错)和3(名字差错)
后面4个16bit字段说明最后4个变长字 段中包含的条目数。

仔细理解 上面这段描述,才能理解各个标志位的含义。

HEADER 中的QDCOUNT只是question节的数量,ANCOUNT为answer节的数量,NSCOUNT为auth节的数量,ARCOUNT为 addition节的数量。这几个2字节构成的整型也是大端格式。

question主要是由客户端请求来填的,他的格式为

  1. /* QUESTION FORMAT */  
  2. /* 
  3.                                     1  1  1  1  1  1 
  4.       0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5 
  5.     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 
  6.     |                                               | 
  7.     /                     QNAME                     / 
  8.     /                                               / 
  9.     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 
  10.     |                     QTYPE                     | 
  11.     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 
  12.     |                     QCLASS                    | 
  13.     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 
  14. */  

一般来说QTYPE如果是域名解析的话,为T_A =1 ,当然也有T_MX(mail exchange)。在所以名称字段,比较特殊,比如这里的QNAME,其他以字符串形式出现的都是一样的规则。

比如,他会编成这样的形式:

0x03 0x77 0x77 0x77  0x04 0x63 0x73 0x64 0x6e 0x03 0x6e 0x65 0x74 0x00

和其他字符串一样,这个格式仍然以0x00结尾,不过中间确实不同,他是len + data来混合编码,每个字符串都是以长度开始,然后接着内容。比如"www",他的字符值为0x77 , 在前头是0x03开始,表示后面紧跟着3个字符。

还有一种格式,是使用指针,准确地说应该是偏移量,如下:

0xc0 0x0c 0x00 0x01 0x00 0x01 0x00 0x00 0x02 0x1a 0x00 0x04

这个是DNS服务端返回报文中地一 段。0xc0开头,表示这个是指针,后面字节跟着地是偏移量。这个偏移量应该是这样计算的:0xc0 & (~0xc0),这样得到偏移量的高字节,然后和0x0c拼接成一个大端格式短整型。这偏移量是相对于报文起点的偏移量。

我打个比方,比如偏移量超过256,是300 ,他的小端格式为0x012c,那么他在内存中的印象应该是这样的:

0xc1 0x2c。

ANSWER/AUTH/ADDS的几个报文格式都是一样:

  1. /* ANSWER/AUTH/ADDI FORMAT */  
  2. /* 
  3.                                     1  1  1  1  1  1 
  4.       0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5 
  5.     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 
  6.     |                                               | 
  7.     /                                               / 
  8.     /                      NAME                     / 
  9.     |                                               | 
  10.     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 
  11.     |                      TYPE                     | 
  12.     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 
  13.     |                     CLASS                     | 
  14.     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 
  15.     |                      TTL                      | 
  16.     |                                               | 
  17.     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 
  18.     |                   RDLENGTH                    | 
  19.     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--| 
  20.     /                     RDATA                     / 
  21.     /                                               / 
  22.     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 
  23. */  

只需要关注的是,每个TYPE都会有自己格式填充在 RDATA中,RDATA实际上存放的是一个缓冲区。根据具体TYPE,来解析他的含义,一般说来,我们比较关注的A/CNAME/MX等格式,其他都可 以忽略。

linux下提供 res_query可以发送请求报文,但返回内容却需要自己解析,没发意思,跟自己写个库都差不多了。解析部分可以参看wireshark,做的很全。

win32也有个函数,不过也差不多。按道理ping应 该会有相应的代码,可惜我没有找到相关。最紧密的是gethostbyname,似乎不能解析吧,没深入研究。

阅读(816) | 评论(0) | 转发(1) |
0

上一篇:没有了

下一篇:Ubuntu中乱码完全解决方案

给主人留下些什么吧!~~