python发送二层报文_python 实现dns 解析发送接收报文

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 的未实现。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值