Python 从特定源接收UDP数据包
我试图测量DNS服务器的响应。对小于512字节的典型DNS响应进行嗅探并不是什么大问题。我的问题是接收3000多字节的大响应—在某些情况下是5000多字节。我还没有找到一个能可靠接收数据的套接字。pythonsockets有没有办法从特定的源地址接收数据 以下是我到目前为止的情况:Python 从特定源接收UDP数据包,python,sockets,networking,dns,Python,Sockets,Networking,Dns,我试图测量DNS服务器的响应。对小于512字节的典型DNS响应进行嗅探并不是什么大问题。我的问题是接收3000多字节的大响应—在某些情况下是5000多字节。我还没有找到一个能可靠接收数据的套接字。pythonsockets有没有办法从特定的源地址接收数据 以下是我到目前为止的情况: import socket import struct def craft_dns(Qdns): iden = struct.pack('!H', randint(0, 65535)) QR_t
import socket
import struct
def craft_dns(Qdns):
iden = struct.pack('!H', randint(0, 65535))
QR_thru_RD = chr(int('00000001', 2)) # '\x01'
RA_thru_RCode = chr(int('00100000', 2)) # '\x00'
Qcount = '\x00\x01' # question count is 1
ANcount = '\x00\x00'
NScount = '\x00\x00'
ARcount = '\x00\x01' # additional resource count is 1
pad = '\x00' #
Rtype_ANY = '\x00\xff' # Request ANY record
PROtype = '\x00\x01' # Protocol IN || '\x00\xff' # Protocol ANY
DNSsec_do = chr(int('10000000', 2)) # flips DNSsec bit to enable
edns0 = '\x00\x00\x29\x10\x00\x00\x00\x00\x00\x00\x00' # DNSsec disabled
domain = Qdns.split('.')
quest = ''
for x in domain:
quest += struct.pack('!B', len(x)) + x
packet = (iden+QR_thru_RD+RA_thru_RCode+Qcount+ANcount+NScount+ARcount+
quest+pad+Rtype_ANY+PROtype+edns0) # remove pad if asking <root>
return packet
def craft_ip(target, resolv):
ip_ver_len = int('01000101', 2) # IPvers: 4, 0100 | IP_hdr len: 5, 0101 = 69
ipvers = 4
ip_tos = 0
ip_len = 0 # socket will put in the right length
iden = randint(0, 65535)
ip_frag = 0 # off
ttl = 255
ip_proto = socket.IPPROTO_UDP # dns, brah
chksm = 0 # socket will do the checksum
s_addr = socket.inet_aton(target)
d_addr = socket.inet_aton(resolv)
ip_hdr = struct.pack('!BBHHHBBH4s4s', ip_ver_len, ip_tos, ip_len, iden,
ip_frag, ttl, ip_proto, chksm, s_addr, d_addr)
return ip_hdr
def craft_udp(sport, dest_port, packet):
#sport = randint(0, 65535) # not recommended to do a random port generation
udp_len = 8 + len(packet) # calculate length of UDP frame in bytes.
chksm = 0 # socket fills in
udp_hdr = struct.pack('!HHHH', sport, dest_port, udp_len, chksm)
return udp_hdr
def get_len(resolv, domain):
target = "10.0.0.3"
d_port = 53
s_port = 5353
ip_hdr = craft_ip(target, resolv)
dns_payload = craft_dns(domain) # '\x00' for root
udp_hdr = craft_udp(s_port, d_port, dns_payload)
packet = ip_hdr + udp_hdr + dns_payload
buf = bytearray("-" * 60000)
recvSock = socket.socket(socket.PF_PACKET, socket.SOCK_RAW, socket.ntohs(0x0800))
recvSock.settimeout(1)
sendSock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
sendSock.settimeout(1)
sendSock.connect((resolv, d_port))
sendSock.send(packet)
msglen = 0
while True:
try:
pkt = recvSock.recvfrom(65535)
msglen += len(pkt[0])
print repr(pkt[0])
except socket.timeout as e:
break
sendSock.close()
recvSock.close()
return msglen
result = get_len('75.75.75.75', 'isc.org')
print result
一无所获。因为我使用的是SOCK_RAW,所以上面的代码不太理想,但它可以正常工作。如果套接字非常嘈杂(比如在WLAN上),我最终可能会收到远远超出DNS数据包的数据包,因为我无法知道在接收多数据包DNS应答时何时停止接收数据包。对于一个安静的网络,如实验室虚拟机,它可以工作
在这种情况下,有没有更好的方法使用接收插座?
显然,从代码来看,我对Python套接字没有那么强。我必须使用SOCK_RAW发送,因为我正在以原始格式构造数据包。如果使用SOCK_DGRAM,则在发送到DNS解析程序时,自定义数据包的格式将不正确
我能看到的唯一方法是使用原始套接字接收器(recvSock.recv或recvfrom)并解压缩每个数据包,查看源地址和目标地址是否在get_len()中提供的内容内匹配,然后查看片段位是否翻转。然后用len()记录每个数据包的字节长度。我宁愿不那样做。似乎有更好的办法 好吧,我很愚蠢,没有查看接收套接字的协议。当您试图通过IPPROTO_原始协议接收数据包时,套接字会变得有点不稳定,所以我们确实需要两个套接字。通过更改为IPPROTO_UDP并绑定它,套接字能够跟踪多个请求的完整DNS响应。我去掉了try/catch和while循环,因为它不再是必需的,并且我能够用这个块拉取响应长度:
recvSock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_UDP)
recvSock.settimeout(.3)
recvSock.bind((target, s_port))
sendSock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
#sendSock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sendSock.settimeout(.3)
sendSock.bind((target, s_port))
sendSock.connect((resolv, d_port))
sendSock.send(packet)
pkt = recvSock.recvfrom(65535)
msglen = len(pkt[0])
现在,该方法将返回从DNS查询接收的确切字节。我将保留此选项,以防其他人需要执行类似操作:)
recvSock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_UDP)
recvSock.settimeout(.3)
recvSock.bind((target, s_port))
sendSock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
#sendSock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sendSock.settimeout(.3)
sendSock.bind((target, s_port))
sendSock.connect((resolv, d_port))
sendSock.send(packet)
pkt = recvSock.recvfrom(65535)
msglen = len(pkt[0])