在上一篇中,使用scrapy修改源IP发送请求的最后我们提到由于hosts文件不支持正则,会导致我们的随机域名DNS查询失败。使用DNS代理服务器可以解决这个问题,
下面是我用gevent写的小工具,很简单。我们只拦截匹配的A记录,然后发送DNS Response,如果不匹配,那么我们服务器就是一个DNS代理,转发请求。
# -*- coding=utf-8 -*- import struct from cStringIO import StringIO from collections import namedtuple from gevent import socket from gevent.server import DatagramServer Hex = lambda x : ‘0x{0:04x}‘.format(x) # Hex(256) => "0x0100" QueryResult = namedtuple("DnsQuery", "transactionID,flags,questions,answerRrs authorityRrs,additionalRrs,qname,qtype,qclass" ) LOCALDNS = ("114.114.114.114",53) Hosts = { "*.ttt.com":"173.194.127.144", # google ip } def preg_match(preg,real): """ only support ‘*‘ >>>preg_match("www.*.test*.com","www.python.test.com") True >>>preg_match("www.*.test*.com","www.python.tes.com") False """ pre = 0 for s in preg.split(‘*‘): now = real.find(s) if now < pre: return False pre = now +len(s) return True def udp_send(address,data): sock = socket.socket(type=socket.SOCK_DGRAM) sock.connect(address) sock.send(data) response, address = sock.recvfrom(8192*4) return response,address class DnsParser: @classmethod def parseQuery(self,query): """ 6a 02 01 00 00 01 j..... 00 00 00 00 00 00 03 77 77 77 03 61 61 61 03 63 .......www.aaa.c 6f 6d 00 00 01 00 01 om..... dns query package like above 03 77 77 77 : three www """ transactionID,flags,questions,answerRrs,authorityRrs,additionalRrs = map(Hex,struct.unpack("!6H",query[:12])) quries = StringIO(query[12:]) c = struct.unpack("!c",quries.read(1))[0] domain = [] while c != ‘\x00‘: n = ord(c) domain.append(‘‘.join(struct.unpack("!%sc" % n,quries.read(ord(c))))) c = struct.unpack("!c",quries.read(1))[0] domain = ‘.‘.join(domain) qtype,qclass = map(Hex,struct.unpack("!2H",quries.read())) return QueryResult(transactionID,flags,questions,answerRrs, authorityRrs,additionalRrs,domain,qtype,qclass) @classmethod def generateReqponse(self,queryData,ip): """ only support ipv4 """ return ‘‘.join([queryData[:2],"\x81\x80\x00\x01\x00\x02\x00\x00\x00\x00", queryData[12:],"\xc0\x0c","\x00\x01","\x00\x01","\x00\x00\x00\x1e","\x00\x04", struct.pack(‘BBBB‘,*map(int,ip.split(‘.‘))) ]) class DnsServer(DatagramServer): def handle(self,data,address): query = DnsParser.parseQuery(data) print "get dns query from %s,query:%s" %(str(address),str(query)) find = False for preg,ip in Hosts.iteritems(): if preg_match(preg,query.qname): find = True break if find and query.qtype == "0x0001": #only handle A record print ‘domain:%s in hosts‘ % query.qname response = DnsParser.generateReqponse(data,ip) self.socket.sendto(response,address) else: print ‘transfer for %s‘ % query.qname response,serveraddress = udp_send(LOCALDNS,data) self.socket.sendto(response,address) if __name__ == "__main__": DnsServer("192.168.9.178:53").serve_forever()
哈哈,刚好100行,不得不说python的强大。
来个截图:
功能有限,各位童鞋可以扩展,代码已经放到github,https://github.com/Skycrab/PyDnsProxy,有兴趣的童鞋一起完善。