comparison 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
comparison
equal deleted inserted replaced
-1:000000000000 0:0ce5cc452d02
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 }