Mercurial > 510Connectbot
comparison app/src/main/java/net/sourceforge/jsocks/ProxyServer.java @ 438:d29cce60f393
migrate from Eclipse to Android Studio
author | Carl Byington <carl@five-ten-sg.com> |
---|---|
date | Thu, 03 Dec 2015 11:23:55 -0800 |
parents | src/net/sourceforge/jsocks/ProxyServer.java@205ee2873330 |
children |
comparison
equal
deleted
inserted
replaced
437:208b31032318 | 438:d29cce60f393 |
---|---|
1 package net.sourceforge.jsocks; | |
2 import net.sourceforge.jsocks.server.ServerAuthenticator; | |
3 import java.net.*; | |
4 import java.io.*; | |
5 | |
6 /** | |
7 SOCKS4 and SOCKS5 proxy, handles both protocols simultaniously. | |
8 Implements all SOCKS commands, including UDP relaying. | |
9 <p> | |
10 In order to use it you will need to implement ServerAuthenticator | |
11 interface. There is an implementation of this interface which does | |
12 no authentication ServerAuthenticatorNone, but it is very dangerous | |
13 to use, as it will give access to your local network to anybody | |
14 in the world. One should never use this authentication scheme unless | |
15 one have pretty good reason to do so. | |
16 There is a couple of other authentication schemes in socks.server package. | |
17 @see socks.server.ServerAuthenticator | |
18 */ | |
19 public class ProxyServer implements Runnable{ | |
20 | |
21 ServerAuthenticator auth; | |
22 ProxyMessage msg = null; | |
23 | |
24 Socket sock=null,remote_sock=null; | |
25 ServerSocket ss=null; | |
26 UDPRelayServer relayServer = null; | |
27 InputStream in,remote_in; | |
28 OutputStream out,remote_out; | |
29 | |
30 int mode; | |
31 static final int START_MODE = 0; | |
32 static final int ACCEPT_MODE = 1; | |
33 static final int PIPE_MODE = 2; | |
34 static final int ABORT_MODE = 3; | |
35 | |
36 static final int BUF_SIZE = 8192; | |
37 | |
38 Thread pipe_thread1,pipe_thread2; | |
39 long lastReadTime; | |
40 | |
41 static int iddleTimeout = 180000; //3 minutes | |
42 static int acceptTimeout = 180000; //3 minutes | |
43 | |
44 static PrintStream log = null; | |
45 static CProxy proxy; | |
46 | |
47 | |
48 //Public Constructors | |
49 ///////////////////// | |
50 | |
51 | |
52 /** | |
53 Creates a proxy server with given Authentication scheme. | |
54 @param auth Authentication scheme to be used. | |
55 */ | |
56 public ProxyServer(ServerAuthenticator auth){ | |
57 this.auth = auth; | |
58 } | |
59 | |
60 //Other constructors | |
61 //////////////////// | |
62 | |
63 ProxyServer(ServerAuthenticator auth,Socket s){ | |
64 this.auth = auth; | |
65 this.sock = s; | |
66 mode = START_MODE; | |
67 } | |
68 | |
69 //Public methods | |
70 ///////////////// | |
71 | |
72 /** | |
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. | |
140 */ | |
141 public static void setDatagramSize(int size){ | |
142 UDPRelayServer.setDatagramSize(size); | |
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); | |
354 } | |
355 else { | |
356 throw (new RuntimeException("onConnect() Failed to create Socket()")); | |
357 } | |
358 | |
359 return; | |
360 } | |
361 | |
362 | |
363 private void onBind(ProxyMessage msg) throws IOException{ | |
364 ProxyMessage response = null; | |
365 | |
366 if(proxy == null) | |
367 ss = new ServerSocket(0); | |
368 else | |
369 ss = new SocksServerSocket(proxy, msg.ip, msg.port); | |
370 | |
371 ss.setSoTimeout(acceptTimeout); | |
372 | |
373 log("Trying accept on "+ss.getInetAddress()+":"+ss.getLocalPort()); | |
374 | |
375 if(msg.version == 5) | |
376 response = new Socks5Message(CProxy.SOCKS_SUCCESS,ss.getInetAddress(), | |
377 ss.getLocalPort()); | |
378 else | |
379 response = new Socks4Message(Socks4Message.REPLY_OK, | |
380 ss.getInetAddress(), | |
381 ss.getLocalPort()); | |
382 response.write(out); | |
383 | |
384 mode = ACCEPT_MODE; | |
385 | |
386 pipe_thread1 = Thread.currentThread(); | |
387 pipe_thread2 = new Thread(this); | |
388 pipe_thread2.start(); | |
389 | |
390 //Make timeout infinit. | |
391 sock.setSoTimeout(0); | |
392 int eof=0; | |
393 | |
394 try{ | |
395 while((eof=in.read())>=0){ | |
396 if(mode != ACCEPT_MODE){ | |
397 if(mode != PIPE_MODE) return;//Accept failed | |
398 | |
399 remote_out.write(eof); | |
400 break; | |
401 } | |
402 } | |
403 }catch(EOFException eofe){ | |
404 //System.out.println("EOF exception"); | |
405 return;//Connection closed while we were trying to accept. | |
406 }catch(InterruptedIOException iioe){ | |
407 //Accept thread interrupted us. | |
408 //System.out.println("Interrupted"); | |
409 if(mode != PIPE_MODE) | |
410 return;//If accept thread was not successfull return. | |
411 }finally{ | |
412 //System.out.println("Finnaly!"); | |
413 } | |
414 | |
415 if(eof < 0)//Connection closed while we were trying to accept; | |
416 return; | |
417 | |
418 //Do not restore timeout, instead timeout is set on the | |
419 //remote socket. It does not make any difference. | |
420 | |
421 pipe(in,remote_out); | |
422 } | |
423 | |
424 private void onUDP(ProxyMessage msg) throws IOException{ | |
425 if(msg.ip.getHostAddress().equals("0.0.0.0")) | |
426 msg.ip = sock.getInetAddress(); | |
427 log("Creating UDP relay server for "+msg.ip+":"+msg.port); | |
428 relayServer = new UDPRelayServer(msg.ip,msg.port, | |
429 Thread.currentThread(),sock,auth); | |
430 | |
431 ProxyMessage response; | |
432 | |
433 response = new Socks5Message(CProxy.SOCKS_SUCCESS, | |
434 relayServer.relayIP,relayServer.relayPort); | |
435 | |
436 response.write(out); | |
437 | |
438 relayServer.start(); | |
439 | |
440 //Make timeout infinit. | |
441 sock.setSoTimeout(0); | |
442 try{ | |
443 while(in.read()>=0) /*do nothing*/; | |
444 }catch(EOFException eofe){ | |
445 } | |
446 } | |
447 | |
448 //Private methods | |
449 ////////////////// | |
450 | |
451 private void doAccept() throws IOException{ | |
452 Socket s; | |
453 long startTime = System.currentTimeMillis(); | |
454 | |
455 while(true){ | |
456 s = ss.accept(); | |
457 if(s.getInetAddress().equals(msg.ip)){ | |
458 //got the connection from the right host | |
459 //Close listenning socket. | |
460 ss.close(); | |
461 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); | |
474 } | |
475 s.close(); //Drop all connections from other hosts | |
476 } | |
477 } | |
478 | |
479 //Accepted connection | |
480 remote_sock = s; | |
481 remote_in = s.getInputStream(); | |
482 remote_out = s.getOutputStream(); | |
483 | |
484 //Set timeout | |
485 remote_sock.setSoTimeout(iddleTimeout); | |
486 | |
487 log("Accepted from "+s.getInetAddress()+":"+s.getPort()); | |
488 | |
489 ProxyMessage response; | |
490 | |
491 if(msg.version == 5) | |
492 response = new Socks5Message(CProxy.SOCKS_SUCCESS, s.getInetAddress(), | |
493 s.getPort()); | |
494 else | |
495 response = new Socks4Message(Socks4Message.REPLY_OK, | |
496 s.getInetAddress(), s.getPort()); | |
497 response.write(out); | |
498 } | |
499 | |
500 private ProxyMessage readMsg(InputStream in) throws IOException{ | |
501 PushbackInputStream push_in; | |
502 if(in instanceof PushbackInputStream) | |
503 push_in = (PushbackInputStream) in; | |
504 else | |
505 push_in = new PushbackInputStream(in); | |
506 | |
507 int version = push_in.read(); | |
508 push_in.unread(version); | |
509 | |
510 | |
511 ProxyMessage msg; | |
512 | |
513 if(version == 5){ | |
514 msg = new Socks5Message(push_in,false); | |
515 }else if(version == 4){ | |
516 msg = new Socks4Message(push_in,false); | |
517 }else{ | |
518 throw new SocksException(CProxy.SOCKS_FAILURE); | |
519 } | |
520 return msg; | |
521 } | |
522 | |
523 private void startPipe(Socket s){ | |
524 mode = PIPE_MODE; | |
525 remote_sock = s; | |
526 try{ | |
527 remote_in = s.getInputStream(); | |
528 remote_out = s.getOutputStream(); | |
529 pipe_thread1 = Thread.currentThread(); | |
530 pipe_thread2 = new Thread(this); | |
531 pipe_thread2.start(); | |
532 pipe(in,remote_out); | |
533 }catch(IOException ioe){ | |
534 } | |
535 } | |
536 | |
537 private void sendErrorMessage(int error_code){ | |
538 ProxyMessage err_msg; | |
539 if(msg instanceof Socks4Message) | |
540 err_msg = new Socks4Message(Socks4Message.REPLY_REJECTED); | |
541 else | |
542 err_msg = new Socks5Message(error_code); | |
543 try{ | |
544 err_msg.write(out); | |
545 }catch(IOException ioe){} | |
546 } | |
547 | |
548 private synchronized void abort(){ | |
549 if(mode == ABORT_MODE) return; | |
550 mode = ABORT_MODE; | |
551 try{ | |
552 log("Aborting operation"); | |
553 if(remote_sock != null) remote_sock.close(); | |
554 if(sock != null) sock.close(); | |
555 if(relayServer!=null) relayServer.stop(); | |
556 if(ss!=null) ss.close(); | |
557 if(pipe_thread1 != null) pipe_thread1.interrupt(); | |
558 if(pipe_thread2 != null) pipe_thread2.interrupt(); | |
559 }catch(IOException ioe){} | |
560 } | |
561 | |
562 static final void log(String s){ | |
563 if(log != null){ | |
564 log.println(s); | |
565 log.flush(); | |
566 } | |
567 } | |
568 | |
569 static final void log(ProxyMessage msg){ | |
570 log("Request version:"+msg.version+ | |
571 "\tCommand: "+command2String(msg.command)); | |
572 log("IP:"+msg.ip +"\tPort:"+msg.port+ | |
573 (msg.version==4?"\tUser:"+msg.user:"")); | |
574 } | |
575 | |
576 private void pipe(InputStream in,OutputStream out) throws IOException{ | |
577 lastReadTime = System.currentTimeMillis(); | |
578 byte[] buf = new byte[BUF_SIZE]; | |
579 int len = 0; | |
580 while(len >= 0){ | |
581 try{ | |
582 if(len!=0){ | |
583 out.write(buf,0,len); | |
584 out.flush(); | |
585 } | |
586 len= in.read(buf); | |
587 lastReadTime = System.currentTimeMillis(); | |
588 }catch(InterruptedIOException iioe){ | |
589 if(iddleTimeout == 0) return;//Other thread interrupted us. | |
590 long timeSinceRead = System.currentTimeMillis() - lastReadTime; | |
591 if(timeSinceRead >= iddleTimeout - 1000) //-1s for adjustment. | |
592 return; | |
593 len = 0; | |
594 | |
595 } | |
596 } | |
597 } | |
598 static final String command_names[] = {"CONNECT","BIND","UDP_ASSOCIATE"}; | |
599 | |
600 static final String command2String(int cmd){ | |
601 if(cmd > 0 && cmd < 4) return command_names[cmd-1]; | |
602 else return "Unknown Command "+cmd; | |
603 } | |
604 } |