diff options
author | Kenny Root <kenny@the-b.org> | 2013-04-11 21:01:32 -0700 |
---|---|---|
committer | Kenny Root <kenny@the-b.org> | 2013-04-11 21:01:32 -0700 |
commit | d8e49ec3f297f9ba0991762eb2cddbc00ed634d2 (patch) | |
tree | aac59c707c980e90202241c7a9c306ab9d278a1c /src/net/sourceforge/jsocks/ProxyServer.java | |
parent | 847af5046fb7d2dbcc668cecf81f1726b7c48bd9 (diff) | |
download | connectbot-d8e49ec3f297f9ba0991762eb2cddbc00ed634d2.tar.gz connectbot-d8e49ec3f297f9ba0991762eb2cddbc00ed634d2.tar.bz2 connectbot-d8e49ec3f297f9ba0991762eb2cddbc00ed634d2.zip |
Fix line endings
Diffstat (limited to 'src/net/sourceforge/jsocks/ProxyServer.java')
-rw-r--r-- | src/net/sourceforge/jsocks/ProxyServer.java | 1182 |
1 files changed, 591 insertions, 591 deletions
diff --git a/src/net/sourceforge/jsocks/ProxyServer.java b/src/net/sourceforge/jsocks/ProxyServer.java index 39f6383..225149d 100644 --- a/src/net/sourceforge/jsocks/ProxyServer.java +++ b/src/net/sourceforge/jsocks/ProxyServer.java @@ -1,591 +1,591 @@ -package net.sourceforge.jsocks;
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InterruptedIOException;
-import java.io.OutputStream;
-import java.io.PrintStream;
-import java.io.PushbackInputStream;
-import java.net.ConnectException;
-import java.net.InetAddress;
-import java.net.NoRouteToHostException;
-import java.net.ServerSocket;
-import java.net.Socket;
-
-import net.sourceforge.jsocks.server.ServerAuthenticator;
-
-/**
- SOCKS4 and SOCKS5 proxy, handles both protocols simultaniously.
- Implements all SOCKS commands, including UDP relaying.
- <p>
- In order to use it you will need to implement ServerAuthenticator
- interface. There is an implementation of this interface which does
- no authentication ServerAuthenticatorNone, but it is very dangerous
- to use, as it will give access to your local network to anybody
- in the world. One should never use this authentication scheme unless
- one have pretty good reason to do so.
- There is a couple of other authentication schemes in socks.server package.
- @see socks.server.ServerAuthenticator
-*/
-public class ProxyServer implements Runnable{
-
- ServerAuthenticator auth;
- ProxyMessage msg = null;
-
- Socket sock=null,remote_sock=null;
- ServerSocket ss=null;
- UDPRelayServer relayServer = null;
- InputStream in,remote_in;
- OutputStream out,remote_out;
-
- int mode;
- static final int START_MODE = 0;
- static final int ACCEPT_MODE = 1;
- static final int PIPE_MODE = 2;
- static final int ABORT_MODE = 3;
-
- static final int BUF_SIZE = 8192;
-
- Thread pipe_thread1,pipe_thread2;
- long lastReadTime;
-
- protected static int iddleTimeout = 180000; //3 minutes
- static int acceptTimeout = 180000; //3 minutes
-
- static PrintStream log = null;
- static Proxy proxy;
-
-
-//Public Constructors
-/////////////////////
-
-
- /**
- Creates a proxy server with given Authentication scheme.
- @param auth Authentication scheme to be used.
- */
- public ProxyServer(ServerAuthenticator auth){
- this.auth = auth;
- }
-
-//Other constructors
-////////////////////
-
- protected ProxyServer(ServerAuthenticator auth,Socket s){
- this.auth = auth;
- this.sock = s;
- mode = START_MODE;
- }
-
-//Public methods
-/////////////////
-
- /**
- Set the logging stream. Specifying null disables logging.
- */
- public static void setLog(OutputStream out){
- if(out == null){
- log = null;
- }else{
- log = new PrintStream(out,true);
- }
-
- UDPRelayServer.log = log;
- }
-
- /**
- Set proxy.
- <p>
- Allows Proxy chaining so that one Proxy server is connected to another
- and so on. If proxy supports SOCKSv4, then only some SOCKSv5 requests
- can be handled, UDP would not work, however CONNECT and BIND will be
- translated.
-
- @param p Proxy which should be used to handle user requests.
- */
- public static void setProxy(Proxy p){
- proxy =p;
- UDPRelayServer.proxy = proxy;
- }
-
- /**
- Get proxy.
- @return Proxy wich is used to handle user requests.
- */
- public static Proxy getProxy(){
- return proxy;
- }
-
- /**
- Sets the timeout for connections, how long shoud server wait
- for data to arrive before dropping the connection.<br>
- Zero timeout implies infinity.<br>
- Default timeout is 3 minutes.
- */
- public static void setIddleTimeout(int timeout){
- iddleTimeout = timeout;
- }
- /**
- Sets the timeout for BIND command, how long the server should
- wait for the incoming connection.<br>
- Zero timeout implies infinity.<br>
- Default timeout is 3 minutes.
- */
- public static void setAcceptTimeout(int timeout){
- acceptTimeout = timeout;
- }
-
- /**
- Sets the timeout for UDPRelay server.<br>
- Zero timeout implies infinity.<br>
- Default timeout is 3 minutes.
- */
- public static void setUDPTimeout(int timeout){
- UDPRelayServer.setTimeout(timeout);
- }
-
- /**
- Sets the size of the datagrams used in the UDPRelayServer.<br>
- Default size is 64K, a bit more than maximum possible size of the
- datagram.
- */
- public static void setDatagramSize(int size){
- UDPRelayServer.setDatagramSize(size);
- }
-
-
- /**
- Start the Proxy server at given port.<br>
- This methods blocks.
- */
- public void start(int port){
- start(port,5,null);
- }
-
- /**
- Create a server with the specified port, listen backlog, and local
- IP address to bind to. The localIP argument can be used on a multi-homed
- host for a ServerSocket that will only accept connect requests to one of
- its addresses. If localIP is null, it will default accepting connections
- on any/all local addresses. The port must be between 0 and 65535,
- inclusive. <br>
- This methods blocks.
- */
- public void start(int port,int backlog,InetAddress localIP){
- try{
- ss = new ServerSocket(port,backlog,localIP);
- log("Starting SOCKS Proxy on:"+ss.getInetAddress().getHostAddress()+":"
- +ss.getLocalPort());
- while(true){
- Socket s = ss.accept();
- log("Accepted from:"+s.getInetAddress().getHostName()+":"
- +s.getPort());
- ProxyServer ps = new ProxyServer(auth,s);
- (new Thread(ps)).start();
- }
- }catch(IOException ioe){
- ioe.printStackTrace();
- }finally{
- }
- }
-
- /**
- Stop server operation.It would be wise to interrupt thread running the
- server afterwards.
- */
- public void stop(){
- try{
- if(ss != null) ss.close();
- }catch(IOException ioe){
- }
- }
-
-//Runnable interface
-////////////////////
- public void run(){
- switch(mode){
- case START_MODE:
- try{
- startSession();
- }catch(IOException ioe){
- handleException(ioe);
- //ioe.printStackTrace();
- }finally{
- abort();
- if(auth!=null) auth.endSession();
- log("Main thread(client->remote)stopped.");
- }
- break;
- case ACCEPT_MODE:
- try{
- doAccept();
- mode = PIPE_MODE;
- pipe_thread1.interrupt(); //Tell other thread that connection have
- //been accepted.
- pipe(remote_in,out);
- }catch(IOException ioe){
- //log("Accept exception:"+ioe);
- handleException(ioe);
- }finally{
- abort();
- log("Accept thread(remote->client) stopped");
- }
- break;
- case PIPE_MODE:
- try{
- pipe(remote_in,out);
- }catch(IOException ioe){
- }finally{
- abort();
- log("Support thread(remote->client) stopped");
- }
- break;
- case ABORT_MODE:
- break;
- default:
- log("Unexpected MODE "+mode);
- }
- }
-
-//Private methods
-/////////////////
- private void startSession() throws IOException{
- sock.setSoTimeout(iddleTimeout);
-
- try{
- auth = auth.startSession(sock);
- }catch(IOException ioe){
- log("Auth throwed exception:"+ioe);
- auth = null;
- return;
- }
-
- if(auth == null){ //Authentication failed
- log("Authentication failed");
- return;
- }
-
- in = auth.getInputStream();
- out = auth.getOutputStream();
-
- msg = readMsg(in);
- handleRequest(msg);
- }
-
- protected void handleRequest(ProxyMessage msg)
- throws IOException{
- if(!auth.checkRequest(msg)) throw new
- SocksException(Proxy.SOCKS_FAILURE);
-
- if(msg.ip == null){
- if(msg instanceof Socks5Message){
- msg.ip = InetAddress.getByName(msg.host);
- }else
- throw new SocksException(Proxy.SOCKS_FAILURE);
- }
- log(msg);
-
- switch(msg.command){
- case Proxy.SOCKS_CMD_CONNECT:
- onConnect(msg);
- break;
- case Proxy.SOCKS_CMD_BIND:
- onBind(msg);
- break;
- case Proxy.SOCKS_CMD_UDP_ASSOCIATE:
- onUDP(msg);
- break;
- default:
- throw new SocksException(Proxy.SOCKS_CMD_NOT_SUPPORTED);
- }
- }
-
- private void handleException(IOException ioe){
- //If we couldn't read the request, return;
- if(msg == null) return;
- //If have been aborted by other thread
- if(mode == ABORT_MODE) return;
- //If the request was successfully completed, but exception happened later
- if(mode == PIPE_MODE) return;
-
- int error_code = Proxy.SOCKS_FAILURE;
-
- if(ioe instanceof SocksException)
- error_code = ((SocksException)ioe).errCode;
- else if(ioe instanceof NoRouteToHostException)
- error_code = Proxy.SOCKS_HOST_UNREACHABLE;
- else if(ioe instanceof ConnectException)
- error_code = Proxy.SOCKS_CONNECTION_REFUSED;
- else if(ioe instanceof InterruptedIOException)
- error_code = Proxy.SOCKS_TTL_EXPIRE;
-
- if(error_code > Proxy.SOCKS_ADDR_NOT_SUPPORTED || error_code < 0){
- error_code = Proxy.SOCKS_FAILURE;
- }
-
- sendErrorMessage(error_code);
- }
-
- private void onConnect(ProxyMessage msg) throws IOException{
- Socket s;
- ProxyMessage response = null;
-
- s = new Socket(msg.ip,msg.port);
-
- log("Connected to "+s.getInetAddress()+":"+s.getPort());
-
- if(msg instanceof Socks5Message){
- response = new Socks5Message(Proxy.SOCKS_SUCCESS,
- s.getLocalAddress(),
- s.getLocalPort());
- }else{
- response = new Socks4Message(Socks4Message.REPLY_OK,
- s.getLocalAddress(),s.getLocalPort());
-
- }
- response.write(out);
- startPipe(s);
- }
-
- private void onBind(ProxyMessage msg) throws IOException{
- ProxyMessage response = null;
-
- if(proxy == null)
- ss = new ServerSocket(0);
- else
- ss = new SocksServerSocket(proxy, msg.ip, msg.port);
-
- ss.setSoTimeout(acceptTimeout);
-
- log("Trying accept on "+ss.getInetAddress()+":"+ss.getLocalPort());
-
- if(msg.version == 5)
- response = new Socks5Message(Proxy.SOCKS_SUCCESS,ss.getInetAddress(),
- ss.getLocalPort());
- else
- response = new Socks4Message(Socks4Message.REPLY_OK,
- ss.getInetAddress(),
- ss.getLocalPort());
- response.write(out);
-
- mode = ACCEPT_MODE;
-
- pipe_thread1 = Thread.currentThread();
- pipe_thread2 = new Thread(this);
- pipe_thread2.start();
-
- //Make timeout infinit.
- sock.setSoTimeout(0);
- int eof=0;
-
- try{
- while((eof=in.read())>=0){
- if(mode != ACCEPT_MODE){
- if(mode != PIPE_MODE) return;//Accept failed
-
- remote_out.write(eof);
- break;
- }
- }
- }catch(EOFException eofe){
- //System.out.println("EOF exception");
- return;//Connection closed while we were trying to accept.
- }catch(InterruptedIOException iioe){
- //Accept thread interrupted us.
- //System.out.println("Interrupted");
- if(mode != PIPE_MODE)
- return;//If accept thread was not successfull return.
- }finally{
- //System.out.println("Finnaly!");
- }
-
- if(eof < 0)//Connection closed while we were trying to accept;
- return;
-
- //Do not restore timeout, instead timeout is set on the
- //remote socket. It does not make any difference.
-
- pipe(in,remote_out);
- }
-
- private void onUDP(ProxyMessage msg) throws IOException{
- if(msg.ip.getHostAddress().equals("0.0.0.0"))
- msg.ip = sock.getInetAddress();
- log("Creating UDP relay server for "+msg.ip+":"+msg.port);
- relayServer = new UDPRelayServer(msg.ip,msg.port,
- Thread.currentThread(),sock,auth);
-
- ProxyMessage response;
-
- response = new Socks5Message(Proxy.SOCKS_SUCCESS,
- relayServer.relayIP,relayServer.relayPort);
-
- response.write(out);
-
- relayServer.start();
-
- //Make timeout infinit.
- sock.setSoTimeout(0);
- try{
- while(in.read()>=0) /*do nothing*/;
- }catch(EOFException eofe){
- }
- }
-
-//Private methods
-//////////////////
-
- private void doAccept() throws IOException{
- Socket s;
- long startTime = System.currentTimeMillis();
-
- while(true){
- s = ss.accept();
- if(s.getInetAddress().equals(msg.ip)){
- //got the connection from the right host
- //Close listenning socket.
- ss.close();
- break;
- }else if(ss instanceof SocksServerSocket){
- //We can't accept more then one connection
- s.close();
- ss.close();
- throw new SocksException(Proxy.SOCKS_FAILURE);
- }else{
- if(acceptTimeout!=0){ //If timeout is not infinit
- int newTimeout = acceptTimeout-(int)(System.currentTimeMillis()-
- startTime);
- if(newTimeout <= 0) throw new InterruptedIOException(
- "In doAccept()");
- ss.setSoTimeout(newTimeout);
- }
- s.close(); //Drop all connections from other hosts
- }
- }
-
- //Accepted connection
- remote_sock = s;
- remote_in = s.getInputStream();
- remote_out = s.getOutputStream();
-
- //Set timeout
- remote_sock.setSoTimeout(iddleTimeout);
-
- log("Accepted from "+s.getInetAddress()+":"+s.getPort());
-
- ProxyMessage response;
-
- if(msg.version == 5)
- response = new Socks5Message(Proxy.SOCKS_SUCCESS, s.getInetAddress(),
- s.getPort());
- else
- response = new Socks4Message(Socks4Message.REPLY_OK,
- s.getInetAddress(), s.getPort());
- response.write(out);
- }
-
- protected ProxyMessage readMsg(InputStream in) throws IOException{
- PushbackInputStream push_in;
- if(in instanceof PushbackInputStream)
- push_in = (PushbackInputStream) in;
- else
- push_in = new PushbackInputStream(in);
-
- int version = push_in.read();
- push_in.unread(version);
-
-
- ProxyMessage msg;
-
- if(version == 5){
- msg = new Socks5Message(push_in,false);
- }else if(version == 4){
- msg = new Socks4Message(push_in,false);
- }else{
- throw new SocksException(Proxy.SOCKS_FAILURE);
- }
- return msg;
- }
-
- private void startPipe(Socket s){
- mode = PIPE_MODE;
- remote_sock = s;
- try{
- remote_in = s.getInputStream();
- remote_out = s.getOutputStream();
- pipe_thread1 = Thread.currentThread();
- pipe_thread2 = new Thread(this);
- pipe_thread2.start();
- pipe(in,remote_out);
- }catch(IOException ioe){
- }
- }
-
- private void sendErrorMessage(int error_code){
- ProxyMessage err_msg;
- if(msg instanceof Socks4Message)
- err_msg = new Socks4Message(Socks4Message.REPLY_REJECTED);
- else
- err_msg = new Socks5Message(error_code);
- try{
- err_msg.write(out);
- }catch(IOException ioe){}
- }
-
- private synchronized void abort(){
- if(mode == ABORT_MODE) return;
- mode = ABORT_MODE;
- try{
- log("Aborting operation");
- if(remote_sock != null) remote_sock.close();
- if(sock != null) sock.close();
- if(relayServer!=null) relayServer.stop();
- if(ss!=null) ss.close();
- if(pipe_thread1 != null) pipe_thread1.interrupt();
- if(pipe_thread2 != null) pipe_thread2.interrupt();
- }catch(IOException ioe){}
- }
-
- static final void log(String s){
- if(log != null){
- log.println(s);
- log.flush();
- }
- }
-
- static final void log(ProxyMessage msg){
- log("Request version:"+msg.version+
- "\tCommand: "+command2String(msg.command));
- log("IP:"+msg.ip +"\tPort:"+msg.port+
- (msg.version==4?"\tUser:"+msg.user:""));
- }
-
- private void pipe(InputStream in,OutputStream out) throws IOException{
- lastReadTime = System.currentTimeMillis();
- byte[] buf = new byte[BUF_SIZE];
- int len = 0;
- while(len >= 0){
- try{
- if(len!=0){
- out.write(buf,0,len);
- out.flush();
- }
- len= in.read(buf);
- lastReadTime = System.currentTimeMillis();
- }catch(InterruptedIOException iioe){
- if(iddleTimeout == 0) return;//Other thread interrupted us.
- long timeSinceRead = System.currentTimeMillis() - lastReadTime;
- if(timeSinceRead >= iddleTimeout - 1000) //-1s for adjustment.
- return;
- len = 0;
-
- }
- }
- }
- static final String command_names[] = {"CONNECT","BIND","UDP_ASSOCIATE"};
-
- static final String command2String(int cmd){
- if(cmd > 0 && cmd < 4) return command_names[cmd-1];
- else return "Unknown Command "+cmd;
- }
-}
+package net.sourceforge.jsocks; +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InterruptedIOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.io.PushbackInputStream; +import java.net.ConnectException; +import java.net.InetAddress; +import java.net.NoRouteToHostException; +import java.net.ServerSocket; +import java.net.Socket; + +import net.sourceforge.jsocks.server.ServerAuthenticator; + +/** + SOCKS4 and SOCKS5 proxy, handles both protocols simultaniously. + Implements all SOCKS commands, including UDP relaying. + <p> + In order to use it you will need to implement ServerAuthenticator + interface. There is an implementation of this interface which does + no authentication ServerAuthenticatorNone, but it is very dangerous + to use, as it will give access to your local network to anybody + in the world. One should never use this authentication scheme unless + one have pretty good reason to do so. + There is a couple of other authentication schemes in socks.server package. + @see socks.server.ServerAuthenticator +*/ +public class ProxyServer implements Runnable{ + + ServerAuthenticator auth; + ProxyMessage msg = null; + + Socket sock=null,remote_sock=null; + ServerSocket ss=null; + UDPRelayServer relayServer = null; + InputStream in,remote_in; + OutputStream out,remote_out; + + int mode; + static final int START_MODE = 0; + static final int ACCEPT_MODE = 1; + static final int PIPE_MODE = 2; + static final int ABORT_MODE = 3; + + static final int BUF_SIZE = 8192; + + Thread pipe_thread1,pipe_thread2; + long lastReadTime; + + protected static int iddleTimeout = 180000; //3 minutes + static int acceptTimeout = 180000; //3 minutes + + static PrintStream log = null; + static Proxy proxy; + + +//Public Constructors +///////////////////// + + + /** + Creates a proxy server with given Authentication scheme. + @param auth Authentication scheme to be used. + */ + public ProxyServer(ServerAuthenticator auth){ + this.auth = auth; + } + +//Other constructors +//////////////////// + + protected ProxyServer(ServerAuthenticator auth,Socket s){ + this.auth = auth; + this.sock = s; + mode = START_MODE; + } + +//Public methods +///////////////// + + /** + Set the logging stream. Specifying null disables logging. + */ + public static void setLog(OutputStream out){ + if(out == null){ + log = null; + }else{ + log = new PrintStream(out,true); + } + + UDPRelayServer.log = log; + } + + /** + Set proxy. + <p> + Allows Proxy chaining so that one Proxy server is connected to another + and so on. If proxy supports SOCKSv4, then only some SOCKSv5 requests + can be handled, UDP would not work, however CONNECT and BIND will be + translated. + + @param p Proxy which should be used to handle user requests. + */ + public static void setProxy(Proxy p){ + proxy =p; + UDPRelayServer.proxy = proxy; + } + + /** + Get proxy. + @return Proxy wich is used to handle user requests. + */ + public static Proxy getProxy(){ + return proxy; + } + + /** + Sets the timeout for connections, how long shoud server wait + for data to arrive before dropping the connection.<br> + Zero timeout implies infinity.<br> + Default timeout is 3 minutes. + */ + public static void setIddleTimeout(int timeout){ + iddleTimeout = timeout; + } + /** + Sets the timeout for BIND command, how long the server should + wait for the incoming connection.<br> + Zero timeout implies infinity.<br> + Default timeout is 3 minutes. + */ + public static void setAcceptTimeout(int timeout){ + acceptTimeout = timeout; + } + + /** + Sets the timeout for UDPRelay server.<br> + Zero timeout implies infinity.<br> + Default timeout is 3 minutes. + */ + public static void setUDPTimeout(int timeout){ + UDPRelayServer.setTimeout(timeout); + } + + /** + Sets the size of the datagrams used in the UDPRelayServer.<br> + Default size is 64K, a bit more than maximum possible size of the + datagram. + */ + public static void setDatagramSize(int size){ + UDPRelayServer.setDatagramSize(size); + } + + + /** + Start the Proxy server at given port.<br> + This methods blocks. + */ + public void start(int port){ + start(port,5,null); + } + + /** + Create a server with the specified port, listen backlog, and local + IP address to bind to. The localIP argument can be used on a multi-homed + host for a ServerSocket that will only accept connect requests to one of + its addresses. If localIP is null, it will default accepting connections + on any/all local addresses. The port must be between 0 and 65535, + inclusive. <br> + This methods blocks. + */ + public void start(int port,int backlog,InetAddress localIP){ + try{ + ss = new ServerSocket(port,backlog,localIP); + log("Starting SOCKS Proxy on:"+ss.getInetAddress().getHostAddress()+":" + +ss.getLocalPort()); + while(true){ + Socket s = ss.accept(); + log("Accepted from:"+s.getInetAddress().getHostName()+":" + +s.getPort()); + ProxyServer ps = new ProxyServer(auth,s); + (new Thread(ps)).start(); + } + }catch(IOException ioe){ + ioe.printStackTrace(); + }finally{ + } + } + + /** + Stop server operation.It would be wise to interrupt thread running the + server afterwards. + */ + public void stop(){ + try{ + if(ss != null) ss.close(); + }catch(IOException ioe){ + } + } + +//Runnable interface +//////////////////// + public void run(){ + switch(mode){ + case START_MODE: + try{ + startSession(); + }catch(IOException ioe){ + handleException(ioe); + //ioe.printStackTrace(); + }finally{ + abort(); + if(auth!=null) auth.endSession(); + log("Main thread(client->remote)stopped."); + } + break; + case ACCEPT_MODE: + try{ + doAccept(); + mode = PIPE_MODE; + pipe_thread1.interrupt(); //Tell other thread that connection have + //been accepted. + pipe(remote_in,out); + }catch(IOException ioe){ + //log("Accept exception:"+ioe); + handleException(ioe); + }finally{ + abort(); + log("Accept thread(remote->client) stopped"); + } + break; + case PIPE_MODE: + try{ + pipe(remote_in,out); + }catch(IOException ioe){ + }finally{ + abort(); + log("Support thread(remote->client) stopped"); + } + break; + case ABORT_MODE: + break; + default: + log("Unexpected MODE "+mode); + } + } + +//Private methods +///////////////// + private void startSession() throws IOException{ + sock.setSoTimeout(iddleTimeout); + + try{ + auth = auth.startSession(sock); + }catch(IOException ioe){ + log("Auth throwed exception:"+ioe); + auth = null; + return; + } + + if(auth == null){ //Authentication failed + log("Authentication failed"); + return; + } + + in = auth.getInputStream(); + out = auth.getOutputStream(); + + msg = readMsg(in); + handleRequest(msg); + } + + protected void handleRequest(ProxyMessage msg) + throws IOException{ + if(!auth.checkRequest(msg)) throw new + SocksException(Proxy.SOCKS_FAILURE); + + if(msg.ip == null){ + if(msg instanceof Socks5Message){ + msg.ip = InetAddress.getByName(msg.host); + }else + throw new SocksException(Proxy.SOCKS_FAILURE); + } + log(msg); + + switch(msg.command){ + case Proxy.SOCKS_CMD_CONNECT: + onConnect(msg); + break; + case Proxy.SOCKS_CMD_BIND: + onBind(msg); + break; + case Proxy.SOCKS_CMD_UDP_ASSOCIATE: + onUDP(msg); + break; + default: + throw new SocksException(Proxy.SOCKS_CMD_NOT_SUPPORTED); + } + } + + private void handleException(IOException ioe){ + //If we couldn't read the request, return; + if(msg == null) return; + //If have been aborted by other thread + if(mode == ABORT_MODE) return; + //If the request was successfully completed, but exception happened later + if(mode == PIPE_MODE) return; + + int error_code = Proxy.SOCKS_FAILURE; + + if(ioe instanceof SocksException) + error_code = ((SocksException)ioe).errCode; + else if(ioe instanceof NoRouteToHostException) + error_code = Proxy.SOCKS_HOST_UNREACHABLE; + else if(ioe instanceof ConnectException) + error_code = Proxy.SOCKS_CONNECTION_REFUSED; + else if(ioe instanceof InterruptedIOException) + error_code = Proxy.SOCKS_TTL_EXPIRE; + + if(error_code > Proxy.SOCKS_ADDR_NOT_SUPPORTED || error_code < 0){ + error_code = Proxy.SOCKS_FAILURE; + } + + sendErrorMessage(error_code); + } + + private void onConnect(ProxyMessage msg) throws IOException{ + Socket s; + ProxyMessage response = null; + + s = new Socket(msg.ip,msg.port); + + log("Connected to "+s.getInetAddress()+":"+s.getPort()); + + if(msg instanceof Socks5Message){ + response = new Socks5Message(Proxy.SOCKS_SUCCESS, + s.getLocalAddress(), + s.getLocalPort()); + }else{ + response = new Socks4Message(Socks4Message.REPLY_OK, + s.getLocalAddress(),s.getLocalPort()); + + } + response.write(out); + startPipe(s); + } + + private void onBind(ProxyMessage msg) throws IOException{ + ProxyMessage response = null; + + if(proxy == null) + ss = new ServerSocket(0); + else + ss = new SocksServerSocket(proxy, msg.ip, msg.port); + + ss.setSoTimeout(acceptTimeout); + + log("Trying accept on "+ss.getInetAddress()+":"+ss.getLocalPort()); + + if(msg.version == 5) + response = new Socks5Message(Proxy.SOCKS_SUCCESS,ss.getInetAddress(), + ss.getLocalPort()); + else + response = new Socks4Message(Socks4Message.REPLY_OK, + ss.getInetAddress(), + ss.getLocalPort()); + response.write(out); + + mode = ACCEPT_MODE; + + pipe_thread1 = Thread.currentThread(); + pipe_thread2 = new Thread(this); + pipe_thread2.start(); + + //Make timeout infinit. + sock.setSoTimeout(0); + int eof=0; + + try{ + while((eof=in.read())>=0){ + if(mode != ACCEPT_MODE){ + if(mode != PIPE_MODE) return;//Accept failed + + remote_out.write(eof); + break; + } + } + }catch(EOFException eofe){ + //System.out.println("EOF exception"); + return;//Connection closed while we were trying to accept. + }catch(InterruptedIOException iioe){ + //Accept thread interrupted us. + //System.out.println("Interrupted"); + if(mode != PIPE_MODE) + return;//If accept thread was not successfull return. + }finally{ + //System.out.println("Finnaly!"); + } + + if(eof < 0)//Connection closed while we were trying to accept; + return; + + //Do not restore timeout, instead timeout is set on the + //remote socket. It does not make any difference. + + pipe(in,remote_out); + } + + private void onUDP(ProxyMessage msg) throws IOException{ + if(msg.ip.getHostAddress().equals("0.0.0.0")) + msg.ip = sock.getInetAddress(); + log("Creating UDP relay server for "+msg.ip+":"+msg.port); + relayServer = new UDPRelayServer(msg.ip,msg.port, + Thread.currentThread(),sock,auth); + + ProxyMessage response; + + response = new Socks5Message(Proxy.SOCKS_SUCCESS, + relayServer.relayIP,relayServer.relayPort); + + response.write(out); + + relayServer.start(); + + //Make timeout infinit. + sock.setSoTimeout(0); + try{ + while(in.read()>=0) /*do nothing*/; + }catch(EOFException eofe){ + } + } + +//Private methods +////////////////// + + private void doAccept() throws IOException{ + Socket s; + long startTime = System.currentTimeMillis(); + + while(true){ + s = ss.accept(); + if(s.getInetAddress().equals(msg.ip)){ + //got the connection from the right host + //Close listenning socket. + ss.close(); + break; + }else if(ss instanceof SocksServerSocket){ + //We can't accept more then one connection + s.close(); + ss.close(); + throw new SocksException(Proxy.SOCKS_FAILURE); + }else{ + if(acceptTimeout!=0){ //If timeout is not infinit + int newTimeout = acceptTimeout-(int)(System.currentTimeMillis()- + startTime); + if(newTimeout <= 0) throw new InterruptedIOException( + "In doAccept()"); + ss.setSoTimeout(newTimeout); + } + s.close(); //Drop all connections from other hosts + } + } + + //Accepted connection + remote_sock = s; + remote_in = s.getInputStream(); + remote_out = s.getOutputStream(); + + //Set timeout + remote_sock.setSoTimeout(iddleTimeout); + + log("Accepted from "+s.getInetAddress()+":"+s.getPort()); + + ProxyMessage response; + + if(msg.version == 5) + response = new Socks5Message(Proxy.SOCKS_SUCCESS, s.getInetAddress(), + s.getPort()); + else + response = new Socks4Message(Socks4Message.REPLY_OK, + s.getInetAddress(), s.getPort()); + response.write(out); + } + + protected ProxyMessage readMsg(InputStream in) throws IOException{ + PushbackInputStream push_in; + if(in instanceof PushbackInputStream) + push_in = (PushbackInputStream) in; + else + push_in = new PushbackInputStream(in); + + int version = push_in.read(); + push_in.unread(version); + + + ProxyMessage msg; + + if(version == 5){ + msg = new Socks5Message(push_in,false); + }else if(version == 4){ + msg = new Socks4Message(push_in,false); + }else{ + throw new SocksException(Proxy.SOCKS_FAILURE); + } + return msg; + } + + private void startPipe(Socket s){ + mode = PIPE_MODE; + remote_sock = s; + try{ + remote_in = s.getInputStream(); + remote_out = s.getOutputStream(); + pipe_thread1 = Thread.currentThread(); + pipe_thread2 = new Thread(this); + pipe_thread2.start(); + pipe(in,remote_out); + }catch(IOException ioe){ + } + } + + private void sendErrorMessage(int error_code){ + ProxyMessage err_msg; + if(msg instanceof Socks4Message) + err_msg = new Socks4Message(Socks4Message.REPLY_REJECTED); + else + err_msg = new Socks5Message(error_code); + try{ + err_msg.write(out); + }catch(IOException ioe){} + } + + private synchronized void abort(){ + if(mode == ABORT_MODE) return; + mode = ABORT_MODE; + try{ + log("Aborting operation"); + if(remote_sock != null) remote_sock.close(); + if(sock != null) sock.close(); + if(relayServer!=null) relayServer.stop(); + if(ss!=null) ss.close(); + if(pipe_thread1 != null) pipe_thread1.interrupt(); + if(pipe_thread2 != null) pipe_thread2.interrupt(); + }catch(IOException ioe){} + } + + static final void log(String s){ + if(log != null){ + log.println(s); + log.flush(); + } + } + + static final void log(ProxyMessage msg){ + log("Request version:"+msg.version+ + "\tCommand: "+command2String(msg.command)); + log("IP:"+msg.ip +"\tPort:"+msg.port+ + (msg.version==4?"\tUser:"+msg.user:"")); + } + + private void pipe(InputStream in,OutputStream out) throws IOException{ + lastReadTime = System.currentTimeMillis(); + byte[] buf = new byte[BUF_SIZE]; + int len = 0; + while(len >= 0){ + try{ + if(len!=0){ + out.write(buf,0,len); + out.flush(); + } + len= in.read(buf); + lastReadTime = System.currentTimeMillis(); + }catch(InterruptedIOException iioe){ + if(iddleTimeout == 0) return;//Other thread interrupted us. + long timeSinceRead = System.currentTimeMillis() - lastReadTime; + if(timeSinceRead >= iddleTimeout - 1000) //-1s for adjustment. + return; + len = 0; + + } + } + } + static final String command_names[] = {"CONNECT","BIND","UDP_ASSOCIATE"}; + + static final String command2String(int cmd){ + if(cmd > 0 && cmd < 4) return command_names[cmd-1]; + else return "Unknown Command "+cmd; + } +} |