comparison src/net/sourceforge/jsocks/ProxyServer.java @ 0:0ce5cc452d02

initial version
author Carl Byington <carl@five-ten-sg.com>
date Thu, 22 May 2014 10:41:19 -0700
parents
children 205ee2873330
comparison
equal deleted inserted replaced
-1:000000000000 0:0ce5cc452d02
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;
16
17 /**
18 SOCKS4 and SOCKS5 proxy, handles both protocols simultaniously.
19 Implements all SOCKS commands, including UDP relaying.
20 <p>
21 In order to use it you will need to implement ServerAuthenticator
22 interface. There is an implementation of this interface which does
23 no authentication ServerAuthenticatorNone, but it is very dangerous
24 to use, as it will give access to your local network to anybody
25 in the world. One should never use this authentication scheme unless
26 one have pretty good reason to do so.
27 There is a couple of other authentication schemes in socks.server package.
28 @see socks.server.ServerAuthenticator
29 */
30 public class ProxyServer implements Runnable {
31
32 ServerAuthenticator auth;
33 ProxyMessage msg = null;
34
35 Socket sock = null, remote_sock = null;
36 ServerSocket ss = null;
37 UDPRelayServer relayServer = null;
38 InputStream in, remote_in;
39 OutputStream out, remote_out;
40
41 int mode;
42 static final int START_MODE = 0;
43 static final int ACCEPT_MODE = 1;
44 static final int PIPE_MODE = 2;
45 static final int ABORT_MODE = 3;
46
47 static final int BUF_SIZE = 8192;
48
49 Thread pipe_thread1, pipe_thread2;
50 long lastReadTime;
51
52 protected static int iddleTimeout = 180000; //3 minutes
53 static int acceptTimeout = 180000; //3 minutes
54
55 static PrintStream log = null;
56 static Proxy proxy;
57
58
59 //Public Constructors
60 /////////////////////
61
62
63 /**
64 Creates a proxy server with given Authentication scheme.
65 @param auth Authentication scheme to be used.
66 */
67 public ProxyServer(ServerAuthenticator auth) {
68 this.auth = auth;
69 }
70
71 //Other constructors
72 ////////////////////
73
74 protected ProxyServer(ServerAuthenticator auth, Socket s) {
75 this.auth = auth;
76 this.sock = s;
77 mode = START_MODE;
78 }
79
80 //Public methods
81 /////////////////
82
83 /**
84 Set the logging stream. Specifying null disables logging.
85 */
86 public static void setLog(OutputStream out) {
87 if (out == null) {
88 log = null;
89 }
90 else {
91 log = new PrintStream(out, true);
92 }
93
94 UDPRelayServer.log = log;
95 }
96
97 /**
98 Set proxy.
99 <p>
100 Allows Proxy chaining so that one Proxy server is connected to another
101 and so on. If proxy supports SOCKSv4, then only some SOCKSv5 requests
102 can be handled, UDP would not work, however CONNECT and BIND will be
103 translated.
104
105 @param p Proxy which should be used to handle user requests.
106 */
107 public static void setProxy(Proxy p) {
108 proxy = p;
109 UDPRelayServer.proxy = proxy;
110 }
111
112 /**
113 Get proxy.
114 @return Proxy wich is used to handle user requests.
115 */
116 public static Proxy getProxy() {
117 return proxy;
118 }
119
120 /**
121 Sets the timeout for connections, how long shoud server wait
122 for data to arrive before dropping the connection.<br>
123 Zero timeout implies infinity.<br>
124 Default timeout is 3 minutes.
125 */
126 public static void setIddleTimeout(int timeout) {
127 iddleTimeout = timeout;
128 }
129 /**
130 Sets the timeout for BIND command, how long the server should
131 wait for the incoming connection.<br>
132 Zero timeout implies infinity.<br>
133 Default timeout is 3 minutes.
134 */
135 public static void setAcceptTimeout(int timeout) {
136 acceptTimeout = timeout;
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 }
189 catch (IOException ioe) {
190 ioe.printStackTrace();
191 }
192 finally {
193 }
194 }
195
196 /**
197 Stop server operation.It would be wise to interrupt thread running the
198 server afterwards.
199 */
200 public void stop() {
201 try {
202 if (ss != null) ss.close();
203 }
204 catch (IOException ioe) {
205 }
206 }
207
208 //Runnable interface
209 ////////////////////
210 public void run() {
211 switch (mode) {
212 case START_MODE:
213 try {
214 startSession();
215 }
216 catch (IOException ioe) {
217 handleException(ioe);
218 //ioe.printStackTrace();
219 }
220 finally {
221 abort();
222
223 if (auth != null) auth.endSession();
224
225 log("Main thread(client->remote)stopped.");
226 }
227
228 break;
229
230 case ACCEPT_MODE:
231 try {
232 doAccept();
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
458 //Private methods
459 //////////////////
460
461 private void doAccept() throws IOException {
462 Socket s;
463 long startTime = System.currentTimeMillis();
464
465 while (true) {
466 s = ss.accept();
467
468 if (s.getInetAddress().equals(msg.ip)) {
469 //got the connection from the right host
470 //Close listenning socket.
471 ss.close();
472 break;
473 }
474 else if (ss instanceof SocksServerSocket) {
475 //We can't accept more then one connection
476 s.close();
477 ss.close();
478 throw new SocksException(Proxy.SOCKS_FAILURE);
479 }
480 else {
481 if (acceptTimeout != 0) { //If timeout is not infinit
482 int newTimeout = acceptTimeout - (int)(System.currentTimeMillis() -
483 startTime);
484
485 if (newTimeout <= 0) throw new InterruptedIOException(
486 "In doAccept()");
487
488 ss.setSoTimeout(newTimeout);
489 }
490
491 s.close(); //Drop all connections from other hosts
492 }
493 }
494
495 //Accepted connection
496 remote_sock = s;
497 remote_in = s.getInputStream();
498 remote_out = s.getOutputStream();
499 //Set timeout
500 remote_sock.setSoTimeout(iddleTimeout);
501 log("Accepted from " + s.getInetAddress() + ":" + s.getPort());
502 ProxyMessage response;
503
504 if (msg.version == 5)
505 response = new Socks5Message(Proxy.SOCKS_SUCCESS, s.getInetAddress(),
506 s.getPort());
507 else
508 response = new Socks4Message(Socks4Message.REPLY_OK,
509 s.getInetAddress(), s.getPort());
510
511 response.write(out);
512 }
513
514 protected ProxyMessage readMsg(InputStream in) throws IOException {
515 PushbackInputStream push_in;
516
517 if (in instanceof PushbackInputStream)
518 push_in = (PushbackInputStream) in;
519 else
520 push_in = new PushbackInputStream(in);
521
522 int version = push_in.read();
523 push_in.unread(version);
524 ProxyMessage msg;
525
526 if (version == 5) {
527 msg = new Socks5Message(push_in, false);
528 }
529 else if (version == 4) {
530 msg = new Socks4Message(push_in, false);
531 }
532 else {
533 throw new SocksException(Proxy.SOCKS_FAILURE);
534 }
535
536 return msg;
537 }
538
539 private void startPipe(Socket s) {
540 mode = PIPE_MODE;
541 remote_sock = s;
542
543 try {
544 remote_in = s.getInputStream();
545 remote_out = s.getOutputStream();
546 pipe_thread1 = Thread.currentThread();
547 pipe_thread2 = new Thread(this);
548 pipe_thread2.start();
549 pipe(in, remote_out);
550 }
551 catch (IOException ioe) {
552 }
553 }
554
555 private void sendErrorMessage(int error_code) {
556 ProxyMessage err_msg;
557
558 if (msg instanceof Socks4Message)
559 err_msg = new Socks4Message(Socks4Message.REPLY_REJECTED);
560 else
561 err_msg = new Socks5Message(error_code);
562
563 try {
564 err_msg.write(out);
565 }
566 catch (IOException ioe) {}
567 }
568
569 private synchronized void abort() {
570 if (mode == ABORT_MODE) return;
571
572 mode = ABORT_MODE;
573
574 try {
575 log("Aborting operation");
576
577 if (remote_sock != null) remote_sock.close();
578
579 if (sock != null) sock.close();
580
581 if (relayServer != null) relayServer.stop();
582
583 if (ss != null) ss.close();
584
585 if (pipe_thread1 != null) pipe_thread1.interrupt();
586
587 if (pipe_thread2 != null) pipe_thread2.interrupt();
588 }
589 catch (IOException ioe) {}
590 }
591
592 static final void log(String s) {
593 if (log != null) {
594 log.println(s);
595 log.flush();
596 }
597 }
598
599 static final void log(ProxyMessage msg) {
600 log("Request version:" + msg.version +
601 "\tCommand: " + command2String(msg.command));
602 log("IP:" + msg.ip + "\tPort:" + msg.port +
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 }