python 實現dns 解析發送接收報文
阿新 • • 發佈:2019-04-17
抓包 esp fse recvfrom unp 算法 rom 是把 head
http://www.qingruxu.com/code/python/851.html
https://tools.ietf.org/html/rfc1035
裏面的圖不一定正確,可以使用抓包軟件來進行分析。
# Resource record format # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ # | | # / / # / NAME / # | | # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ # | TYPE | # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ # | CLASS | # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ # | TTL | # | | # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ # | RDLENGTH | # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--| # / RDATA / # / / # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
這裏的 TYPE CLASS 應該是各占8個字節。 這裏卻畫錯了。
抓包軟件推薦 Wireshark 挺好用的。
#coding:utf-8 import socket import struct #dns 服務器地址 ip = ‘8.8.8.8‘ port = 53 #創建一個dup通訊 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # rfc1035 # format # +---------------------+ # | Header | # +---------------------+ # | Question | the question for the name server # +---------------------+ # | Answer | RRs answering the question # +---------------------+ # | Authority | RRs pointing toward an authority # +---------------------+ # | Additional | RRs holding additional information # +---------------------+ # # header # 1 1 1 1 1 1 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ # | ID | # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ # |QR| Opcode |AA|TC|RD|RA| Z | RCODE | # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ # | QDCOUNT | # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ # | ANCOUNT | # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ # | NSCOUNT | # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ # | ARCOUNT | # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ request_id = 65535 #2個Byte 長度無符號數 header = struct.pack(‘!HBBHHHH‘, request_id, 1, 0, 1, 0, 0, 0) #question = struct.pack("!B5sB3sBHH",5,"baidu",3,"com",0,1,1) #這裏是原生格式 就是數一下有幾個字母 #格式要求是5baidu3com011 最後的11是2個字節的寬度 #QTYPE 1 是 A記錄 #QCLASS 默認都是1 question = "" domain = "baidu.com" #用算法實現上面的格式 for element in domain.split("."): question += struct.pack("!B",len(element)) + element question += struct.pack("!BHH",0,1,1) dns_req = header + question sock.sendto(dns_req,(ip,port)) resp_data,(resp_addr,resp_port) = sock.recvfrom(512) # Resource record format # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ # | | # / / # / NAME / # | | # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ # | TYPE | # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ # | CLASS | # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ # | TTL | # | | # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ # | RDLENGTH | # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--| # / RDATA / # / / # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ # header (resp_request_id,resp_flag,resp_qdcount,resp_ancount,resp_nscount,resp_arcount) = struct.unpack("!HHHHHH",resp_data[:12]) #檢測request_id 和 RQ RCODE if resp_request_id == request_id: #RQ 是15位 if resp_flag == resp_flag | 1<<15: #RCODE 必須是0 這裏與上 0 和原來的值做比較 if resp_flag == resp_flag & ~0xf: #返回記錄數大於0 查詢記錄數大於0 if 0 < resp_qdcount and 0 < resp_ancount: #減去header 6個字節 record = resp_data[12:] #減去問題 返回問題和發送問題一樣 record = record[len(question):] (offset,type,rdclass,ttl,rdlen,ip1,ip2,ip3,ip4) = struct.unpack("!HHHLHBBBB",record[:struct.calcsize("!HHHLHBBBB")]) print "{0}.{1}.{2}.{3}".format(ip1,ip2,ip3,ip4) 主要的就是把數據打包成網絡二進制流,使用 struct 非常方便。這裏僅實現了對 A 記錄的解析,CNAME 的未實現。
python 實現dns 解析發送接收報文