对DNS应答报文的解析及简单处理代码

来源:互联网 发布:centos 7搭建html 编辑:程序博客网 时间:2024/06/06 03:24

这几天因为要做一个事情,又因为没有现成的工具使用,一直在找DNS报文的相关资料,最终在国外找到一篇关于DNS的MX查找的代码,但是发现直接将它套用到A记录查询并不合适,只好继续找,直到昨晚,找到了。

关于DNS发送报文的更多信息请查阅《tcp/ip卷一》 dns域名系统这一章,这里只说对DNS应答报文的一点解析处理。

DNS报文结构如下:

+---------------------------+---------------------------+
| 标识 (最重要的 :) | 参数 |
+---------------------------+---------------------------+
| 问题数 | 回答数 |
+---------------------------+---------------------------+
| 管理机构数 | 附加信息数 |
+---------------------------+---------------------------+
| |
/ /
/ 问题 /
| |
+-------------------------------------------------------+
| |
/ /
 //文章出处:网络技术论坛(http://bbs.nettf.net) 作者:小金
/ 回答 /
| |
+-------------------------------------------------------+
| |
/ /
/ 附加信息(无关紧要) /
| |
+-------------------------------------------------------+



typedef struct dns_header //DNS数据报:

{
unsigned short id; //标识,通过它客户端可以将DNS的请求与应答相匹配;

unsigned short flags; //标志:[ QR | opcode | AA| TC| RD| RA | zero | rcode ]

unsigned short quests; //问题数目;

 //文章出处:网络技术论坛(http://bbs.nettf.net) 作者:小金
unsigned short answers; //资源记录数目;

unsigned short author; //授权资源记录数目;

unsigned short addition; //额外资源记录数目;

}DNS_HEADER; //在16位的标志中:QR位判断是查询/响应报文,opcode区别查询类型,AA判断是否为授权回答,TC判断

//是否可截断,RD判断是否期望递归查询,RA判断是否为可用递归,zero必须为0,rcode为返回码字段。


typedef struct dns_query //DNS查询数据报:

{
unsigned short type; //查询类型,大约有20个不同的类型

unsigned short classes; //查询类,通常是A类既查询IP地址。

}DNS_QUERY;

 //文章出处:网络技术论坛(http://bbs.nettf.net) 作者:小金
typedef struct dns_response //DNS响应数据报:

{
unsigned short name; //查询的域名

unsigned short type; //查询类型

unsigned short classes; //类型码

unsigned int ttl; //生存时间

unsigned short length; //资源数据长度

struct in_addr addr; //资源数据length字节...

}DNS_RESPONSE;

在VB里,简单的DNS头部声明如下:
Public Type DNS_HEADER

 //文章出处:网络技术论坛(http://bbs.nettf.net) 作者:小金
qryID As Integer

options As Byte

Response As Byte

qdcount As Integer

ancount As Integer

nscount As Integer

arcount As Integer

End Type

当我们要发送类型为A的DNS查询请求时,需要这样构造头部:

dnsHead.qryID = htons(&H11DF)

 //文章出处:网络技术论坛(http://bbs.nettf.net) 作者:小金
dnsHead.options = 1

dnsHead.qdcount = htons(1)

dnsHead.ancount = 0

dnsHead.nscount = 0

dnsHead.arcount = 0

但是头部里并不包含查询类型,查询类型是在DNS报文的主体数据里的,这个部分请参考以下代码:

Dim dnsQuery() As Byte

Dim sQName As String

Dim dnsQueryNdx As Integer

Dim iTemp As Integer

 //文章出处:网络技术论坛(http://bbs.nettf.net) 作者:小金
Dim iNdx As Integer

dnsQueryNdx = 0

ReDim dnsQuery(4000)

' Setup the dns structure to send the query in


' First goes the DNS header information


MemCopy dnsQuery(dnsQueryNdx), dnsHead, 12

dnsQueryNdx = dnsQueryNdx + 12

' Then the domain name (as a QNAME)


sQName = MakeQName(Domain_Addr)
 //文章出处:网络技术论坛(http://bbs.nettf.net) 作者:小金

iNdx = 0

While (iNdx < Len(sQName))

dnsQuery(dnsQueryNdx + iNdx) = Asc(Mid(sQName, iNdx + 1, 1))

iNdx = iNdx + 1

Wend

dnsQueryNdx = dnsQueryNdx + Len(sQName)

' Null terminate the string


dnsQuery(dnsQueryNdx) = &H0

dnsQueryNdx = dnsQueryNdx + 1

 //文章出处:网络技术论坛(http://bbs.nettf.net) 作者:小金
' The type of query (15 means MX query)


iTemp = htons(1)

MemCopy dnsQuery(dnsQueryNdx), iTemp, Len(iTemp)

dnsQueryNdx = dnsQueryNdx + Len(iTemp)

' The class of query (1 means INET)


iTemp = htons(1)

MemCopy dnsQuery(dnsQueryNdx), iTemp, Len(iTemp)

dnsQueryNdx = dnsQueryNdx + Len(iTemp)

On Error Resume Next

 //文章出处:网络技术论坛(http://bbs.nettf.net) 作者:小金
ReDim Preserve dnsQuery(dnsQueryNdx - 1)

发送DNS查询报文的具体步骤这里不予描述,好了,现在我们已经发送了一个DNS查询,当它返回应答的时候,该怎么处理呢?

当回应正常时,DNS使用以下格式返回数据:
事务ID | 应答类型 | …… | 资源授权1 | …… | 资源授权2 |

以查询www.nettf.net为例,返回的报文结构是:
'========DNS应答头部==========
'dnsReply(0) = 17 '事务ID
'dnsReply(1) = 223 '事务ID
'dnsReply(2) = 129 'DNS应答标志位0x81
'dnsReply(3) = 128 'DNS返回标志位0x80 'DNS-Flag(128-Found,131-No such name,133-refused)
'dnsReply(4) = 0
'dnsReply(5) = 1 'Questions
'dnsReply(6) = 0
'dnsReply(7) = 1 'Answer RRs
'dnsReply(8) = 0 'Autho-RRs
'dnsReply(9) = 0
'dnsReply(10) = 0 'Addit-RRs
 //文章出处:网络技术论坛(http://bbs.nettf.net) 作者:小金
'dnsReply(11) = 0 '结束/0
'========DNS资源授权1开始========
'dnsReply(12) = 3 'DNS计数3
'dnsReply(13) = 119(w) 'DNS
'dnsReply(14) = 119(w) 'DNS
'dnsReply(15) = 119(w) 'DNS
'dnsReply(16) = 5 'DNS计数5
'dnsReply(17) = 110(n) 'DNS
'dnsReply(18) = 101(e) 'DNS
'dnsReply(19) = 116(t) 'DNS
'dnsReply(20) = 116(t) 'DNS
'dnsReply(21) = 102(f) 'DNS
'dnsReply(22) = 3 'DNS计数3
'dnsReply(23) = 110(n) 'DNS
'dnsReply(24) = 101(e) 'DNS
'dnsReply(25) = 116(t) 'DNS
'dnsReply(26) = 0 '结束/0
'dnsReply(27) = 0
'dnsReply(28) = 1
'dnsReply(29) = 0
 //文章出处:网络技术论坛(http://bbs.nettf.net) 作者:小金
'dnsReply(30) = 1
'========DNS资源授权2开始========
'dnsReply(31) = 192 '授权标志位0xC0
'dnsReply(32) = 12 'IP计数12
'dnsReply(33) = 0 '结束/0
'dnsReply(34) = 1
'dnsReply(35) = 0
'dnsReply(36) = 1
'dnsReply(37) = 0
'dnsReply(38) = 0
'dnsReply(39) = 4
'dnsReply(40) = 242
'dnsReply(41) = 0
'dnsReply(42) = 4
'dnsReply(43) = 58 'IP地址1
'dnsReply(44) = 49 'IP地址2
'dnsReply(45) = 58 'IP地址3
'dnsReply(46) = 88 'IP地址4
'=================================

 //文章出处:网络技术论坛(http://bbs.nettf.net) 作者:小金
在DNS资源部分,使用/0(Null)作为结束符,而IP资源部分则放到最后,使用标志位0xC0表示IP资源段开始,其后一位指向IP第一位的偏移量。

所以我粗略声明了一个DNS应答结构体:
Public Type DNS_ANSWER
Response As Integer
DomainName As String
DomainIP As String
End Type

然后……大家就自己看代码吧,代码说明一切,嘿嘿嘿……(其实是最近比较忙,没闲工夫写这些了-_-这个也是匆匆忙忙分析出来的,如有错误还望指正)

Function DecodeDNSAnswer(dnsReply() As Byte) As DNS_ANSWER

Dim i As Integer
Dim iPkgCounter As Integer
Dim iPkgLen As Integer
Dim bDomainEndFlag As Boolean

iPkgCounter = 0
iPkgLen = 0
 //文章出处:网络技术论坛(http://bbs.nettf.net) 作者:小金
bDomainEndFlag = False

If Hex(dnsReply(2)) <> 81 Then '判断DNS应答标志位0x81

DecodeDNSAnswer.Response = 0
DecodeDNSAnswer.DomainIP = "0.0.0.0"
DecodeDNSAnswer.DomainName = "0.0.0.0"
Exit Function
Else
End If

DecodeDNSAnswer.Response = Hex(dnsReply(3))

Select Case DecodeDNSAnswer.Response '根据DNS返回标志位选择处理

Case 80 '正常应答

'先取DNS应答的Domain部分

'取授权部分DNS第一计数位,从第13个封包(dnsReply(12))开始
 //文章出处:网络技术论坛(http://bbs.nettf.net) 作者:小金

iPkgCounter = 12 '初始化指针-->dnsReply(12)

Do Until bDomainEndFlag = True
iPkgLen = CInt(dnsReply(iPkgCounter)) '获取这个包的内容(DNS计数开始)

For i = iPkgCounter + 1 To (iPkgCounter + iPkgLen)
DecodeDNSAnswer.DomainName = DecodeDNSAnswer.DomainName & Chr(dnsReply(i))
Next i
If CInt(dnsReply(i)) = 0 Then '资源结束标志位

iPkgCounter = i
bDomainEndFlag = True
Else
DecodeDNSAnswer.DomainName = DecodeDNSAnswer.DomainName & "."
iPkgCounter = i
End If
Loop

'现在来取DNS应答的IP部分
 //文章出处:网络技术论坛(http://bbs.nettf.net) 作者:小金

'iPkgCounter当前指向了越过Domain后的封包

'开始查找授权标志位0xC0

Do Until dnsReply(iPkgCounter) = 192
iPkgCounter = iPkgCounter + 1
Loop

'下一位是IP计数位

iPkgLen = CInt(dnsReply(iPkgCounter + 1)) '获取这个包的内容(IP记录偏移量计数开始)

iPkgCounter = iPkgCounter + iPkgLen

For i = iPkgCounter To UBound(dnsReply)
DecodeDNSAnswer.DomainIP = DecodeDNSAnswer.DomainIP & dnsReply(i)
If i < UBound(dnsReply) Then DecodeDNSAnswer.DomainIP = DecodeDNSAnswer.DomainIP & "."
Next i

 //文章出处:网络技术论坛(http://bbs.nettf.net) 作者:小金
Case 83 '无此域名

DecodeDNSAnswer.DomainIP = "0.0.0.0"
DecodeDNSAnswer.DomainName = "0.0.0.0"
Exit Function
Case 85 '授权拒绝

DecodeDNSAnswer.DomainIP = "0.0.0.0"
DecodeDNSAnswer.DomainName = "0.0.0.0"
Exit Function
Case Else
DecodeDNSAnswer.Response = 0
DecodeDNSAnswer.DomainIP = "0.0.0.0"
DecodeDNSAnswer.DomainName = "0.0.0.0"
Exit Function
End Select

End Function

原创粉丝点击