DNS message解析
案例吐个槽,命苦啊,要自己动手解包。
另外,这里的内容是半路找来的,如果有冲突,自行翻阅rfc1035。我还没校正过。
The Structure
如下图:
所有的DNS message都包含了下面这几个部分:
1、HEADER。基本上HEADER都是些概述啥的。
2、QUESTION。描述你的请求,比如,www.qkdemo.com的IP是多少。
3、Answer。描述你的应答,比如,www.qkdemo.com的IP是1.2.3.4。
4、不知道干吗的。
5、继续不知道。
DNS HEADER部分
首先,需要明确一下DNS的message的结构:
如上图所示,不管是DNS的query还是response,DNS message都分为这么几个部分:
1、上图每行表示一个16bit的数,也就是两个byte。
2、ID是这个message的编号,response会把query的id给copy过来,好让请求的program识别response是哪一个。一个16bit的随机数。
3、ID之后的flag是一个组合的双字节,包括了:
- QA.1:message的类型,0是query,1是response
- Opcode.4:query的类型。0000表示是一个标准的query。
- AA.1:没看懂是干嘛的,但是貌似对我没用。设0是没问题的。
- TC.1:继续设0。
- RD.1:Recursion Desired,query是否请求递归,1表示请求,0则不需要。继续设0是可以的,bonjour里面是这么干的。
- RA.1:Recursion Available,递归是否可用。和上面这个是有关系的,但是是response里面带来的。
- Z.3:保留字段。所有的保留字段都是渣。不过,反正不是标准请求的,你可以自己爱怎么玩怎么玩。
- RCODE.4:这个是response必须要要带的一部分。0表示没有错误,1表示query格式错了,2表示server自己出问题了,3表示name没找到,4表示query的类型server没法处理,5表示拒绝进行解析。
- 所以,我的心得是,对于一个标准的query,设0x0000就好了;对于一个标准的response,设0x8000也没问题。
4、我带了几个请求。设成1表示只有一个。恩,我们就设1就好了。
5、我带了几个answer。0表示我带了0个解答。
6、服务器上有几条记录?我没搞懂。但是,可以忽略这个字段的值。
7、有几条额外的记录?继续搞不懂。好吧,继续忽略。
Question描述域
Question分为三个部分,如图:
具体讲来:
1、QNAME:这里是要请求的域名的描述。以上面www.qkdemo.com为例,描述为3www6qkdemo3com00;如果是qkdemo.local,则为6qkdemo5local00。
2、QTYPE:请求的类型。A记录是0x0001。Bonjour里面query的类型是0x00ff,wireshark解释是any。
3、QCLASS:请求的类。Bonjour里面用的0x8001,而且wireshark是分成两部分来解读的,大概是1+7,前面是query,后面是class。0x0001表示internet address。
Answer域
Answer域稍微复杂一点,有6个域:
1、NAME,和上面QNAME的描述方式是一样的。
2、TYPE,和上面QTYPE含义是一样的。局域网里胡乱收来的一个包把这个域设为0x00,不知道搞飞机。
3、同QCLASS。
4、TTL,这个结果灰白cache多久。
5、下面的RDATA有多长。
6、这个域的描述方法根据class不同。比如,如果class是一个A记录,表示地址,那么它就是一个IP地址;如果是0x000f,表示是一个邮件服务器,那么,它包含了preference和cname两个部分。
DNS message的压缩
作为互联网使用最频繁的机制之一,所占我们网络日常开销的比重是相当相当大滴!所以,需要考虑怎么来减小message的大小。这里的压缩主要是减少重复信息。
使用压缩的主要有三种内容:QNAME,NAME,RDATA。
压缩的方式也很简单:保存一个指针,指向这一数据第一次出现的地址。比如,假设我们某query携带三个Question,而有两个Question的QNAME是一样的,那么第二个QNAME的位置,就会放着一个指向第一个QNAME的指针。
该指针占了16bit的空间,而且,前两个bit必须是11。因为域名的每个部分必须在63 Byte以内,所以,域名每个部分长度(label,www.qkdemo.com有三个部分,就有三个label)写作0x0000格式时,它的最高两位不会是11,这样,pointer和label就被区别开来。至于offset,offset为零,代表了header中ID部分的第一个Byte,后面的一次计算。
另外需要注意的是,如果我们在RDATA中使用了压缩,那么,我们在计算RDATA长度时,使用的将是2,即一个Pointer的长度,而不是域名本来的长度。
举例,我们先假设忽略message里面的其它域,假设我们先后使用了F.ISI.ARPA,FOO.F.ISI.ARPA和ARPA三个域名,那么,我们可以将message压缩:
如上,F.ISI.ARPA是从20位置开始,那么FOO.F.ISI.ARPA就可以表示为3F 00 E0(E0是0b1110 0000),而ARPA则可以表示为E6。需要注意的是,一个比较容易忽视的问题,offset是从0开始的,所以清点byte的个数后记得减一。
一个response的例子
暂缺,重启后有心情再添加。
其它
multiCast DNS的文档是rfc6762,可查阅:http://tools.ietf.org/html/rfc6762