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 }