aboutsummaryrefslogtreecommitdiffstats
path: root/src/net/sourceforge/jsocks/UDPRelayServer.java
blob: dfa601688cb26075d6402055483f667b376d7630 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
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);
       }
    }
}