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