背景
详见《Hosts绑定新思路之DNS代理篇》
核心内容
1. DNS协议解析
2. 启动UDP服务,监听53端口
3. 根据DB或者文本,进行Hosts解析
DNS协议
DNS Protocol Overview (推荐)
非强详细,但是不怎么看得懂的长篇大论
如果没有耐心的同学,可以看看我通过wireshark分析之后制作的两张gif图片。大概能知道DNS协议的内容。
Request数据包(图片可放大)
Response数据包(图片可放大)
代码
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()
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()
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