001: package socks;
002:
003: import socks.server.*;
004: import java.net.*;
005: import java.io.*;
006:
007: /**
008: UDP Relay server, used by ProxyServer to perform udp forwarding.
009: */
010: class UDPRelayServer implements Runnable {
011:
012: DatagramSocket client_sock;
013: DatagramSocket remote_sock;
014:
015: Socket controlConnection;
016:
017: int relayPort;
018: InetAddress relayIP;
019:
020: Thread pipe_thread1, pipe_thread2;
021: Thread master_thread;
022:
023: ServerAuthenticator auth;
024:
025: long lastReadTime;
026:
027: static PrintStream log = null;
028: static Proxy proxy = null;
029: static int datagramSize = 0xFFFF;//64K, a bit more than max udp size
030: static int iddleTimeout = 180000;//3 minutes
031:
032: /**
033: Constructs UDP relay server to communicate with client
034: on given ip and port.
035: @param clientIP Address of the client from whom datagrams
036: will be recieved and to whom they will be forwarded.
037: @param clientPort Clients port.
038: @param master_thread Thread which will be interrupted, when
039: UDP relay server stoppes for some reason.
040: @param controlConnection Socket which will be closed, before
041: interrupting the master thread, it is introduced due to a bug
042: in windows JVM which does not throw InterruptedIOException in
043: threads which block in I/O operation.
044: */
045: public UDPRelayServer(InetAddress clientIP, int clientPort,
046: Thread master_thread, Socket controlConnection,
047: ServerAuthenticator auth) throws IOException {
048: this .master_thread = master_thread;
049: this .controlConnection = controlConnection;
050: this .auth = auth;
051:
052: client_sock = new Socks5DatagramSocket(true, auth
053: .getUdpEncapsulation(), clientIP, clientPort);
054: relayPort = client_sock.getLocalPort();
055: relayIP = client_sock.getLocalAddress();
056:
057: if (relayIP.getHostAddress().equals("0.0.0.0"))
058: relayIP = InetAddress.getLocalHost();
059:
060: if (proxy == null)
061: remote_sock = new DatagramSocket();
062: else
063: remote_sock = new Socks5DatagramSocket(proxy, 0, null);
064: }
065:
066: //Public methods
067: /////////////////
068:
069: /**
070: Sets the timeout for UDPRelay server.<br>
071: Zero timeout implies infinity.<br>
072: Default timeout is 3 minutes.
073: */
074:
075: static public void setTimeout(int timeout) {
076: iddleTimeout = timeout;
077: }
078:
079: /**
080: Sets the size of the datagrams used in the UDPRelayServer.<br>
081: Default size is 64K, a bit more than maximum possible size of the
082: datagram.
083: */
084: static public void setDatagramSize(int size) {
085: datagramSize = size;
086: }
087:
088: /**
089: Port to which client should send datagram for association.
090: */
091: public int getRelayPort() {
092: return relayPort;
093: }
094:
095: /**
096: IP address to which client should send datagrams for association.
097: */
098: public InetAddress getRelayIP() {
099: return relayIP;
100: }
101:
102: /**
103: Starts udp relay server.
104: Spawns two threads of execution and returns.
105: */
106: public void start() throws IOException {
107: remote_sock.setSoTimeout(iddleTimeout);
108: client_sock.setSoTimeout(iddleTimeout);
109:
110: log("Starting UDP relay server on " + relayIP + ":" + relayPort);
111: log("Remote socket " + remote_sock.getLocalAddress() + ":"
112: + remote_sock.getLocalPort());
113:
114: pipe_thread1 = new Thread(this , "pipe1");
115: pipe_thread2 = new Thread(this , "pipe2");
116:
117: lastReadTime = System.currentTimeMillis();
118:
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: public synchronized void stop() {
129: master_thread = null;
130: controlConnection = null;
131: abort();
132: }
133:
134: //Runnable interface
135: ////////////////////
136: public void run() {
137: try {
138: if (Thread.currentThread().getName().equals("pipe1"))
139: pipe(remote_sock, client_sock, false);
140: else
141: pipe(client_sock, remote_sock, true);
142: } catch (IOException ioe) {
143: } finally {
144: abort();
145: log("UDP Pipe thread " + Thread.currentThread().getName()
146: + " stopped.");
147: }
148:
149: }
150:
151: //Private methods
152: /////////////////
153: private synchronized void abort() {
154: if (pipe_thread1 == null)
155: return;
156:
157: log("Aborting UDP Relay Server");
158:
159: remote_sock.close();
160: client_sock.close();
161:
162: if (controlConnection != null)
163: try {
164: controlConnection.close();
165: } catch (IOException ioe) {
166: }
167:
168: if (master_thread != null)
169: master_thread.interrupt();
170:
171: pipe_thread1.interrupt();
172: pipe_thread2.interrupt();
173:
174: pipe_thread1 = null;
175: }
176:
177: static private void log(String s) {
178: if (log != null) {
179: log.println(s);
180: log.flush();
181: }
182: }
183:
184: private void pipe(DatagramSocket from, DatagramSocket to,
185: boolean out) throws IOException {
186: byte[] data = new byte[datagramSize];
187: DatagramPacket dp = new DatagramPacket(data, data.length);
188:
189: while (true) {
190: try {
191: from.receive(dp);
192: lastReadTime = System.currentTimeMillis();
193:
194: if (auth.checkRequest(dp, out))
195: to.send(dp);
196:
197: } catch (UnknownHostException uhe) {
198: log("Dropping datagram for unknown host");
199: } catch (InterruptedIOException iioe) {
200: //log("Interrupted: "+iioe);
201: //If we were interrupted by other thread.
202: if (iddleTimeout == 0)
203: return;
204:
205: //If last datagram was received, long time ago, return.
206: long timeSinceRead = System.currentTimeMillis()
207: - lastReadTime;
208: if (timeSinceRead >= iddleTimeout - 100) //-100 for adjustment
209: return;
210: }
211: dp.setLength(data.length);
212: }
213: }
214: }
|