7
7
import socket
8
8
import time
9
9
import select
10
+ import struct
10
11
11
12
current_path = os .path .dirname (os .path .abspath (__file__ ))
12
13
root_path = os .path .abspath (os .path .join (current_path , os .pardir , os .pardir ))
30
31
class DnsServer (object ):
31
32
def __init__ (self , bind_ip = "127.0.0.1" , port = 53 , backup_port = 8053 , ttl = 24 * 3600 ):
32
33
self .sockets = []
34
+ self .udp_relay_sock = None
35
+ self .udp_relay_port = 0
33
36
self .listen_port = port
34
37
self .running = False
35
38
if isinstance (bind_ip , str ):
@@ -51,6 +54,7 @@ def init_socket(self):
51
54
listen_all_v4 and '.' in ip or
52
55
listen_all_v6 and ':' in ip ):
53
56
continue
57
+ self .bing_udp_relay (ip )
54
58
self .bing_listen (ip )
55
59
56
60
def bing_listen (self , bind_ip ):
@@ -84,7 +88,25 @@ def bing_listen(self, bind_ip):
84
88
xlog .warn ("Then: sudo setcap 'cap_net_bind_service=+ep' /usr/bin/python2.7" )
85
89
xlog .warn ("Or run as root" )
86
90
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 ):
88
110
start_time = time .time ()
89
111
try :
90
112
request = DNSRecord .parse (req_data )
@@ -118,12 +140,62 @@ def on_udp_query(self, rsock, req_data, addr):
118
140
reply .add_answer (RR (domain , rtype = dns_type , ttl = 60 , rdata = NS (ip )))
119
141
res_data = reply .pack ()
120
142
121
- rsock .sendto (res_data , addr )
122
143
xlog .debug ("query:%s type:%d from:%s, return ip num:%d cost:%d" , domain , dns_type , addr ,
123
144
len (reply .rr ), (time .time ()- start_time )* 1000 )
145
+ return res_data
124
146
except Exception as e :
125
147
xlog .exception ("on_query except:%r" , e )
126
148
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
+
127
199
def server_forever (self ):
128
200
while self .running :
129
201
r , w , e = select .select (self .sockets , [], [], 1 )
@@ -137,7 +209,10 @@ def server_forever(self):
137
209
xlog .warn ("recv except: %r" , e )
138
210
break
139
211
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 ()
141
216
142
217
self .th = None
143
218
0 commit comments