Mercurial > 510Connectbot
diff src/net/sourceforge/jsocks/UDPRelayServer.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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/net/sourceforge/jsocks/UDPRelayServer.java Thu May 22 10:41:19 2014 -0700 @@ -0,0 +1,212 @@ +package net.sourceforge.jsocks; +import net.sourceforge.jsocks.server.*; +import java.net.*; +import java.io.*; + +/** + UDP Relay server, used by ProxyServer to perform udp forwarding. +*/ +class UDPRelayServer implements Runnable { + + + DatagramSocket client_sock; + DatagramSocket remote_sock; + + Socket controlConnection; + + int relayPort; + InetAddress relayIP; + + Thread pipe_thread1, pipe_thread2; + Thread master_thread; + + ServerAuthenticator auth; + + long lastReadTime; + + static PrintStream log = null; + static Proxy proxy = null; + static int datagramSize = 0xFFFF;//64K, a bit more than max udp size + static int iddleTimeout = 180000;//3 minutes + + + /** + Constructs UDP relay server to communicate with client + on given ip and port. + @param clientIP Address of the client from whom datagrams + will be recieved and to whom they will be forwarded. + @param clientPort Clients port. + @param master_thread Thread which will be interrupted, when + UDP relay server stoppes for some reason. + @param controlConnection Socket which will be closed, before + interrupting the master thread, it is introduced due to a bug + in windows JVM which does not throw InterruptedIOException in + threads which block in I/O operation. + */ + public UDPRelayServer(InetAddress clientIP, int clientPort, + Thread master_thread, + Socket controlConnection, + ServerAuthenticator auth) + throws IOException { + this.master_thread = master_thread; + this.controlConnection = controlConnection; + this.auth = auth; + client_sock = new Socks5DatagramSocket(true, auth.getUdpEncapsulation(), + clientIP, clientPort); + relayPort = client_sock.getLocalPort(); + relayIP = client_sock.getLocalAddress(); + + if (relayIP.getHostAddress().equals("0.0.0.0")) + relayIP = InetAddress.getLocalHost(); + + if (proxy == null) + remote_sock = new DatagramSocket(); + else + remote_sock = new Socks5DatagramSocket(proxy, 0, null); + } + + +//Public methods +///////////////// + + + /** + Sets the timeout for UDPRelay server.<br> + Zero timeout implies infinity.<br> + Default timeout is 3 minutes. + */ + + static public void setTimeout(int timeout) { + iddleTimeout = 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. + */ + static public void setDatagramSize(int size) { + datagramSize = size; + } + + /** + Port to which client should send datagram for association. + */ + public int getRelayPort() { + return relayPort; + } + /** + IP address to which client should send datagrams for association. + */ + public InetAddress getRelayIP() { + return relayIP; + } + + /** + Starts udp relay server. + Spawns two threads of execution and returns. + */ + public void start() throws IOException { + remote_sock.setSoTimeout(iddleTimeout); + client_sock.setSoTimeout(iddleTimeout); + log("Starting UDP relay server on " + relayIP + ":" + relayPort); + log("Remote socket " + remote_sock.getLocalAddress() + ":" + + remote_sock.getLocalPort()); + pipe_thread1 = new Thread(this, "pipe1"); + pipe_thread2 = new Thread(this, "pipe2"); + lastReadTime = System.currentTimeMillis(); + pipe_thread1.start(); + pipe_thread2.start(); + } + + /** + Stops Relay server. + <p> + Does not close control connection, does not interrupt master_thread. + */ + + public synchronized void stop() { + master_thread = null; + controlConnection = null; + abort(); + } + +//Runnable interface +//////////////////// + public void run() { + try { + if (Thread.currentThread().getName().equals("pipe1")) + pipe(remote_sock, client_sock, false); + else + pipe(client_sock, remote_sock, true); + } + catch (IOException ioe) { + } + finally { + abort(); + log("UDP Pipe thread " + Thread.currentThread().getName() + " stopped."); + } + } + +//Private methods +///////////////// + + private synchronized void abort() { + if (pipe_thread1 == null) return; + + log("Aborting UDP Relay Server"); + remote_sock.close(); + client_sock.close(); + + if (controlConnection != null) + try { controlConnection.close();} + catch (IOException ioe) {} + + if (master_thread != null) master_thread.interrupt(); + + pipe_thread1.interrupt(); + pipe_thread2.interrupt(); + pipe_thread1 = null; + } + + + static private void log(String s) { + if (log != null) { + log.println(s); + log.flush(); + } + } + + private void pipe(DatagramSocket from, DatagramSocket to, boolean out) + throws IOException { + byte[] data = new byte[datagramSize]; + DatagramPacket dp = new DatagramPacket(data, data.length); + + while (true) { + try { + from.receive(dp); + lastReadTime = System.currentTimeMillis(); + + if (auth.checkRequest(dp, out)) + to.send(dp); + } + catch (UnknownHostException uhe) { + log("Dropping datagram for unknown host"); + } + catch (InterruptedIOException iioe) { + //log("Interrupted: "+iioe); + //If we were interrupted by other thread. + if (iddleTimeout == 0) return; + + //If last datagram was received, long time ago, return. + long timeSinceRead = System.currentTimeMillis() - lastReadTime; + + if (timeSinceRead >= iddleTimeout - 100) //-100 for adjustment + return; + } + + dp.setLength(data.length); + } + } +}