77import socket
88import time
99import select
10+ import struct
1011
1112current_path = os .path .dirname (os .path .abspath (__file__ ))
1213root_path = os .path .abspath (os .path .join (current_path , os .pardir , os .pardir ))
3031class DnsServer (object ):
3132 def __init__ (self , bind_ip = "127.0.0.1" , port = 53 , backup_port = 8053 , ttl = 24 * 3600 ):
3233 self .sockets = []
34+ self .udp_relay_sock = None
35+ self .udp_relay_port = 0
3336 self .listen_port = port
3437 self .running = False
3538 if isinstance (bind_ip , str ):
@@ -51,6 +54,7 @@ def init_socket(self):
5154 listen_all_v4 and '.' in ip or
5255 listen_all_v6 and ':' in ip ):
5356 continue
57+ self .bing_udp_relay (ip )
5458 self .bing_listen (ip )
5559
5660 def bing_listen (self , bind_ip ):
@@ -84,7 +88,25 @@ def bing_listen(self, bind_ip):
8488 xlog .warn ("Then: sudo setcap 'cap_net_bind_service=+ep' /usr/bin/python2.7" )
8589 xlog .warn ("Or run as root" )
8690
87- def on_udp_query (self , rsock , req_data , addr ):
91+ def bing_udp_relay (self , bind_ip ):
92+ if ":" in bind_ip :
93+ sock = socket .socket (socket .AF_INET6 , socket .SOCK_DGRAM )
94+ else :
95+ sock = socket .socket (socket .AF_INET , socket .SOCK_DGRAM )
96+
97+ port = g .config .udp_relay_port
98+ for port in range (port , port + 20 ):
99+ try :
100+ sock .bind ((bind_ip , port ))
101+ xlog .info ("start UDP relay server at %s:%d" , bind_ip , port )
102+ self .sockets .append (sock )
103+ self .udp_relay_sock = sock
104+ self .udp_relay_port = port
105+ return
106+ except :
107+ xlog .warn ("bind UDP %s:%d fail" , bind_ip , self .port )
108+
109+ def dns_query (self , req_data , addr ):
88110 start_time = time .time ()
89111 try :
90112 request = DNSRecord .parse (req_data )
@@ -118,12 +140,62 @@ def on_udp_query(self, rsock, req_data, addr):
118140 reply .add_answer (RR (domain , rtype = dns_type , ttl = 60 , rdata = NS (ip )))
119141 res_data = reply .pack ()
120142
121- rsock .sendto (res_data , addr )
122143 xlog .debug ("query:%s type:%d from:%s, return ip num:%d cost:%d" , domain , dns_type , addr ,
123144 len (reply .rr ), (time .time ()- start_time )* 1000 )
145+ return res_data
124146 except Exception as e :
125147 xlog .exception ("on_query except:%r" , e )
126148
149+ def on_udp_query (self , rsock , req_data , addr ):
150+ res_data = self .dns_query (req_data , addr )
151+ rsock .sendto (res_data , addr )
152+
153+ def on_udp_relay (self , rsock , req_data , from_addr ):
154+ # We currently only support DNS query for UDP relay
155+
156+ # SOCKS5 UDP forward request
157+ # reserved, frag, addr_type, domain_len, domain, port, data
158+ try :
159+ reserved = struct .unpack (">H" , req_data [0 :2 ])[0 ]
160+ frag = ord (req_data [2 :3 ])
161+ if reserved != 0 or frag != 0 :
162+ xlog .warn ("reserved:%d frag:%d" , reserved , frag )
163+ return
164+
165+ addr_type = ord (req_data [3 :4 ])
166+ if addr_type == 1 : # IPv4
167+ addr_pack = req_data [4 :8 ]
168+ addr = socket .inet_ntoa (addr_pack )
169+ port = struct .unpack (">H" , req_data [8 :10 ])[0 ]
170+ data = req_data [10 :]
171+ elif addr_type == 3 : # Domain name
172+ domain_len_pack = req_data [4 :5 ]
173+ domain_len = ord (domain_len_pack )
174+ domain = req_data [5 :5 + domain_len ]
175+ addr = domain
176+ port = struct .unpack (">H" , req_data [5 + domain_len :5 + domain_len + 2 ])[0 ]
177+ data = req_data [5 + domain_len + 2 :]
178+ elif addr_type == 4 : # IPv6
179+ addr_pack = req_data [4 :20 ]
180+ addr = socket .inet_ntop (socket .AF_INET6 , addr_pack )
181+ port = struct .unpack (">H" , req_data [20 :22 ])[0 ]
182+ data = req_data [22 :]
183+ else :
184+ xlog .warn ("request address type unknown:%d" , addr_type )
185+ return
186+
187+ xlog .debug ("UDP relay from %s size:%d to:%s:%d" , from_addr , len (data ), addr , port )
188+ head_length = len (req_data ) - len (data )
189+ head = req_data [:head_length ]
190+ res_data = self .dns_query (data , from_addr )
191+ if not res_data :
192+ return
193+
194+ rsock .sendto (head + res_data , from_addr )
195+ xlog .debug ("UDP relay from %s size:%d to:%s:%d res len:%d" , from_addr , len (data ), addr , port , len (res_data ))
196+ except Exception as e :
197+ xlog .exception ("on_udp_relay data:[%s] except:%r" , utils .str2hex (req_data ), e )
198+
127199 def server_forever (self ):
128200 while self .running :
129201 r , w , e = select .select (self .sockets , [], [], 1 )
@@ -137,7 +209,10 @@ def server_forever(self):
137209 xlog .warn ("recv except: %r" , e )
138210 break
139211
140- threading .Thread (target = self .on_udp_query , args = (rsock , data , addr ), name = "DNSServer_udp_handler" ).start ()
212+ if rsock == self .udp_relay_sock :
213+ threading .Thread (target = self .on_udp_relay , args = (rsock , data , addr ), name = "UDP_relay" ).start ()
214+ else :
215+ threading .Thread (target = self .on_udp_query , args = (rsock , data , addr ), name = "DNSServer_udp_handler" ).start ()
141216
142217 self .th = None
143218
0 commit comments