python解析DNS数据包实例代码

发布时间:2020-11-24编辑:脚本学堂
本文介绍了python解析dns数据包的一例代码,有关python dns数据解析的方法,常用模块socket、struct、random、domaintobyte的用法,有需要的朋友参考下。

例子,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: 域名-不定长度,以''结尾
        #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] == 'x00x05':#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] == 'x00x01':#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 :         #域名以结束
        return 'x00'
    else :#域名部分
        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:
        formatstr = 'B%ds' % len(cs)
        newsplit = struct.pack(formatstr, len(cs), cs)
        domaintobyte += newsplit    domaintobyte += ''
    #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