MoinQ:

DNS/実装/python/dnslib/intercept.pyについて、ここに記述してください。

   1 # -*- coding: utf-8 -*-
   2 
   3 """
   4     InterceptResolver - proxy requests to upstream server 
   5                         (optionally intercepting)
   6         
   7 """
   8 from __future__ import print_function
   9 
  10 import binascii,copy,socket,struct,sys
  11 
  12 from dnslib import DNSRecord,RR,QTYPE,RCODE,parse_time
  13 from dnslib.server import DNSServer,DNSHandler,BaseResolver,DNSLogger
  14 from dnslib.label import DNSLabel
  15 
  16 class InterceptResolver(BaseResolver):
  17 
  18     """
  19         Intercepting resolver 
  20         
  21         Proxy requests to upstream server optionally intercepting requests
  22         matching local records
  23     """
  24 
  25     def __init__(self,address,port,ttl,intercept,skip,nxdomain,timeout=0):
  26         """
  27             address/port    - upstream server
  28             ttl             - default ttl for intercept records
  29             intercept       - list of wildcard RRs to respond to (zone format)
  30             skip            - list of wildcard labels to skip 
  31             nxdomain        - list of wildcard labels to retudn NXDOMAIN
  32             timeout         - timeout for upstream server
  33         """
  34         self.address = address
  35         self.port = port
  36         self.ttl = parse_time(ttl)
  37         self.skip = skip
  38         self.nxdomain = nxdomain
  39         self.timeout = timeout
  40         self.zone = []
  41         for i in intercept:
  42             if i == '-':
  43                 i = sys.stdin.read()
  44             for rr in RR.fromZone(i,ttl=self.ttl):
  45                 self.zone.append((rr.rname,QTYPE[rr.rtype],rr))
  46 
  47     def resolve(self,request,handler):
  48         reply = request.reply()
  49         qname = request.q.qname
  50         qtype = QTYPE[request.q.qtype]
  51         # Try to resolve locally unless on skip list
  52         if not any([qname.matchGlob(s) for s in self.skip]):
  53             for name,rtype,rr in self.zone:
  54                 if qname.matchGlob(name) and (qtype in (rtype,'ANY','CNAME')):
  55                     a = copy.copy(rr)
  56                     a.rname = qname
  57                     reply.add_answer(a)
  58         # Check for NXDOMAIN
  59         if any([qname.matchGlob(s) for s in self.nxdomain]):
  60             reply.header.rcode = getattr(RCODE,'NXDOMAIN')
  61             return reply
  62         # Otherwise proxy
  63         if not reply.rr:
  64             try:
  65                 if handler.protocol == 'udp':
  66                     proxy_r = request.send(self.address,self.port,
  67                                     timeout=self.timeout)
  68                 else:
  69                     proxy_r = request.send(self.address,self.port,
  70                                     tcp=True,timeout=self.timeout)
  71                 reply = DNSRecord.parse(proxy_r)
  72             except socket.timeout:
  73                 reply.header.rcode = getattr(RCODE,'NXDOMAIN')
  74 
  75         return reply
  76 
  77 if __name__ == '__main__':
  78 
  79     import argparse,sys,time
  80 
  81     p = argparse.ArgumentParser(description="DNS Intercept Proxy")
  82     p.add_argument("--port","-p",type=int,default=53,
  83                     metavar="<port>",
  84                     help="Local proxy port (default:53)")
  85     p.add_argument("--address","-a",default="",
  86                     metavar="<address>",
  87                     help="Local proxy listen address (default:all)")
  88     p.add_argument("--upstream","-u",default="8.8.8.8:53",
  89             metavar="<dns server:port>",
  90                     help="Upstream DNS server:port (default:8.8.8.8:53)")
  91     p.add_argument("--tcp",action='store_true',default=False,
  92                     help="TCP proxy (default: UDP only)")
  93     p.add_argument("--intercept","-i",action="append",
  94                     metavar="<zone record>",
  95                     help="Intercept requests matching zone record (glob) ('-' for stdin)")
  96     p.add_argument("--skip","-s",action="append",
  97                     metavar="<label>",
  98                     help="Don't intercept matching label (glob)")
  99     p.add_argument("--nxdomain","-x",action="append",
 100                     metavar="<label>",
 101                     help="Return NXDOMAIN (glob)")
 102     p.add_argument("--ttl","-t",default="60s",
 103                     metavar="<ttl>",
 104                     help="Intercept TTL (default: 60s)")
 105     p.add_argument("--timeout","-o",type=float,default=5,
 106                     metavar="<timeout>",
 107                     help="Upstream timeout (default: 5s)")
 108     p.add_argument("--log",default="request,reply,truncated,error",
 109                     help="Log hooks to enable (default: +request,+reply,+truncated,+error,-recv,-send,-data)")
 110     p.add_argument("--log-prefix",action='store_true',default=False,
 111                     help="Log prefix (timestamp/handler/resolver) (default: False)")
 112     args = p.parse_args()
 113 
 114     args.dns,_,args.dns_port = args.upstream.partition(':')
 115     args.dns_port = int(args.dns_port or 53)
 116 
 117     resolver = InterceptResolver(args.dns,
 118                                  args.dns_port,
 119                                  args.ttl,
 120                                  args.intercept or [],
 121                                  args.skip or [],
 122                                  args.nxdomain or [],
 123                                  args.timeout)
 124     logger = DNSLogger(args.log,args.log_prefix)
 125 
 126     print("Starting Intercept Proxy (%s:%d -> %s:%d) [%s]" % (
 127                         args.address or "*",args.port,
 128                         args.dns,args.dns_port,
 129                         "UDP/TCP" if args.tcp else "UDP"))
 130 
 131     for rr in resolver.zone:
 132         print("    | ",rr[2].toZone(),sep="")
 133     if resolver.nxdomain:
 134         print("    NXDOMAIN:",", ".join(resolver.nxdomain))
 135     if resolver.skip:
 136         print("    Skipping:",", ".join(resolver.skip))
 137     print()
 138 
 139 
 140     DNSHandler.log = { 
 141         'log_request',      # DNS Request
 142         'log_reply',        # DNS Response
 143         'log_truncated',    # Truncated
 144         'log_error',        # Decoding error
 145     }
 146 
 147     udp_server = DNSServer(resolver,
 148                            port=args.port,
 149                            address=args.address,
 150                            logger=logger)
 151     udp_server.start_thread()
 152 
 153     if args.tcp:
 154         tcp_server = DNSServer(resolver,
 155                                port=args.port,
 156                                address=args.address,
 157                                tcp=True,
 158                                logger=logger)
 159         tcp_server.start_thread()
 160 
 161     while udp_server.isAlive():
 162         time.sleep(1)