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 | } |
---|