1- # -*- coding: utf-8 -*-
1+ import socket
2+ import struct
3+ from zope .interface import implementer
4+ from twisted .names .dns import str2time , tputil , readPrecisely , IRecord , IEncodable
25
3- # version
4- __version__ = '1.0.1.1'
6+ EDNS = 41
57
6- import logging
7- import os
8- import sys
9- import yaml
10- from multiprocessing import cpu_count , Process
11- from os .path import isfile
12- from twisted .internet import reactor
13- from twisted .names import dns
8+ @implementer (IEncodable , IRecord )
9+ class Record_EDNS (tputil .FancyEqMixin ):
10+ """
11+ An EDNS
12+ """
1413
15- from . import server , monitor
16- from .finder import Finder
14+ compareAttributes = ('address' , 'family' , 'opt_code' , 'opt_length' , 'source_nm' , 'scope_nm' )
1715
18- logger = logging .getLogger (__name__ )
16+ TYPE = EDNS
17+ address = None
18+ opt_code = None
19+ opt_length = None
20+ family = None
21+ source_nm = None
22+ scope_nm = None
1923
24+ def __init__ (self , address = '0.0.0.0' , ttl = None ):
25+ if address is not None :
26+ address = socket .inet_aton (address )
27+ self .address = address
28+ self .ttl = str2time (ttl )
2029
21- def loadconfig (path ):
22- if not isfile (path ):
23- print ("[FATAL] can't find config file %s !" % path )
24- exit (1 )
25- with open (path , 'r' ) as f :
26- return yaml .load (f , Loader = yaml .FullLoader )
2730
31+ def encode (self , strio , compDict = None ):
32+ if self .address is not None :
33+ strio .write (struct .pack ('!HHHcc' , self .opt_code , self .opt_length , self .family , self .source_nm , self .scope_nm ))
34+ strio .write (self .address )
2835
29- def prepare_run (run_env ):
30- # load main config
31- logger .info ('start to load %s ......' % run_env ['conf' ])
32- conf = loadconfig (os .path .join (run_env ['conf' ], 'sdns.yaml' ))
33- logger .info ('start to load A,SOA,NS record ......' )
34- a_mapping = loadconfig (os .path .join (run_env ['conf' ], 'a.yaml' ))
35- ns_mapping = loadconfig (os .path .join (run_env ['conf' ], 'ns.yaml' ))
36- soa_mapping = loadconfig (os .path .join (run_env ['conf' ], 'soa.yaml' ))
36+ def decode (self , strio , length = None ):
37+ if length > 8 :
38+ rdata = readPrecisely (strio , 8 )
39+ self .opt_code , self .opt_length , self .family , self .source_nm , self .scope_nm = struct .unpack ("!HHHcc" , rdata )
40+ #hack let scope netmask = source netmask
41+ self .scope_nm = self .source_nm
42+ self .address = readPrecisely (strio , length - 8 )
3743
38- # start monitor
39- monitor_config = loadconfig (os .path .join (run_env ['conf' ], 'monitor.yaml' ))
40- monitor_mapping = monitor .MonitorMapping (monitor_config , a_mapping )
41- # load dns record config file
42- logger .info ('start to init IP pool ......' )
43- finder = Finder (
44- os .path .join (run_env ['conf' ], 'ip.csv' ),
45- os .path .join (run_env ['conf' ], 'a.yaml' ),
46- monitor_mapping )
44+ def __hash__ (self ):
45+ return hash (self .address )
4746
48- run_env ['finder' ] = finder
4947
50- # set up a resolver that uses the mapping or a secondary nameserver
51- dns_forwards = []
52- for i in conf ['dns_forwards' ]:
53- dns_forward_ip , dns_forward_port = i .split (":" )
54- dns_forwards .append ((dns_forward_ip , int (dns_forward_port )))
48+ def __str__ (self ):
49+ return '<A address=%s ttl=%s>' % (self .address , self .ttl )
50+ __repr__ = __str__
5551
56- # create the protocols
57- for listen_tcp in conf ['listen' ]['tcp' ]:
58- listen_tcp_ip , listen_tcp_port = listen_tcp .split (":" )
59- f = server .SmartDNSFactory (clients = [
60- server .MapResolver (
61- finder , a_mapping , ns_mapping , soa_mapping , servers = dns_forwards )])
62- f .noisy = False
63- run_env ['tcp' ].append ([int (listen_tcp_port ), f , listen_tcp_ip ])
64- for listen_udp in conf ['listen' ]['tcp' ]:
65- listen_udp_ip , listen_udp_port = listen_udp .split (":" )
66- p = dns .DNSDatagramProtocol (server .SmartDNSFactory (clients = [
67- server .MapResolver (
68- finder , a_mapping , ns_mapping , soa_mapping , servers = dns_forwards )]))
69- p .noisy = False
70- run_env ['udp' ].append ([int (listen_udp_port ), p , listen_udp_ip ])
71- return conf
7252
73-
74- def main ():
75- run_env = {'udp' : [], 'tcp' : [], 'closed' : 0 ,
76- 'updated' : False , 'finder' : None }
77- if len (sys .argv ) > 1 :
78- run_env ['conf' ] = sys .argv [1 ]
79- else :
80- run_env ['conf' ] = '/etc/smartdns'
81-
82- conf = prepare_run (run_env )
83- for e in run_env ['tcp' ]:
84- reactor .listenTCP (e [0 ], e [1 ], interface = e [2 ])
85- for e in run_env ['udp' ]:
86- reactor .listenUDP (e [0 ], e [1 ], interface = e [2 ])
87- workers = []
88- for _ in range (conf .get ("workers" , cpu_count () * 2 )):
89- process = Process (target = reactor .run )
90- process .daemon = True
91- process .start ()
92- workers .append (process )
93- [worker .join () for worker in workers ]
53+ def dottedQuad (self ):
54+ n = 4 - len (self .address )
55+ return socket .inet_ntoa (self .address + '\x00 ' * n )
0 commit comments