Mercurial > 510Connectbot
comparison src/net/sourceforge/jsocks/ProxyServer.java @ 349:205ee2873330
update jsocks to 2011-03-19
author | Carl Byington <carl@five-ten-sg.com> |
---|---|
date | Fri, 01 Aug 2014 11:23:10 -0700 |
parents | 0ce5cc452d02 |
children |
comparison
equal
deleted
inserted
replaced
348:29076621bab0 | 349:205ee2873330 |
---|---|
1 package net.sourceforge.jsocks; | 1 package net.sourceforge.jsocks; |
2 import java.io.EOFException; | |
3 import java.io.IOException; | |
4 import java.io.InputStream; | |
5 import java.io.InterruptedIOException; | |
6 import java.io.OutputStream; | |
7 import java.io.PrintStream; | |
8 import java.io.PushbackInputStream; | |
9 import java.net.ConnectException; | |
10 import java.net.InetAddress; | |
11 import java.net.NoRouteToHostException; | |
12 import java.net.ServerSocket; | |
13 import java.net.Socket; | |
14 | |
15 import net.sourceforge.jsocks.server.ServerAuthenticator; | 2 import net.sourceforge.jsocks.server.ServerAuthenticator; |
3 import java.net.*; | |
4 import java.io.*; | |
16 | 5 |
17 /** | 6 /** |
18 SOCKS4 and SOCKS5 proxy, handles both protocols simultaniously. | 7 SOCKS4 and SOCKS5 proxy, handles both protocols simultaniously. |
19 Implements all SOCKS commands, including UDP relaying. | 8 Implements all SOCKS commands, including UDP relaying. |
20 <p> | 9 <p> |
25 in the world. One should never use this authentication scheme unless | 14 in the world. One should never use this authentication scheme unless |
26 one have pretty good reason to do so. | 15 one have pretty good reason to do so. |
27 There is a couple of other authentication schemes in socks.server package. | 16 There is a couple of other authentication schemes in socks.server package. |
28 @see socks.server.ServerAuthenticator | 17 @see socks.server.ServerAuthenticator |
29 */ | 18 */ |
30 public class ProxyServer implements Runnable { | 19 public class ProxyServer implements Runnable{ |
31 | 20 |
32 ServerAuthenticator auth; | 21 ServerAuthenticator auth; |
33 ProxyMessage msg = null; | 22 ProxyMessage msg = null; |
34 | 23 |
35 Socket sock = null, remote_sock = null; | 24 Socket sock=null,remote_sock=null; |
36 ServerSocket ss = null; | 25 ServerSocket ss=null; |
37 UDPRelayServer relayServer = null; | 26 UDPRelayServer relayServer = null; |
38 InputStream in, remote_in; | 27 InputStream in,remote_in; |
39 OutputStream out, remote_out; | 28 OutputStream out,remote_out; |
40 | 29 |
41 int mode; | 30 int mode; |
42 static final int START_MODE = 0; | 31 static final int START_MODE = 0; |
43 static final int ACCEPT_MODE = 1; | 32 static final int ACCEPT_MODE = 1; |
44 static final int PIPE_MODE = 2; | 33 static final int PIPE_MODE = 2; |
45 static final int ABORT_MODE = 3; | 34 static final int ABORT_MODE = 3; |
46 | 35 |
47 static final int BUF_SIZE = 8192; | 36 static final int BUF_SIZE = 8192; |
48 | 37 |
49 Thread pipe_thread1, pipe_thread2; | 38 Thread pipe_thread1,pipe_thread2; |
50 long lastReadTime; | 39 long lastReadTime; |
51 | 40 |
52 protected static int iddleTimeout = 180000; //3 minutes | 41 static int iddleTimeout = 180000; //3 minutes |
53 static int acceptTimeout = 180000; //3 minutes | 42 static int acceptTimeout = 180000; //3 minutes |
54 | 43 |
55 static PrintStream log = null; | 44 static PrintStream log = null; |
56 static Proxy proxy; | 45 static CProxy proxy; |
57 | 46 |
58 | 47 |
59 //Public Constructors | 48 //Public Constructors |
60 ///////////////////// | 49 ///////////////////// |
61 | 50 |
62 | 51 |
63 /** | 52 /** |
64 Creates a proxy server with given Authentication scheme. | 53 Creates a proxy server with given Authentication scheme. |
65 @param auth Authentication scheme to be used. | 54 @param auth Authentication scheme to be used. |
66 */ | 55 */ |
67 public ProxyServer(ServerAuthenticator auth) { | 56 public ProxyServer(ServerAuthenticator auth){ |
68 this.auth = auth; | 57 this.auth = auth; |
69 } | 58 } |
70 | 59 |
71 //Other constructors | 60 //Other constructors |
72 //////////////////// | 61 //////////////////// |
73 | 62 |
74 protected ProxyServer(ServerAuthenticator auth, Socket s) { | 63 ProxyServer(ServerAuthenticator auth,Socket s){ |
75 this.auth = auth; | 64 this.auth = auth; |
76 this.sock = s; | 65 this.sock = s; |
77 mode = START_MODE; | 66 mode = START_MODE; |
78 } | 67 } |
79 | 68 |
80 //Public methods | 69 //Public methods |
81 ///////////////// | 70 ///////////////// |
82 | 71 |
83 /** | 72 /** |
84 Set the logging stream. Specifying null disables logging. | 73 Set the logging stream. Specifying null disables logging. |
74 */ | |
75 public static void setLog(OutputStream out){ | |
76 if(out == null){ | |
77 log = null; | |
78 }else{ | |
79 log = new PrintStream(out,true); | |
80 } | |
81 | |
82 UDPRelayServer.log = log; | |
83 } | |
84 | |
85 /** | |
86 Set proxy. | |
87 <p> | |
88 Allows CProxy chaining so that one CProxy server is connected to another | |
89 and so on. If proxy supports SOCKSv4, then only some SOCKSv5 requests | |
90 can be handled, UDP would not work, however CONNECT and BIND will be | |
91 translated. | |
92 | |
93 @param p CProxy which should be used to handle user requests. | |
94 */ | |
95 public static void setProxy(CProxy p){ | |
96 proxy =p; | |
97 UDPRelayServer.proxy = proxy; | |
98 } | |
99 | |
100 /** | |
101 Get proxy. | |
102 @return CProxy wich is used to handle user requests. | |
103 */ | |
104 public static CProxy getProxy(){ | |
105 return proxy; | |
106 } | |
107 | |
108 /** | |
109 Sets the timeout for connections, how long shoud server wait | |
110 for data to arrive before dropping the connection.<br> | |
111 Zero timeout implies infinity.<br> | |
112 Default timeout is 3 minutes. | |
113 */ | |
114 public static void setIddleTimeout(int timeout){ | |
115 iddleTimeout = timeout; | |
116 } | |
117 /** | |
118 Sets the timeout for BIND command, how long the server should | |
119 wait for the incoming connection.<br> | |
120 Zero timeout implies infinity.<br> | |
121 Default timeout is 3 minutes. | |
122 */ | |
123 public static void setAcceptTimeout(int timeout){ | |
124 acceptTimeout = timeout; | |
125 } | |
126 | |
127 /** | |
128 Sets the timeout for UDPRelay server.<br> | |
129 Zero timeout implies infinity.<br> | |
130 Default timeout is 3 minutes. | |
131 */ | |
132 public static void setUDPTimeout(int timeout){ | |
133 UDPRelayServer.setTimeout(timeout); | |
134 } | |
135 | |
136 /** | |
137 Sets the size of the datagrams used in the UDPRelayServer.<br> | |
138 Default size is 64K, a bit more than maximum possible size of the | |
139 datagram. | |
85 */ | 140 */ |
86 public static void setLog(OutputStream out) { | 141 public static void setDatagramSize(int size){ |
87 if (out == null) { | 142 UDPRelayServer.setDatagramSize(size); |
88 log = null; | 143 } |
144 | |
145 | |
146 /** | |
147 Start the CProxy server at given port.<br> | |
148 This methods blocks. | |
149 */ | |
150 public void start(int port){ | |
151 start(port,5,null); | |
152 } | |
153 | |
154 /** | |
155 Create a server with the specified port, listen backlog, and local | |
156 IP address to bind to. The localIP argument can be used on a multi-homed | |
157 host for a ServerSocket that will only accept connect requests to one of | |
158 its addresses. If localIP is null, it will default accepting connections | |
159 on any/all local addresses. The port must be between 0 and 65535, | |
160 inclusive. <br> | |
161 This methods blocks. | |
162 */ | |
163 public void start(int port,int backlog,InetAddress localIP){ | |
164 try{ | |
165 ss = new ServerSocket(port,backlog,localIP); | |
166 log("Starting SOCKS Proxy on:"+ss.getInetAddress().getHostAddress()+":" | |
167 +ss.getLocalPort()); | |
168 while(true){ | |
169 Socket s = ss.accept(); | |
170 log("Accepted from:"+s.getInetAddress().getHostName()+":" | |
171 +s.getPort()); | |
172 ProxyServer ps = new ProxyServer(auth,s); | |
173 (new Thread(ps)).start(); | |
174 } | |
175 }catch(IOException ioe){ | |
176 ioe.printStackTrace(); | |
177 }finally{ | |
178 } | |
179 } | |
180 | |
181 /** | |
182 Stop server operation.It would be wise to interrupt thread running the | |
183 server afterwards. | |
184 */ | |
185 public void stop(){ | |
186 try{ | |
187 if(ss != null) ss.close(); | |
188 }catch(IOException ioe){ | |
189 } | |
190 } | |
191 | |
192 //Runnable interface | |
193 //////////////////// | |
194 public void run(){ | |
195 switch(mode){ | |
196 case START_MODE: | |
197 try{ | |
198 startSession(); | |
199 }catch(IOException ioe){ | |
200 handleException(ioe); | |
201 //ioe.printStackTrace(); | |
202 }finally{ | |
203 abort(); | |
204 if(auth!=null) auth.endSession(); | |
205 log("Main thread(client->remote)stopped."); | |
206 } | |
207 break; | |
208 case ACCEPT_MODE: | |
209 try{ | |
210 doAccept(); | |
211 mode = PIPE_MODE; | |
212 pipe_thread1.interrupt(); //Tell other thread that connection have | |
213 //been accepted. | |
214 pipe(remote_in,out); | |
215 }catch(IOException ioe){ | |
216 //log("Accept exception:"+ioe); | |
217 handleException(ioe); | |
218 }finally{ | |
219 abort(); | |
220 log("Accept thread(remote->client) stopped"); | |
221 } | |
222 break; | |
223 case PIPE_MODE: | |
224 try{ | |
225 pipe(remote_in,out); | |
226 }catch(IOException ioe){ | |
227 }finally{ | |
228 abort(); | |
229 log("Support thread(remote->client) stopped"); | |
230 } | |
231 break; | |
232 case ABORT_MODE: | |
233 break; | |
234 default: | |
235 log("Unexpected MODE "+mode); | |
236 } | |
237 } | |
238 | |
239 //Private methods | |
240 ///////////////// | |
241 private void startSession() throws IOException{ | |
242 sock.setSoTimeout(iddleTimeout); | |
243 | |
244 try{ | |
245 auth = auth.startSession(sock); | |
246 }catch(IOException ioe){ | |
247 log("Auth throwed exception:"+ioe); | |
248 auth = null; | |
249 return; | |
250 } | |
251 | |
252 if(auth == null){ //Authentication failed | |
253 log("Authentication failed"); | |
254 return; | |
255 } | |
256 | |
257 in = auth.getInputStream(); | |
258 out = auth.getOutputStream(); | |
259 | |
260 msg = readMsg(in); | |
261 handleRequest(msg); | |
262 } | |
263 | |
264 private void handleRequest(ProxyMessage msg) | |
265 throws IOException{ | |
266 if(!auth.checkRequest(msg)) throw new | |
267 SocksException(CProxy.SOCKS_FAILURE); | |
268 | |
269 if(msg.ip == null){ | |
270 if(msg instanceof Socks5Message){ | |
271 msg.ip = InetAddress.getByName(msg.host); | |
272 }else | |
273 throw new SocksException(CProxy.SOCKS_FAILURE); | |
274 } | |
275 log(msg); | |
276 | |
277 switch(msg.command){ | |
278 case CProxy.SOCKS_CMD_CONNECT: | |
279 onConnect(msg); | |
280 break; | |
281 case CProxy.SOCKS_CMD_BIND: | |
282 onBind(msg); | |
283 break; | |
284 case CProxy.SOCKS_CMD_UDP_ASSOCIATE: | |
285 onUDP(msg); | |
286 break; | |
287 default: | |
288 throw new SocksException(CProxy.SOCKS_CMD_NOT_SUPPORTED); | |
289 } | |
290 } | |
291 | |
292 private void handleException(IOException ioe){ | |
293 //If we couldn't read the request, return; | |
294 if(msg == null) return; | |
295 //If have been aborted by other thread | |
296 if(mode == ABORT_MODE) return; | |
297 //If the request was successfully completed, but exception happened later | |
298 if(mode == PIPE_MODE) return; | |
299 | |
300 int error_code = CProxy.SOCKS_FAILURE; | |
301 | |
302 if(ioe instanceof SocksException) | |
303 error_code = ((SocksException)ioe).errCode; | |
304 else if(ioe instanceof NoRouteToHostException) | |
305 error_code = CProxy.SOCKS_HOST_UNREACHABLE; | |
306 else if(ioe instanceof ConnectException) | |
307 error_code = CProxy.SOCKS_CONNECTION_REFUSED; | |
308 else if(ioe instanceof InterruptedIOException) | |
309 error_code = CProxy.SOCKS_TTL_EXPIRE; | |
310 | |
311 if(error_code > CProxy.SOCKS_ADDR_NOT_SUPPORTED || error_code < 0){ | |
312 error_code = CProxy.SOCKS_FAILURE; | |
313 } | |
314 | |
315 sendErrorMessage(error_code); | |
316 } | |
317 | |
318 private void onConnect(ProxyMessage msg) throws IOException { | |
319 Socket s = null; | |
320 ProxyMessage response = null; | |
321 int iSock5Cmd = CProxy.SOCKS_FAILURE; //defaulting to failure | |
322 int iSock4Msg = Socks4Message.REPLY_NO_CONNECT; | |
323 InetAddress sIp = null; int iPort = 0; | |
324 | |
325 try { | |
326 if (proxy == null) { | |
327 s = new Socket(msg.ip, msg.port); | |
328 } else { | |
329 s = new SocksSocket(proxy, msg.ip, msg.port); | |
330 } | |
331 log("Connected to " + s.getInetAddress() + ":" + s.getPort()); | |
332 | |
333 iSock5Cmd = CProxy.SOCKS_SUCCESS; iSock4Msg = Socks4Message.REPLY_OK; | |
334 sIp = s.getInetAddress(); iPort = s.getPort(); | |
335 | |
336 } | |
337 catch (Exception sE) { | |
338 log("Failed connecting to remote socket. Exception: " + sE.getLocalizedMessage()); | |
339 | |
340 //TBD Pick proper socks error for corresponding socket error, below is too generic | |
341 iSock5Cmd = CProxy.SOCKS_CONNECTION_REFUSED; iSock4Msg = Socks4Message.REPLY_NO_CONNECT; | |
342 } | |
343 | |
344 if (msg instanceof Socks5Message) { | |
345 response = new Socks5Message(iSock5Cmd, sIp, iPort); | |
346 } else { | |
347 response = new Socks4Message(iSock4Msg, sIp, iPort); | |
348 } | |
349 | |
350 response.write(out); | |
351 | |
352 if (s != null) { | |
353 startPipe(s); | |
89 } | 354 } |
90 else { | 355 else { |
91 log = new PrintStream(out, true); | 356 throw (new RuntimeException("onConnect() Failed to create Socket()")); |
92 } | 357 } |
93 | 358 |
94 UDPRelayServer.log = log; | 359 return; |
95 } | 360 } |
96 | 361 |
97 /** | 362 |
98 Set proxy. | 363 private void onBind(ProxyMessage msg) throws IOException{ |
99 <p> | 364 ProxyMessage response = null; |
100 Allows Proxy chaining so that one Proxy server is connected to another | 365 |
101 and so on. If proxy supports SOCKSv4, then only some SOCKSv5 requests | 366 if(proxy == null) |
102 can be handled, UDP would not work, however CONNECT and BIND will be | 367 ss = new ServerSocket(0); |
103 translated. | 368 else |
104 | 369 ss = new SocksServerSocket(proxy, msg.ip, msg.port); |
105 @param p Proxy which should be used to handle user requests. | 370 |
106 */ | 371 ss.setSoTimeout(acceptTimeout); |
107 public static void setProxy(Proxy p) { | 372 |
108 proxy = p; | 373 log("Trying accept on "+ss.getInetAddress()+":"+ss.getLocalPort()); |
109 UDPRelayServer.proxy = proxy; | 374 |
110 } | 375 if(msg.version == 5) |
111 | 376 response = new Socks5Message(CProxy.SOCKS_SUCCESS,ss.getInetAddress(), |
112 /** | 377 ss.getLocalPort()); |
113 Get proxy. | 378 else |
114 @return Proxy wich is used to handle user requests. | 379 response = new Socks4Message(Socks4Message.REPLY_OK, |
115 */ | 380 ss.getInetAddress(), |
116 public static Proxy getProxy() { | 381 ss.getLocalPort()); |
117 return proxy; | 382 response.write(out); |
118 } | 383 |
119 | 384 mode = ACCEPT_MODE; |
120 /** | 385 |
121 Sets the timeout for connections, how long shoud server wait | 386 pipe_thread1 = Thread.currentThread(); |
122 for data to arrive before dropping the connection.<br> | 387 pipe_thread2 = new Thread(this); |
123 Zero timeout implies infinity.<br> | 388 pipe_thread2.start(); |
124 Default timeout is 3 minutes. | 389 |
125 */ | 390 //Make timeout infinit. |
126 public static void setIddleTimeout(int timeout) { | 391 sock.setSoTimeout(0); |
127 iddleTimeout = timeout; | 392 int eof=0; |
128 } | 393 |
129 /** | 394 try{ |
130 Sets the timeout for BIND command, how long the server should | 395 while((eof=in.read())>=0){ |
131 wait for the incoming connection.<br> | 396 if(mode != ACCEPT_MODE){ |
132 Zero timeout implies infinity.<br> | 397 if(mode != PIPE_MODE) return;//Accept failed |
133 Default timeout is 3 minutes. | 398 |
134 */ | 399 remote_out.write(eof); |
135 public static void setAcceptTimeout(int timeout) { | 400 break; |
136 acceptTimeout = timeout; | 401 } |
137 } | |
138 | |
139 /** | |
140 Sets the timeout for UDPRelay server.<br> | |
141 Zero timeout implies infinity.<br> | |
142 Default timeout is 3 minutes. | |
143 */ | |
144 public static void setUDPTimeout(int timeout) { | |
145 UDPRelayServer.setTimeout(timeout); | |
146 } | |
147 | |
148 /** | |
149 Sets the size of the datagrams used in the UDPRelayServer.<br> | |
150 Default size is 64K, a bit more than maximum possible size of the | |
151 datagram. | |
152 */ | |
153 public static void setDatagramSize(int size) { | |
154 UDPRelayServer.setDatagramSize(size); | |
155 } | |
156 | |
157 | |
158 /** | |
159 Start the Proxy server at given port.<br> | |
160 This methods blocks. | |
161 */ | |
162 public void start(int port) { | |
163 start(port, 5, null); | |
164 } | |
165 | |
166 /** | |
167 Create a server with the specified port, listen backlog, and local | |
168 IP address to bind to. The localIP argument can be used on a multi-homed | |
169 host for a ServerSocket that will only accept connect requests to one of | |
170 its addresses. If localIP is null, it will default accepting connections | |
171 on any/all local addresses. The port must be between 0 and 65535, | |
172 inclusive. <br> | |
173 This methods blocks. | |
174 */ | |
175 public void start(int port, int backlog, InetAddress localIP) { | |
176 try { | |
177 ss = new ServerSocket(port, backlog, localIP); | |
178 log("Starting SOCKS Proxy on:" + ss.getInetAddress().getHostAddress() + ":" | |
179 + ss.getLocalPort()); | |
180 | |
181 while (true) { | |
182 Socket s = ss.accept(); | |
183 log("Accepted from:" + s.getInetAddress().getHostName() + ":" | |
184 + s.getPort()); | |
185 ProxyServer ps = new ProxyServer(auth, s); | |
186 (new Thread(ps)).start(); | |
187 } | |
188 } | 402 } |
189 catch (IOException ioe) { | 403 }catch(EOFException eofe){ |
190 ioe.printStackTrace(); | 404 //System.out.println("EOF exception"); |
191 } | 405 return;//Connection closed while we were trying to accept. |
192 finally { | 406 }catch(InterruptedIOException iioe){ |
193 } | 407 //Accept thread interrupted us. |
194 } | 408 //System.out.println("Interrupted"); |
195 | 409 if(mode != PIPE_MODE) |
196 /** | 410 return;//If accept thread was not successfull return. |
197 Stop server operation.It would be wise to interrupt thread running the | 411 }finally{ |
198 server afterwards. | 412 //System.out.println("Finnaly!"); |
199 */ | 413 } |
200 public void stop() { | 414 |
201 try { | 415 if(eof < 0)//Connection closed while we were trying to accept; |
202 if (ss != null) ss.close(); | 416 return; |
203 } | 417 |
204 catch (IOException ioe) { | 418 //Do not restore timeout, instead timeout is set on the |
205 } | 419 //remote socket. It does not make any difference. |
206 } | 420 |
207 | 421 pipe(in,remote_out); |
208 //Runnable interface | 422 } |
209 //////////////////// | 423 |
210 public void run() { | 424 private void onUDP(ProxyMessage msg) throws IOException{ |
211 switch (mode) { | 425 if(msg.ip.getHostAddress().equals("0.0.0.0")) |
212 case START_MODE: | 426 msg.ip = sock.getInetAddress(); |
213 try { | 427 log("Creating UDP relay server for "+msg.ip+":"+msg.port); |
214 startSession(); | 428 relayServer = new UDPRelayServer(msg.ip,msg.port, |
215 } | 429 Thread.currentThread(),sock,auth); |
216 catch (IOException ioe) { | 430 |
217 handleException(ioe); | 431 ProxyMessage response; |
218 //ioe.printStackTrace(); | 432 |
219 } | 433 response = new Socks5Message(CProxy.SOCKS_SUCCESS, |
220 finally { | 434 relayServer.relayIP,relayServer.relayPort); |
221 abort(); | 435 |
222 | 436 response.write(out); |
223 if (auth != null) auth.endSession(); | 437 |
224 | 438 relayServer.start(); |
225 log("Main thread(client->remote)stopped."); | 439 |
226 } | 440 //Make timeout infinit. |
227 | 441 sock.setSoTimeout(0); |
228 break; | 442 try{ |
229 | 443 while(in.read()>=0) /*do nothing*/; |
230 case ACCEPT_MODE: | 444 }catch(EOFException eofe){ |
231 try { | 445 } |
232 doAccept(); | 446 } |
233 mode = PIPE_MODE; | |
234 pipe_thread1.interrupt(); //Tell other thread that connection have | |
235 //been accepted. | |
236 pipe(remote_in, out); | |
237 } | |
238 catch (IOException ioe) { | |
239 //log("Accept exception:"+ioe); | |
240 handleException(ioe); | |
241 } | |
242 finally { | |
243 abort(); | |
244 log("Accept thread(remote->client) stopped"); | |
245 } | |
246 | |
247 break; | |
248 | |
249 case PIPE_MODE: | |
250 try { | |
251 pipe(remote_in, out); | |
252 } | |
253 catch (IOException ioe) { | |
254 } | |
255 finally { | |
256 abort(); | |
257 log("Support thread(remote->client) stopped"); | |
258 } | |
259 | |
260 break; | |
261 | |
262 case ABORT_MODE: | |
263 break; | |
264 | |
265 default: | |
266 log("Unexpected MODE " + mode); | |
267 } | |
268 } | |
269 | |
270 //Private methods | |
271 ///////////////// | |
272 private void startSession() throws IOException { | |
273 sock.setSoTimeout(iddleTimeout); | |
274 | |
275 try { | |
276 auth = auth.startSession(sock); | |
277 } | |
278 catch (IOException ioe) { | |
279 log("Auth throwed exception:" + ioe); | |
280 auth = null; | |
281 return; | |
282 } | |
283 | |
284 if (auth == null) { //Authentication failed | |
285 log("Authentication failed"); | |
286 return; | |
287 } | |
288 | |
289 in = auth.getInputStream(); | |
290 out = auth.getOutputStream(); | |
291 msg = readMsg(in); | |
292 handleRequest(msg); | |
293 } | |
294 | |
295 protected void handleRequest(ProxyMessage msg) | |
296 throws IOException { | |
297 if (!auth.checkRequest(msg)) throw new | |
298 SocksException(Proxy.SOCKS_FAILURE); | |
299 | |
300 if (msg.ip == null) { | |
301 if (msg instanceof Socks5Message) { | |
302 msg.ip = InetAddress.getByName(msg.host); | |
303 } | |
304 else | |
305 throw new SocksException(Proxy.SOCKS_FAILURE); | |
306 } | |
307 | |
308 log(msg); | |
309 | |
310 switch (msg.command) { | |
311 case Proxy.SOCKS_CMD_CONNECT: | |
312 onConnect(msg); | |
313 break; | |
314 | |
315 case Proxy.SOCKS_CMD_BIND: | |
316 onBind(msg); | |
317 break; | |
318 | |
319 case Proxy.SOCKS_CMD_UDP_ASSOCIATE: | |
320 onUDP(msg); | |
321 break; | |
322 | |
323 default: | |
324 throw new SocksException(Proxy.SOCKS_CMD_NOT_SUPPORTED); | |
325 } | |
326 } | |
327 | |
328 private void handleException(IOException ioe) { | |
329 //If we couldn't read the request, return; | |
330 if (msg == null) return; | |
331 | |
332 //If have been aborted by other thread | |
333 if (mode == ABORT_MODE) return; | |
334 | |
335 //If the request was successfully completed, but exception happened later | |
336 if (mode == PIPE_MODE) return; | |
337 | |
338 int error_code = Proxy.SOCKS_FAILURE; | |
339 | |
340 if (ioe instanceof SocksException) | |
341 error_code = ((SocksException)ioe).errCode; | |
342 else if (ioe instanceof NoRouteToHostException) | |
343 error_code = Proxy.SOCKS_HOST_UNREACHABLE; | |
344 else if (ioe instanceof ConnectException) | |
345 error_code = Proxy.SOCKS_CONNECTION_REFUSED; | |
346 else if (ioe instanceof InterruptedIOException) | |
347 error_code = Proxy.SOCKS_TTL_EXPIRE; | |
348 | |
349 if (error_code > Proxy.SOCKS_ADDR_NOT_SUPPORTED || error_code < 0) { | |
350 error_code = Proxy.SOCKS_FAILURE; | |
351 } | |
352 | |
353 sendErrorMessage(error_code); | |
354 } | |
355 | |
356 private void onConnect(ProxyMessage msg) throws IOException { | |
357 Socket s; | |
358 ProxyMessage response = null; | |
359 s = new Socket(msg.ip, msg.port); | |
360 log("Connected to " + s.getInetAddress() + ":" + s.getPort()); | |
361 | |
362 if (msg instanceof Socks5Message) { | |
363 response = new Socks5Message(Proxy.SOCKS_SUCCESS, | |
364 s.getLocalAddress(), | |
365 s.getLocalPort()); | |
366 } | |
367 else { | |
368 response = new Socks4Message(Socks4Message.REPLY_OK, | |
369 s.getLocalAddress(), s.getLocalPort()); | |
370 } | |
371 | |
372 response.write(out); | |
373 startPipe(s); | |
374 } | |
375 | |
376 private void onBind(ProxyMessage msg) throws IOException { | |
377 ProxyMessage response = null; | |
378 | |
379 if (proxy == null) | |
380 ss = new ServerSocket(0); | |
381 else | |
382 ss = new SocksServerSocket(proxy, msg.ip, msg.port); | |
383 | |
384 ss.setSoTimeout(acceptTimeout); | |
385 log("Trying accept on " + ss.getInetAddress() + ":" + ss.getLocalPort()); | |
386 | |
387 if (msg.version == 5) | |
388 response = new Socks5Message(Proxy.SOCKS_SUCCESS, ss.getInetAddress(), | |
389 ss.getLocalPort()); | |
390 else | |
391 response = new Socks4Message(Socks4Message.REPLY_OK, | |
392 ss.getInetAddress(), | |
393 ss.getLocalPort()); | |
394 | |
395 response.write(out); | |
396 mode = ACCEPT_MODE; | |
397 pipe_thread1 = Thread.currentThread(); | |
398 pipe_thread2 = new Thread(this); | |
399 pipe_thread2.start(); | |
400 //Make timeout infinit. | |
401 sock.setSoTimeout(0); | |
402 int eof = 0; | |
403 | |
404 try { | |
405 while ((eof = in.read()) >= 0) { | |
406 if (mode != ACCEPT_MODE) { | |
407 if (mode != PIPE_MODE) return; //Accept failed | |
408 | |
409 remote_out.write(eof); | |
410 break; | |
411 } | |
412 } | |
413 } | |
414 catch (EOFException eofe) { | |
415 //System.out.println("EOF exception"); | |
416 return;//Connection closed while we were trying to accept. | |
417 } | |
418 catch (InterruptedIOException iioe) { | |
419 //Accept thread interrupted us. | |
420 //System.out.println("Interrupted"); | |
421 if (mode != PIPE_MODE) | |
422 return;//If accept thread was not successfull return. | |
423 } | |
424 finally { | |
425 //System.out.println("Finnaly!"); | |
426 } | |
427 | |
428 if (eof < 0) //Connection closed while we were trying to accept; | |
429 return; | |
430 | |
431 //Do not restore timeout, instead timeout is set on the | |
432 //remote socket. It does not make any difference. | |
433 pipe(in, remote_out); | |
434 } | |
435 | |
436 private void onUDP(ProxyMessage msg) throws IOException { | |
437 if (msg.ip.getHostAddress().equals("0.0.0.0")) | |
438 msg.ip = sock.getInetAddress(); | |
439 | |
440 log("Creating UDP relay server for " + msg.ip + ":" + msg.port); | |
441 relayServer = new UDPRelayServer(msg.ip, msg.port, | |
442 Thread.currentThread(), sock, auth); | |
443 ProxyMessage response; | |
444 response = new Socks5Message(Proxy.SOCKS_SUCCESS, | |
445 relayServer.relayIP, relayServer.relayPort); | |
446 response.write(out); | |
447 relayServer.start(); | |
448 //Make timeout infinit. | |
449 sock.setSoTimeout(0); | |
450 | |
451 try { | |
452 while (in.read() >= 0) /*do nothing*/; | |
453 } | |
454 catch (EOFException eofe) { | |
455 } | |
456 } | |
457 | 447 |
458 //Private methods | 448 //Private methods |
459 ////////////////// | 449 ////////////////// |
460 | 450 |
461 private void doAccept() throws IOException { | 451 private void doAccept() throws IOException{ |
462 Socket s; | 452 Socket s; |
463 long startTime = System.currentTimeMillis(); | 453 long startTime = System.currentTimeMillis(); |
464 | 454 |
465 while (true) { | 455 while(true){ |
466 s = ss.accept(); | 456 s = ss.accept(); |
467 | 457 if(s.getInetAddress().equals(msg.ip)){ |
468 if (s.getInetAddress().equals(msg.ip)) { | 458 //got the connection from the right host |
469 //got the connection from the right host | 459 //Close listenning socket. |
470 //Close listenning socket. | 460 ss.close(); |
471 ss.close(); | 461 break; |
472 break; | 462 }else if(ss instanceof SocksServerSocket){ |
463 //We can't accept more then one connection | |
464 s.close(); | |
465 ss.close(); | |
466 throw new SocksException(CProxy.SOCKS_FAILURE); | |
467 }else{ | |
468 if(acceptTimeout!=0){ //If timeout is not infinit | |
469 int newTimeout = acceptTimeout-(int)(System.currentTimeMillis()- | |
470 startTime); | |
471 if(newTimeout <= 0) throw new InterruptedIOException( | |
472 "In doAccept()"); | |
473 ss.setSoTimeout(newTimeout); | |
473 } | 474 } |
474 else if (ss instanceof SocksServerSocket) { | 475 s.close(); //Drop all connections from other hosts |
475 //We can't accept more then one connection | 476 } |
476 s.close(); | 477 } |
477 ss.close(); | 478 |
478 throw new SocksException(Proxy.SOCKS_FAILURE); | 479 //Accepted connection |
479 } | 480 remote_sock = s; |
480 else { | 481 remote_in = s.getInputStream(); |
481 if (acceptTimeout != 0) { //If timeout is not infinit | 482 remote_out = s.getOutputStream(); |
482 int newTimeout = acceptTimeout - (int)(System.currentTimeMillis() - | 483 |
483 startTime); | 484 //Set timeout |
484 | 485 remote_sock.setSoTimeout(iddleTimeout); |
485 if (newTimeout <= 0) throw new InterruptedIOException( | 486 |
486 "In doAccept()"); | 487 log("Accepted from "+s.getInetAddress()+":"+s.getPort()); |
487 | 488 |
488 ss.setSoTimeout(newTimeout); | 489 ProxyMessage response; |
489 } | 490 |
490 | 491 if(msg.version == 5) |
491 s.close(); //Drop all connections from other hosts | 492 response = new Socks5Message(CProxy.SOCKS_SUCCESS, s.getInetAddress(), |
492 } | 493 s.getPort()); |
493 } | 494 else |
494 | 495 response = new Socks4Message(Socks4Message.REPLY_OK, |
495 //Accepted connection | 496 s.getInetAddress(), s.getPort()); |
496 remote_sock = s; | 497 response.write(out); |
497 remote_in = s.getInputStream(); | 498 } |
498 remote_out = s.getOutputStream(); | 499 |
499 //Set timeout | 500 private ProxyMessage readMsg(InputStream in) throws IOException{ |
500 remote_sock.setSoTimeout(iddleTimeout); | 501 PushbackInputStream push_in; |
501 log("Accepted from " + s.getInetAddress() + ":" + s.getPort()); | 502 if(in instanceof PushbackInputStream) |
502 ProxyMessage response; | 503 push_in = (PushbackInputStream) in; |
503 | 504 else |
504 if (msg.version == 5) | 505 push_in = new PushbackInputStream(in); |
505 response = new Socks5Message(Proxy.SOCKS_SUCCESS, s.getInetAddress(), | 506 |
506 s.getPort()); | 507 int version = push_in.read(); |
507 else | 508 push_in.unread(version); |
508 response = new Socks4Message(Socks4Message.REPLY_OK, | 509 |
509 s.getInetAddress(), s.getPort()); | 510 |
510 | 511 ProxyMessage msg; |
511 response.write(out); | 512 |
512 } | 513 if(version == 5){ |
513 | 514 msg = new Socks5Message(push_in,false); |
514 protected ProxyMessage readMsg(InputStream in) throws IOException { | 515 }else if(version == 4){ |
515 PushbackInputStream push_in; | 516 msg = new Socks4Message(push_in,false); |
516 | 517 }else{ |
517 if (in instanceof PushbackInputStream) | 518 throw new SocksException(CProxy.SOCKS_FAILURE); |
518 push_in = (PushbackInputStream) in; | 519 } |
519 else | 520 return msg; |
520 push_in = new PushbackInputStream(in); | 521 } |
521 | 522 |
522 int version = push_in.read(); | 523 private void startPipe(Socket s){ |
523 push_in.unread(version); | 524 mode = PIPE_MODE; |
524 ProxyMessage msg; | 525 remote_sock = s; |
525 | 526 try{ |
526 if (version == 5) { | 527 remote_in = s.getInputStream(); |
527 msg = new Socks5Message(push_in, false); | 528 remote_out = s.getOutputStream(); |
528 } | 529 pipe_thread1 = Thread.currentThread(); |
529 else if (version == 4) { | 530 pipe_thread2 = new Thread(this); |
530 msg = new Socks4Message(push_in, false); | 531 pipe_thread2.start(); |
531 } | 532 pipe(in,remote_out); |
532 else { | 533 }catch(IOException ioe){ |
533 throw new SocksException(Proxy.SOCKS_FAILURE); | 534 } |
534 } | 535 } |
535 | 536 |
536 return msg; | 537 private void sendErrorMessage(int error_code){ |
537 } | 538 ProxyMessage err_msg; |
538 | 539 if(msg instanceof Socks4Message) |
539 private void startPipe(Socket s) { | 540 err_msg = new Socks4Message(Socks4Message.REPLY_REJECTED); |
540 mode = PIPE_MODE; | 541 else |
541 remote_sock = s; | 542 err_msg = new Socks5Message(error_code); |
542 | 543 try{ |
543 try { | 544 err_msg.write(out); |
544 remote_in = s.getInputStream(); | 545 }catch(IOException ioe){} |
545 remote_out = s.getOutputStream(); | 546 } |
546 pipe_thread1 = Thread.currentThread(); | 547 |
547 pipe_thread2 = new Thread(this); | 548 private synchronized void abort(){ |
548 pipe_thread2.start(); | 549 if(mode == ABORT_MODE) return; |
549 pipe(in, remote_out); | 550 mode = ABORT_MODE; |
550 } | 551 try{ |
551 catch (IOException ioe) { | 552 log("Aborting operation"); |
552 } | 553 if(remote_sock != null) remote_sock.close(); |
553 } | 554 if(sock != null) sock.close(); |
554 | 555 if(relayServer!=null) relayServer.stop(); |
555 private void sendErrorMessage(int error_code) { | 556 if(ss!=null) ss.close(); |
556 ProxyMessage err_msg; | 557 if(pipe_thread1 != null) pipe_thread1.interrupt(); |
557 | 558 if(pipe_thread2 != null) pipe_thread2.interrupt(); |
558 if (msg instanceof Socks4Message) | 559 }catch(IOException ioe){} |
559 err_msg = new Socks4Message(Socks4Message.REPLY_REJECTED); | 560 } |
560 else | 561 |
561 err_msg = new Socks5Message(error_code); | 562 static final void log(String s){ |
562 | 563 if(log != null){ |
563 try { | 564 log.println(s); |
564 err_msg.write(out); | 565 log.flush(); |
565 } | 566 } |
566 catch (IOException ioe) {} | 567 } |
567 } | 568 |
568 | 569 static final void log(ProxyMessage msg){ |
569 private synchronized void abort() { | 570 log("Request version:"+msg.version+ |
570 if (mode == ABORT_MODE) return; | 571 "\tCommand: "+command2String(msg.command)); |
571 | 572 log("IP:"+msg.ip +"\tPort:"+msg.port+ |
572 mode = ABORT_MODE; | 573 (msg.version==4?"\tUser:"+msg.user:"")); |
573 | 574 } |
574 try { | 575 |
575 log("Aborting operation"); | 576 private void pipe(InputStream in,OutputStream out) throws IOException{ |
576 | 577 lastReadTime = System.currentTimeMillis(); |
577 if (remote_sock != null) remote_sock.close(); | 578 byte[] buf = new byte[BUF_SIZE]; |
578 | 579 int len = 0; |
579 if (sock != null) sock.close(); | 580 while(len >= 0){ |
580 | 581 try{ |
581 if (relayServer != null) relayServer.stop(); | 582 if(len!=0){ |
582 | 583 out.write(buf,0,len); |
583 if (ss != null) ss.close(); | 584 out.flush(); |
584 | 585 } |
585 if (pipe_thread1 != null) pipe_thread1.interrupt(); | 586 len= in.read(buf); |
586 | 587 lastReadTime = System.currentTimeMillis(); |
587 if (pipe_thread2 != null) pipe_thread2.interrupt(); | 588 }catch(InterruptedIOException iioe){ |
588 } | 589 if(iddleTimeout == 0) return;//Other thread interrupted us. |
589 catch (IOException ioe) {} | 590 long timeSinceRead = System.currentTimeMillis() - lastReadTime; |
590 } | 591 if(timeSinceRead >= iddleTimeout - 1000) //-1s for adjustment. |
591 | 592 return; |
592 static final void log(String s) { | 593 len = 0; |
593 if (log != null) { | 594 |
594 log.println(s); | 595 } |
595 log.flush(); | 596 } |
596 } | 597 } |
597 } | 598 static final String command_names[] = {"CONNECT","BIND","UDP_ASSOCIATE"}; |
598 | 599 |
599 static final void log(ProxyMessage msg) { | 600 static final String command2String(int cmd){ |
600 log("Request version:" + msg.version + | 601 if(cmd > 0 && cmd < 4) return command_names[cmd-1]; |
601 "\tCommand: " + command2String(msg.command)); | 602 else return "Unknown Command "+cmd; |
602 log("IP:" + msg.ip + "\tPort:" + msg.port + | 603 } |
603 (msg.version == 4 ? "\tUser:" + msg.user : "")); | |
604 } | |
605 | |
606 private void pipe(InputStream in, OutputStream out) throws IOException { | |
607 lastReadTime = System.currentTimeMillis(); | |
608 byte[] buf = new byte[BUF_SIZE]; | |
609 int len = 0; | |
610 | |
611 while (len >= 0) { | |
612 try { | |
613 if (len != 0) { | |
614 out.write(buf, 0, len); | |
615 out.flush(); | |
616 } | |
617 | |
618 len = in.read(buf); | |
619 lastReadTime = System.currentTimeMillis(); | |
620 } | |
621 catch (InterruptedIOException iioe) { | |
622 if (iddleTimeout == 0) return; //Other thread interrupted us. | |
623 | |
624 long timeSinceRead = System.currentTimeMillis() - lastReadTime; | |
625 | |
626 if (timeSinceRead >= iddleTimeout - 1000) //-1s for adjustment. | |
627 return; | |
628 | |
629 len = 0; | |
630 } | |
631 } | |
632 } | |
633 static final String command_names[] = {"CONNECT", "BIND", "UDP_ASSOCIATE"}; | |
634 | |
635 static final String command2String(int cmd) { | |
636 if (cmd > 0 && cmd < 4) return command_names[cmd - 1]; | |
637 else return "Unknown Command " + cmd; | |
638 } | |
639 } | 604 } |