229 lines
5.5 KiB
Java
229 lines
5.5 KiB
Java
package com.runjva.sourceforge.jsocks.protocol;
|
|
|
|
import java.io.IOException;
|
|
import java.io.InterruptedIOException;
|
|
import java.net.DatagramPacket;
|
|
import java.net.DatagramSocket;
|
|
import java.net.InetAddress;
|
|
import java.net.Socket;
|
|
import java.net.UnknownHostException;
|
|
|
|
import com.runjva.sourceforge.jsocks.server.ServerAuthenticator;
|
|
|
|
/**
|
|
* 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 Logger log = LoggerFactory.getLogger(UDPRelayServer.class);
|
|
static SocksProxyBase 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.info("Starting UDP relay server on {}:{}", relayIP, relayPort);
|
|
// log.info("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 (final IOException ioe) {
|
|
} finally {
|
|
abort();
|
|
// log.info("UDP Pipe thread " + Thread.currentThread().getName()
|
|
// + " stopped.");
|
|
}
|
|
|
|
}
|
|
|
|
// Private methods
|
|
// ///////////////
|
|
private synchronized void abort() {
|
|
if (pipe_thread1 == null) {
|
|
return;
|
|
}
|
|
|
|
// log.info("Aborting UDP Relay Server");
|
|
|
|
remote_sock.close();
|
|
client_sock.close();
|
|
|
|
if (controlConnection != null) {
|
|
try {
|
|
controlConnection.close();
|
|
} catch (final IOException ioe) {
|
|
}
|
|
}
|
|
|
|
if (master_thread != null) {
|
|
master_thread.interrupt();
|
|
}
|
|
|
|
pipe_thread1.interrupt();
|
|
pipe_thread2.interrupt();
|
|
|
|
pipe_thread1 = null;
|
|
}
|
|
|
|
private void pipe(DatagramSocket from, DatagramSocket to, boolean out)
|
|
throws IOException {
|
|
final byte[] data = new byte[datagramSize];
|
|
final 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 (final UnknownHostException uhe) {
|
|
// log.info("Dropping datagram for unknown host");
|
|
} catch (final 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.
|
|
final long timeSinceRead = System.currentTimeMillis()
|
|
- lastReadTime;
|
|
if (timeSinceRead >= iddleTimeout - 100) {
|
|
return;
|
|
}
|
|
}
|
|
dp.setLength(data.length);
|
|
}
|
|
}
|
|
}
|