[1001] | 1 | package nu.fw.jeti.plugins.filetransfer.socks5.jsocks; |
---|
| 2 | import java.io.IOException; |
---|
| 3 | import java.io.InterruptedIOException; |
---|
| 4 | import java.io.PrintStream; |
---|
| 5 | import java.net.*; |
---|
| 6 | |
---|
| 7 | |
---|
| 8 | /** |
---|
| 9 | UDP Relay server, used by ProxyServer to perform udp forwarding. |
---|
| 10 | */ |
---|
| 11 | class UDPRelayServer implements Runnable{ |
---|
| 12 | |
---|
| 13 | |
---|
| 14 | DatagramSocket client_sock; |
---|
| 15 | DatagramSocket remote_sock; |
---|
| 16 | |
---|
| 17 | Socket controlConnection; |
---|
| 18 | |
---|
| 19 | int relayPort; |
---|
| 20 | InetAddress relayIP; |
---|
| 21 | |
---|
| 22 | Thread pipe_thread1,pipe_thread2; |
---|
| 23 | Thread master_thread; |
---|
| 24 | |
---|
| 25 | ServerAuthenticator auth; |
---|
| 26 | |
---|
| 27 | long lastReadTime; |
---|
| 28 | |
---|
| 29 | static PrintStream log = null; |
---|
| 30 | static Proxy proxy = null; |
---|
| 31 | static int datagramSize = 0xFFFF;//64K, a bit more than max udp size |
---|
| 32 | static int iddleTimeout = 180000;//3 minutes |
---|
| 33 | |
---|
| 34 | |
---|
| 35 | /** |
---|
| 36 | Constructs UDP relay server to communicate with client |
---|
| 37 | on given ip and port. |
---|
| 38 | @param clientIP Address of the client from whom datagrams |
---|
| 39 | will be recieved and to whom they will be forwarded. |
---|
| 40 | @param clientPort Clients port. |
---|
| 41 | @param master_thread Thread which will be interrupted, when |
---|
| 42 | UDP relay server stoppes for some reason. |
---|
| 43 | @param controlConnection Socket which will be closed, before |
---|
| 44 | interrupting the master thread, it is introduced due to a bug |
---|
| 45 | in windows JVM which does not throw InterruptedIOException in |
---|
| 46 | threads which block in I/O operation. |
---|
| 47 | */ |
---|
| 48 | public UDPRelayServer(InetAddress clientIP,int clientPort, |
---|
| 49 | Thread master_thread, |
---|
| 50 | Socket controlConnection, |
---|
| 51 | ServerAuthenticator auth) |
---|
| 52 | throws IOException{ |
---|
| 53 | this.master_thread = master_thread; |
---|
| 54 | this.controlConnection = controlConnection; |
---|
| 55 | this.auth = auth; |
---|
| 56 | |
---|
| 57 | client_sock = new Socks5DatagramSocket(true,auth.getUdpEncapsulation(), |
---|
| 58 | clientIP,clientPort); |
---|
| 59 | relayPort = client_sock.getLocalPort(); |
---|
| 60 | relayIP = client_sock.getLocalAddress(); |
---|
| 61 | |
---|
| 62 | if(relayIP.getHostAddress().equals("0.0.0.0")) |
---|
| 63 | relayIP = InetAddress.getLocalHost(); |
---|
| 64 | |
---|
| 65 | if(proxy == null) |
---|
| 66 | remote_sock = new DatagramSocket(); |
---|
| 67 | else |
---|
| 68 | remote_sock = new Socks5DatagramSocket(proxy,0,null); |
---|
| 69 | } |
---|
| 70 | |
---|
| 71 | |
---|
| 72 | //Public methods |
---|
| 73 | ///////////////// |
---|
| 74 | |
---|
| 75 | |
---|
| 76 | /** |
---|
| 77 | Sets the timeout for UDPRelay server.<br> |
---|
| 78 | Zero timeout implies infinity.<br> |
---|
| 79 | Default timeout is 3 minutes. |
---|
| 80 | */ |
---|
| 81 | |
---|
| 82 | static public void setTimeout(int timeout){ |
---|
| 83 | iddleTimeout = timeout; |
---|
| 84 | } |
---|
| 85 | |
---|
| 86 | |
---|
| 87 | /** |
---|
| 88 | Sets the size of the datagrams used in the UDPRelayServer.<br> |
---|
| 89 | Default size is 64K, a bit more than maximum possible size of the |
---|
| 90 | datagram. |
---|
| 91 | */ |
---|
| 92 | static public void setDatagramSize(int size){ |
---|
| 93 | datagramSize = size; |
---|
| 94 | } |
---|
| 95 | |
---|
| 96 | /** |
---|
| 97 | Port to which client should send datagram for association. |
---|
| 98 | */ |
---|
| 99 | public int getRelayPort(){ |
---|
| 100 | return relayPort; |
---|
| 101 | } |
---|
| 102 | /** |
---|
| 103 | IP address to which client should send datagrams for association. |
---|
| 104 | */ |
---|
| 105 | public InetAddress getRelayIP(){ |
---|
| 106 | return relayIP; |
---|
| 107 | } |
---|
| 108 | |
---|
| 109 | /** |
---|
| 110 | Starts udp relay server. |
---|
| 111 | Spawns two threads of execution and returns. |
---|
| 112 | */ |
---|
| 113 | public void start() throws IOException{ |
---|
| 114 | remote_sock.setSoTimeout(iddleTimeout); |
---|
| 115 | client_sock.setSoTimeout(iddleTimeout); |
---|
| 116 | |
---|
| 117 | log("Starting UDP relay server on "+relayIP+":"+relayPort); |
---|
| 118 | log("Remote socket "+remote_sock.getLocalAddress()+":"+ |
---|
| 119 | remote_sock.getLocalPort()); |
---|
| 120 | |
---|
| 121 | pipe_thread1 = new Thread(this,"pipe1"); |
---|
| 122 | pipe_thread2 = new Thread(this,"pipe2"); |
---|
| 123 | |
---|
| 124 | lastReadTime = System.currentTimeMillis(); |
---|
| 125 | |
---|
| 126 | pipe_thread1.start(); |
---|
| 127 | pipe_thread2.start(); |
---|
| 128 | } |
---|
| 129 | |
---|
| 130 | /** |
---|
| 131 | Stops Relay server. |
---|
| 132 | <p> |
---|
| 133 | Does not close control connection, does not interrupt master_thread. |
---|
| 134 | */ |
---|
| 135 | public synchronized void stop(){ |
---|
| 136 | master_thread = null; |
---|
| 137 | controlConnection = null; |
---|
| 138 | abort(); |
---|
| 139 | } |
---|
| 140 | |
---|
| 141 | //Runnable interface |
---|
| 142 | //////////////////// |
---|
| 143 | public void run(){ |
---|
| 144 | try{ |
---|
| 145 | if(Thread.currentThread().getName().equals("pipe1")) |
---|
| 146 | pipe(remote_sock,client_sock,false); |
---|
| 147 | else |
---|
| 148 | pipe(client_sock,remote_sock,true); |
---|
| 149 | }catch(IOException ioe){ |
---|
| 150 | }finally{ |
---|
| 151 | abort(); |
---|
| 152 | log("UDP Pipe thread "+Thread.currentThread().getName()+" stopped."); |
---|
| 153 | } |
---|
| 154 | |
---|
| 155 | } |
---|
| 156 | |
---|
| 157 | //Private methods |
---|
| 158 | ///////////////// |
---|
| 159 | private synchronized void abort(){ |
---|
| 160 | if(pipe_thread1 == null) return; |
---|
| 161 | |
---|
| 162 | log("Aborting UDP Relay Server"); |
---|
| 163 | |
---|
| 164 | remote_sock.close(); |
---|
| 165 | client_sock.close(); |
---|
| 166 | |
---|
| 167 | if(controlConnection != null) |
---|
| 168 | try{ controlConnection.close();} catch(IOException ioe){} |
---|
| 169 | |
---|
| 170 | if(master_thread!=null) master_thread.interrupt(); |
---|
| 171 | |
---|
| 172 | pipe_thread1.interrupt(); |
---|
| 173 | pipe_thread2.interrupt(); |
---|
| 174 | |
---|
| 175 | pipe_thread1 = null; |
---|
| 176 | } |
---|
| 177 | |
---|
| 178 | |
---|
| 179 | static private void log(String s){ |
---|
| 180 | if(log != null){ |
---|
| 181 | log.println(s); |
---|
| 182 | log.flush(); |
---|
| 183 | } |
---|
| 184 | } |
---|
| 185 | |
---|
| 186 | private void pipe(DatagramSocket from,DatagramSocket to,boolean out) |
---|
| 187 | throws IOException{ |
---|
| 188 | byte[] data = new byte[datagramSize]; |
---|
| 189 | DatagramPacket dp = new DatagramPacket(data,data.length); |
---|
| 190 | |
---|
| 191 | while(true){ |
---|
| 192 | try{ |
---|
| 193 | from.receive(dp); |
---|
| 194 | lastReadTime = System.currentTimeMillis(); |
---|
| 195 | |
---|
| 196 | if(auth.checkRequest(dp,out)) |
---|
| 197 | to.send(dp); |
---|
| 198 | |
---|
| 199 | }catch(UnknownHostException uhe){ |
---|
| 200 | log("Dropping datagram for unknown host"); |
---|
| 201 | }catch(InterruptedIOException iioe){ |
---|
| 202 | //log("Interrupted: "+iioe); |
---|
| 203 | //If we were interrupted by other thread. |
---|
| 204 | if(iddleTimeout == 0) return; |
---|
| 205 | |
---|
| 206 | //If last datagram was received, long time ago, return. |
---|
| 207 | long timeSinceRead = System.currentTimeMillis() - lastReadTime; |
---|
| 208 | if(timeSinceRead >= iddleTimeout -100) //-100 for adjustment |
---|
| 209 | return; |
---|
| 210 | } |
---|
| 211 | dp.setLength(data.length); |
---|
| 212 | } |
---|
| 213 | } |
---|
| 214 | } |
---|