Hosts綁定新思路之DNS代理服務(wù)器實(shí)現(xiàn)篇
背景詳見《Hosts綁定新思路之DNS代理篇》
核心內(nèi)容
1. DNS協(xié)議解析
2. 啟動(dòng)UDP服務(wù),監(jiān)聽53端口
3. 根據(jù)DB或者文本,進(jìn)行Hosts解析
DNS協(xié)議
DNS Protocol Overview (推薦)
非強(qiáng)詳細(xì),但是不怎么看得懂的長篇大論
如果沒有耐心的同學(xué),可以看看我通過wireshark分析之后制作的兩張gif圖片。大概能知道DNS協(xié)議的內(nèi)容。
Request數(shù)據(jù)包(圖片可放大)

Response數(shù)據(jù)包(圖片可放大)

代碼


1 import struct
2 from cStringIO import StringIO
3
4 class Header(object):
5
6 def __init__(self, id, flags, questions, answer_rrs, authority_rrs, additional_rrs):
7 self.id = id
8 self.flags = flags
9 self.questions = questions
10 self.answer_rrs = answer_rrs
11 self.authority_rrs = authority_rrs
12 self.additional_rrs = additional_rrs
13
14 @classmethod
15 def parse(cls, data):
16 id, flags, questions, answer_rrs, authority_rrs, additional_rrs = struct.unpack('!6H', data.read(12))
17 return Header(id, flags, questions, answer_rrs, authority_rrs, additional_rrs)
18
19 def serialize(self, data):
20 data.write(struct.pack('!6H', self.id, self.flags, self.questions, self.answer_rrs, self.authority_rrs, self.additional_rrs))
21
22 def __str__(self):
23 return '{id: %s, flags: %s, questions: %s, answer_rrs: %s, authority_rrs: %s, additional_rrs: %s}' % (
24 self.id, self.flags, self.questions, self.answer_rrs, self.authority_rrs, self.additional_rrs)
25
26 class Query(object):
27
28 TYPE_A = 1
29 TYPE_AAAA = 28
30 CLASS_IN = 1
31
32 def __init__(self, name, type, clazz):
33 self.name = name
34 self.type = type
35 self.clazz = clazz
36
37 @classmethod
38 def parse(cls, data):
39 domain = cls._parse_domain_name(data)
40 type, clazz = struct.unpack('!2H', data.read(4))
41 return Query(domain, type, clazz)
42
43 def serialize(self, data):
44 self._serialize_domain_name(data)
45 data.write(struct.pack('!2H', self.type, self.clazz))
46
47 @staticmethod
48 def _parse_domain_name(data):
49 list = []
50 while True:
51 tmp = data.read(1)
52 len = ord(tmp)
53 if len == 0:
54 break
55 list.append(data.read(len))
56 return '.'.join(list)
57
58 def _serialize_domain_name(self, data):
59 list = self.name.split('.')
60 for i in list:
61 data.write(struct.pack('!B%ss' % (len(i)), len(i), i))
62 data.write(struct.pack('!B', 0))
63
64 def __str__(self):
65 return '{name: %s, type: %s, class: %s}' % (
66 self.name, self.type, self.clazz)
67
68 class Answer(Query):
69
70 def __init__(self, name, type, clazz, ttl, data):
71 Query.__init__(self, name, type, clazz)
72 self.ttl = ttl
73 self.data = data
74
75 def serialize(self, data):
76 data.write(struct.pack('!2B', 0xc0, 0x0c))
77 data.write(struct.pack('!2HI', self.type, self.clazz, self.ttl))
78 #only support A
79 if self.type == Answer.TYPE_A:
80 addr = self.data.split('.')
81 data.write(struct.pack('!H', len(addr)))
82 for i in addr:
83 data.write(struct.pack('!B', int(i)))
84
85 def __str__(self):
86 return '{name: %s, type: %s, class: %s, ttl: %s, data_length: %s, data: %s}' % (
87 self.name, self.type, self.clazz, self.ttl, self.data_length, self.data)
88
89 class DnsRequest(object):
90
91 def __init__(self, header, queries=[]):
92 self.header = header
93 self.queries = queries
94
95 @classmethod
96 def parse(cls, data):
97 header = Header.parse(data)
98 queries = []
99 for _ in range(header.questions):
100 queries.append(Query.parse(data))
101 return DnsRequest(header, queries)
102
103 def serialize(self):
104 data = StringIO()
105 self.header.serialize(data)
106 for i in range(len(self.queries)):
107 self.queries[i].serialize(data)
108 data.seek(0)
109 return data.read()
110
111 def __str__(self):
112 return '{header: %s, queries: %s}' % (
113 self.header, str(self.queries))
114
115 class DnsResponse(object):
116
117 def __init__(self, header, queries=[], answers=[]):
118 self.header = header
119 self.queries = queries
120 self.answers = answers
121
122 def serialize(self):
123 data = StringIO()
124 self.header.serialize(data)
125 for q in self.queries:
126 q.serialize(data)
127 for a in self.answers:
128 a.serialize(data)
129 data.seek(0)
130 return data.read()


1 from SocketServer import BaseRequestHandler, ThreadingUDPServer
2 from cStringIO import StringIO
3 from protocol import DnsRequest, Answer, DnsResponse
4 import socket
5 import time
6
7 DEBUG = True
8
9 class Hosts:
10
11 def __init__(self, hosts_file):
12 self.hosts = []
13 list = [line.strip() for line in open(hosts_file) if line.strip() != '' and not line.strip().startswith('#')]
14 for l in list:
15 info = l.split()
16 self.hosts.extend([(h, info[0]) for h in info[1:]])
17
18 def get_ip(self, domain):
19 for host in self.hosts:
20 if host[0].startswith('*'):
21 if domain.endswith(host[0][2:]):
22 return host[1]
23 else:
24 if host[0] == domain:
25 return host[1]
26 return None
27
28 class Dns:
29 def __init__(self, server):
30 self.server = server
31
32 def query(self, data):
33 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
34 sock.connect(self.server)
35 sock.sendall(data)
36 resp = sock.recv(65535)
37 sock.close()
38 return resp
39
40 class DnsProxyHandler(BaseRequestHandler):
41 def handle(self):
42 data, sock = self.request
43 hosts = self.server.hosts
44
45 req = DnsRequest.parse(StringIO(data))
46 domain = req.queries[0].name
47
48 ip = hosts.get_ip(domain)
49 if ip:
50 self.log('%s -- [%s] %s %s %s' % (self.client_address[0], time.ctime(), domain, 'Found', ip))
51 header = req.header
52 header.answer_rrs = 1
53 query = req.queries[0]
54 answer = Answer(domain, Answer.TYPE_A, Answer.CLASS_IN, 60, ip)
55 resp = DnsResponse(header, [query], [answer]).serialize()
56 else:
57 self.log('%s -- [%s] %s %s' % (self.client_address[0], time.ctime(), domain, 'Not Found'))
58 resp = Dns(self.server.dns_server).query(data)
59 sock.sendto(resp, self.client_address)
60
61 def log(self, message):
62 global DEBUG
63 if DEBUG:
64 print message
65
66
67 class DnsProxyServer(ThreadingUDPServer):
68 def __init__(self, local_server, dns_server, hosts='/ets/hosts'):
69 self.local_server = local_server
70 self.dns_server = dns_server
71 self.hosts = Hosts(hosts)
72 ThreadingUDPServer.__init__(self, local_server, DnsProxyHandler)
73
74 DnsProxyServer(('127.0.0.1', 53), ('10.20.30.40', 53), '/home/stone/tmp/hosts').serve_forever()
其中protocol,未實(shí)現(xiàn)Authority和Additional數(shù)據(jù)包
proxy,僅完成了最簡單的代理
備注:
linux下利用convert命令制作gif圖片的方法
convert -delay 100 *.png req.gif
posted on 2011-05-23 21:24 stone2083 閱讀(2918) 評論(0) 編輯 收藏 所屬分類: python