Hosts绑定新思路之DNS代理服务器实现篇

背景
详见《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()

protocol.py   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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值