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);
+        }
+    }
+}