Mercurial > 510Connectbot
diff 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 |
line wrap: on
line diff
--- a/src/net/sourceforge/jsocks/ProxyServer.java Fri Aug 01 10:25:44 2014 -0700 +++ b/src/net/sourceforge/jsocks/ProxyServer.java Fri Aug 01 11:23:10 2014 -0700 @@ -1,639 +1,604 @@ -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 net.sourceforge.jsocks.server.ServerAuthenticator; +import java.net.*; +import java.io.*; + +/** + 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; + + static int iddleTimeout = 180000; //3 minutes + static int acceptTimeout = 180000; //3 minutes + + static PrintStream log = null; + static CProxy 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 +//////////////////// + + 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 CProxy chaining so that one CProxy 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 CProxy which should be used to handle user requests. + */ + public static void setProxy(CProxy p){ + proxy =p; + UDPRelayServer.proxy = proxy; + } + + /** + Get proxy. + @return CProxy wich is used to handle user requests. + */ + public static CProxy 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 CProxy 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); + } + + private void handleRequest(ProxyMessage msg) + throws IOException{ + if(!auth.checkRequest(msg)) throw new + SocksException(CProxy.SOCKS_FAILURE); + + if(msg.ip == null){ + if(msg instanceof Socks5Message){ + msg.ip = InetAddress.getByName(msg.host); + }else + throw new SocksException(CProxy.SOCKS_FAILURE); + } + log(msg); + + switch(msg.command){ + case CProxy.SOCKS_CMD_CONNECT: + onConnect(msg); + break; + case CProxy.SOCKS_CMD_BIND: + onBind(msg); + break; + case CProxy.SOCKS_CMD_UDP_ASSOCIATE: + onUDP(msg); + break; + default: + throw new SocksException(CProxy.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 = CProxy.SOCKS_FAILURE; + + if(ioe instanceof SocksException) + error_code = ((SocksException)ioe).errCode; + else if(ioe instanceof NoRouteToHostException) + error_code = CProxy.SOCKS_HOST_UNREACHABLE; + else if(ioe instanceof ConnectException) + error_code = CProxy.SOCKS_CONNECTION_REFUSED; + else if(ioe instanceof InterruptedIOException) + error_code = CProxy.SOCKS_TTL_EXPIRE; + + if(error_code > CProxy.SOCKS_ADDR_NOT_SUPPORTED || error_code < 0){ + error_code = CProxy.SOCKS_FAILURE; + } + + sendErrorMessage(error_code); + } + + private void onConnect(ProxyMessage msg) throws IOException { + Socket s = null; + ProxyMessage response = null; + int iSock5Cmd = CProxy.SOCKS_FAILURE; //defaulting to failure + int iSock4Msg = Socks4Message.REPLY_NO_CONNECT; + InetAddress sIp = null; int iPort = 0; + + try { + if (proxy == null) { + s = new Socket(msg.ip, msg.port); + } else { + s = new SocksSocket(proxy, msg.ip, msg.port); + } + log("Connected to " + s.getInetAddress() + ":" + s.getPort()); + + iSock5Cmd = CProxy.SOCKS_SUCCESS; iSock4Msg = Socks4Message.REPLY_OK; + sIp = s.getInetAddress(); iPort = s.getPort(); + + } + catch (Exception sE) { + log("Failed connecting to remote socket. Exception: " + sE.getLocalizedMessage()); + + //TBD Pick proper socks error for corresponding socket error, below is too generic + iSock5Cmd = CProxy.SOCKS_CONNECTION_REFUSED; iSock4Msg = Socks4Message.REPLY_NO_CONNECT; + } + + if (msg instanceof Socks5Message) { + response = new Socks5Message(iSock5Cmd, sIp, iPort); + } else { + response = new Socks4Message(iSock4Msg, sIp, iPort); + } + + response.write(out); + + if (s != null) { + startPipe(s); + } + else { + throw (new RuntimeException("onConnect() Failed to create Socket()")); + } + + return; + } + + + 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(CProxy.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(CProxy.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(CProxy.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(CProxy.SOCKS_SUCCESS, s.getInetAddress(), + s.getPort()); + else + response = new Socks4Message(Socks4Message.REPLY_OK, + s.getInetAddress(), s.getPort()); + response.write(out); + } + + private 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(CProxy.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; + } +}