例子,python解析DNS数据包。 代码示例:

###file QueryDNS.py##
-*- coding: utf-8 -*-
#Get DNS answer
#详情见RFC 1035
import os, sys
import socket
import struct
import random
from domaintobyte import domaintobyte, bytetodomainDHOST = '208.67.222.222' #DNS

服务器的地址

DPORT = 53   #默认端口是53LHOST = ''
LPORT = 10001#绑定到的本地端口TIMEOUT = 3.0#超时设置为3秒##数据包整体的格式
##    +---------------------+
##    |        Header       |
##    +---------------------+
##    |       Question      | the question for the name server
##    +---------------------+
##    |        Answer       | RRs answering the question
##    +---------------------+
##    |      Authority      | RRs pointing toward an authority
##    +---------------------+
##    |      Additional     | RRs holding additional information
##    +---------------------+def QueryDNS(domain):
TID = random.randint(-32768, 32767)
Flags = 0x0100
Questions = 0x0001
AnswerRRs = 0x0000
AuthorityRRs = 0x0000
AdditionalRRs = 0x0000    TIDCHARS = struct.pack('!h', TID)    domainbyte = domaintobyte(domain)
#TYPE value and meaning
#A    1 a host address
#NS   2 an authoritative name server
#MD   3 a mail destination (Obsolete - use MX)
#MF   4 a mail forwarder (Obsolete - use MX)
#CNAME5 the canonical name for an alias
SEARCHTYPE = 0x0001
#Class 一般为 1 指 Internet
SEARCHCLASS = 0x0001    #构造请求报文
Bufhead = struct.pack('!hhhhhh', TID, Flags, Questions, AnswerRRs, AuthorityRRs, AdditionalRRs)
Buftail = struct.pack('!hh', SEARCHTYPE, SEARCHCLASS)
Buf = Bufhead + domainbyte + Buftail    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.settimeout(TIMEOUT) #设置超时时间
s.
bind((LHOST,LPORT))
s.sendto(Buf,(DHOST, DPORT))
print 'Send [OK]'
try:
data, addr = s.recvfrom(1024)
except socket.timeout:
s.close()
print u'响应超时'
return
s.close()
print 'From', addr
#print 'Receved', repr(data)
if data[0:2] == TIDCHARS:
#Header 部分
#[0:2]数据包最开始两个字节是TID
print 'TID [0k]'
#[2:4]接着是Flags 两个字节
#Flags 的最后4位是错误代码,如果是0000则表示没有错误,否则就有错误
flags = struct.unpack('!h', data[2:4])[0]
errormsg = flags & 0x000F
if errormsg != 0:
print "Error, maybe no such domain"
return
#[4:6]Questions 数 两个字节
#[6:8]Answer RRs数    两个字节
#[8:10]Authority RRs数   两个字节
#[10:12]Additional RRs数   两个字节
answerRRs = struct.unpack('!h', data[6:8])[0]        bitflags = 12; #跳过上面12个字节
#Question 部分
#Name: 域名-不定长度,以'\0'结尾
#Type: 查询类型 两个字节
#Class:一般为1(Internet) 两个字节
while data[bitflags] != '\x00':
bitflags += 1
bitflags += 1 #域名结束        bitflags += 2 #跳过Type
bitflags += 2 #跳过Class
print 'Answers', answerRRs
i = 0#Answer 部分一条记录的格式
#
#   1 1 1 1 1 1
#      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
#    |   |
#    /   /
#    /NAME          /
#    |   |
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
#    |TYPE          |
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
#    |          CLASS          |
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
#    |TTL|
#    |   |
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
#    |        RDLENGTH         |
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
#    /          RDATA          /
#    /   /
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+        while i < answerRRs:
#Name 两个字节 指针-指向域名的字符
bitflags += 2
if data[bitflags:bitflags+2] == '\x00\x05':#CNAME
#Type 两个字节
#Class两个字节
#TTL 四个字节
bitflags += 8
rdatalength = struct.unpack('!h', data[bitflags:bitflags+2])[0]
#Data length 两个字节
bitflags += 2
#得到rdata中的全部域名
fullRecord = GetFullName(bitflags, data)
bitflags += rdatalength
print 'CNAME:', bytetodomain(fullRecord)
elif data[bitflags:bitflags+2] == '\x00\x01':#A(Host Address)
#Type 两个字节
#Class两个字节
#TTL 四个字节
bitflags += 8
rdatalength = struct.unpack('!h', data[bitflags:bitflags+2])[0]
#Data length 两个字节
bitflags += 2
#A记录的RDatalength = 4
iptuple = struct.unpack('!BBBB', data[bitflags:bitflags+4])
ipstr = '%d.%d.%d.%d' % iptuple
bitflags += rdatalength
print 'IP:', ipstr i += 1 #CNAME记录中域名并不是全部存储在rdata中,和Query或者其他包含域名部分有重复的部分是用一个指针,指向数据包的一个地址,这样就节省了存储空间
#offset是相对数据包头部的偏移量
#两个字节 16bit如下图所示
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
#    | 1 1|     OFFSET        |
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
#比如
#Question部分中的查询的是www.sina.com.cn
#CNAME记录中的域名是jupiter.sina.com.cn,只用在rdata中存储"jupiter",接下来的部分用一个指针指向Question部分中的"sina.com.cn"
#CNAME中的rdata
#    +--+--+--+--+--+--+--+--+--+--+
#    |07| j| u| p| i| t| e| r|c0|10|
#    +--+--+--+--+--+--+--+--+--+--+
#最后两个字节就是一个指针,指向0x10这个地址
def GetFullName(offset, Buf):
fullRecord = ''
oneChar = struct.unpack('!B', Buf[offset:offset+1])[0]
#print oneChar
if oneChar & 0xc0 == 0xc0 : #指针
jump = struct.unpack('!h', Buf[offset:offset+2])[0]
jump = jump & 0x3FFF    #指针指向的地址
fullRecord += GetFullName(jump, Buf)
elif oneChar == 0 :         #域名以\0结束
return '\x00'
e
lse :#域名部分
fullRecord += Buf[offset:offset+oneChar+1]
fullRecord += GetFullName(offset+oneChar+1, Buf)
return fullRecord
if __name__ == "__main__":
if len(sys.argv) < 2:
print "Usage:QueryDNS.py www.google.cn"
else:
domain = sys.argv[1]
QueryDNS(domain)
###file domaintobyte.py####domaintobyte 代码示例:
#www.google.cn => 03www06google02cn00
#bytetodomain
#03www06google02cn00 => www.google.cn
import sys
import structdef domaintobyte(domain):    #print 'old dimain', domain    domaintobyte = ''    dsplit = domain.split('.')    for cs in dsplit:
fo
rmatstr = 'B%ds' % len(cs)
newsplit = struct.pack(formatstr, len(cs), cs)
domaintobyte += newsplit    domaintobyte += '\0'
#print 'new domain', domaintobyte
#print repr(domaintobyte)
return domaintobytedef bytetodomain(str):
domain = ''
i = 0
length = struct.unpack('!B', str[0:1])[0]
while length != 0 :
i += 1
domain += str[i:i+length]
i += length
length = struct.unpack('!B', str[i:i+1])[0]
if length != 0 :
domain += '.'
return domain
#Python