blob: 8f6fad5b4c1dbc89d1d0bca09ef9a848c39aa990 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
20 * CA 95054 USA or visit www.sun.com if you need additional information or
21 * have any questions.
22 */
23import java.net.*;
24import java.io.*;
25
26public class SocksServer extends Thread {
27 // Some useful SOCKS constant
28
29 static final int PROTO_VERS4 = 4;
30 static final int PROTO_VERS = 5;
31 static final int DEFAULT_PORT = 1080;
32
33 static final int NO_AUTH = 0;
34 static final int GSSAPI = 1;
35 static final int USER_PASSW = 2;
36 static final int NO_METHODS = -1;
37
38 static final int CONNECT = 1;
39 static final int BIND = 2;
40 static final int UDP_ASSOC = 3;
41
42 static final int IPV4 = 1;
43 static final int DOMAIN_NAME = 3;
44 static final int IPV6 = 4;
45
46 static final int REQUEST_OK = 0;
47 static final int GENERAL_FAILURE = 1;
48 static final int NOT_ALLOWED = 2;
49 static final int NET_UNREACHABLE = 3;
50 static final int HOST_UNREACHABLE = 4;
51 static final int CONN_REFUSED = 5;
52 static final int TTL_EXPIRED = 6;
53 static final int CMD_NOT_SUPPORTED = 7;
54 static final int ADDR_TYPE_NOT_SUP = 8;
55
56 private int port;
57 private ServerSocket server;
58 private boolean useV4 = false;
59 private java.util.Hashtable users = new java.util.Hashtable();
60 private boolean done = false;
61 // Inner class to handle protocol with client
62 // This is the bulk of the work (protocol handler)
63 class ClientHandler extends Thread {
64 private InputStream in;
65 private OutputStream out;
66 private Socket client;
67 private Socket dest;
68
69 // Simple tunneling class, moving bits from one stream to another
70
71 class Tunnel extends Thread {
72 private InputStream tin;
73 private OutputStream tout;
74
75 Tunnel(InputStream in, OutputStream out) {
76 tin = in;
77 tout = out;
78 }
79
80 public void run() {
81 int b;
82 while (true) {
83 try {
84 b = tin.read();
85 if (b == -1) {
86 tin.close();
87 tout.close();
88 return;
89 }
90 tout.write(b);
91 } catch (IOException e) {
92 // actually exit from the thread
93 return;
94 }
95 }
96 }
97 }
98
99 ClientHandler(Socket s) throws IOException {
100 client = s;
101 in = client.getInputStream();
102 out = client.getOutputStream();
103 }
104
105 private void readBuf(InputStream is, byte[] buf) throws IOException {
106 int l = buf.length;
107 int count = 0;
108 int i;
109 do {
110 i = is.read(buf, count, l - count);
111 if (i == -1)
112 throw new IOException("unexpected EOF");
113 count += i;
114 } while (count < l);
115 }
116
117
118 private boolean userPassAuth() throws IOException {
119 int ver = in.read();
120 int ulen = in.read();
121 if (ulen <= 0)
122 throw new SocketException("SOCKS protocol error");
123 byte[] buf = new byte[ulen];
124 readBuf(in, buf);
125 String uname = new String(buf);
126 String password = null;
127 ulen = in.read();
128 if (ulen < 0)
129 throw new SocketException("SOCKS protocol error");
130 if (ulen > 0) {
131 buf = new byte[ulen];
132 readBuf(in, buf);
133 password = new String(buf);
134 }
135 // Check username/password validity here
136 System.err.println("User: '" + uname);
137 System.err.println("PSWD: '" + password);
138 if (users.containsKey(uname)) {
139 String p1 = (String) users.get(uname);
140 System.err.println("p1 = " + p1);
141 if (p1.equals(password)) {
142 out.write(PROTO_VERS);
143 out.write(REQUEST_OK);
144 out.flush();
145 return true;
146 }
147 }
148 out.write(PROTO_VERS);
149 out.write(NOT_ALLOWED);
150 out.flush();
151 return false;
152 }
153
154 private void purge() throws IOException {
155 boolean done = false;
156 int i = 0;
157 client.setSoTimeout(1000);
158 while(!done && i != -1) {
159 try {
160 i = in.read();
161 } catch(IOException e) {
162 done = true;
163 }
164 }
165 }
166
167
168 // Handle the SOCKS version 4 protocl
169
170 private void getRequestV4() throws IOException {
171 int ver = in.read();
172 int cmd = in.read();
173 if (ver == -1 || cmd == -1) {
174 // EOF
175 in.close();
176 out.close();
177 return;
178 }
179
180 if (ver != 0 && ver != 4) {
181 out.write(PROTO_VERS4);
182 out.write(91); // Bad Request
183 out.write(0);
184 out.write(0);
185 out.write(0);
186 out.write(0);
187 out.write(0);
188 out.write(0);
189 out.write(0);
190 out.flush();
191 purge();
192 out.close();
193 in.close();
194 return;
195 }
196
197 if (cmd == CONNECT) {
198 int port = ((in.read() & 0xff) << 8);
199 port += (in.read() & 0xff);
200 byte[] buf = new byte[4];
201 readBuf(in, buf);
202 InetAddress addr = InetAddress.getByAddress(buf);
203 // We don't use the username...
204 int c;
205 do {
206 c = (in.read() & 0xff);
207 } while (c!=0);
208 boolean ok = true;
209 try {
210 dest = new Socket(addr, port);
211 } catch (IOException e) {
212 ok = false;
213 }
214 if (!ok) {
215 out.write(PROTO_VERS4);
216 out.write(91);
217 out.write(0);
218 out.write(0);
219 out.write(buf);
220 out.flush();
221 purge();
222 out.close();
223 in.close();
224 return;
225 }
226 out.write(PROTO_VERS4);
227 out.write(90); // Success
228 out.write((port >> 8) & 0xff);
229 out.write(port & 0xff);
230 out.write(buf);
231 out.flush();
232 InputStream in2 = dest.getInputStream();
233 OutputStream out2 = dest.getOutputStream();
234
235 Tunnel tunnel = new Tunnel(in2, out);
236 tunnel.start();
237
238 int b = 0;
239 do {
240 try {
241 b = in.read();
242 if (b == -1) {
243 in.close();
244 out2.close();
245 return;
246 }
247 out2.write(b);
248 } catch (IOException ex) {
249 }
250 } while (!client.isClosed());
251 }
252 }
253
254
255 // Negociate the authentication scheme with the client
256 private void negociate() throws IOException {
257 int ver = in.read();
258 int n = in.read();
259 byte[] buf = null;
260 if (n > 0) {
261 buf = new byte[n];
262 readBuf(in, buf);
263 }
264 int scheme = NO_AUTH;
265 for (int i = 0; i < n; i++)
266 if (buf[i] == USER_PASSW)
267 scheme = USER_PASSW;
268 out.write(PROTO_VERS);
269 out.write(scheme);
270 out.flush();
271 if (scheme == USER_PASSW)
272 userPassAuth();
273 }
274
275 // Send error message then close the streams
276 private void sendError(int code) {
277 try {
278 out.write(PROTO_VERS);
279 out.write(code);
280 out.write(0);
281 out.write(IPV4);
282 for (int i=0; i<6; i++)
283 out.write(0);
284 out.flush();
285 out.close();
286 } catch (IOException ex) {
287 }
288 }
289
290 // Actually connect the proxy to the destination then initiate tunneling
291
292 private void doConnect(InetSocketAddress addr) throws IOException {
293 dest = new Socket();
294 try {
295 dest.connect(addr, 10000);
296 } catch (SocketTimeoutException ex) {
297 sendError(HOST_UNREACHABLE);
298 return;
299 } catch (ConnectException cex) {
300 sendError(CONN_REFUSED);
301 return;
302 }
303 // Success
304 InetAddress iadd = addr.getAddress();
305 if (iadd instanceof Inet4Address) {
306 out.write(PROTO_VERS);
307 out.write(REQUEST_OK);
308 out.write(0);
309 out.write(IPV4);
310 out.write(iadd.getAddress());
311 } else if (iadd instanceof Inet6Address) {
312 out.write(PROTO_VERS);
313 out.write(REQUEST_OK);
314 out.write(0);
315 out.write(IPV6);
316 out.write(iadd.getAddress());
317 } else {
318 sendError(GENERAL_FAILURE);
319 return;
320 }
321 out.write((addr.getPort() >> 8) & 0xff);
322 out.write((addr.getPort() >> 0) & 0xff);
323 out.flush();
324
325 InputStream in2 = dest.getInputStream();
326 OutputStream out2 = dest.getOutputStream();
327
328 Tunnel tunnel = new Tunnel(in2, out);
329 tunnel.start();
330
331 int b = 0;
332 do {
333 // Note that the socket might be closed from another thread (the tunnel)
334 try {
335 b = in.read();
336 if (b == -1) {
337 in.close();
338 out2.close();
339 return;
340 }
341 out2.write(b);
342 } catch(IOException ioe) {
343 }
344 } while (!client.isClosed());
345 }
346
347 private void doBind(InetSocketAddress addr) throws IOException {
348 ServerSocket svr = new ServerSocket();
349 svr.bind(null);
350 InetSocketAddress bad = (InetSocketAddress) svr.getLocalSocketAddress();
351 out.write(PROTO_VERS);
352 out.write(REQUEST_OK);
353 out.write(0);
354 out.write(IPV4);
355 out.write(bad.getAddress().getAddress());
356 out.write((bad.getPort() >> 8) & 0xff);
357 out.write((bad.getPort() & 0xff));
358 out.flush();
359 dest = svr.accept();
360 bad = (InetSocketAddress) dest.getRemoteSocketAddress();
361 out.write(PROTO_VERS);
362 out.write(REQUEST_OK);
363 out.write(0);
364 out.write(IPV4);
365 out.write(bad.getAddress().getAddress());
366 out.write((bad.getPort() >> 8) & 0xff);
367 out.write((bad.getPort() & 0xff));
368 out.flush();
369 InputStream in2 = dest.getInputStream();
370 OutputStream out2 = dest.getOutputStream();
371
372 Tunnel tunnel = new Tunnel(in2, out);
373 tunnel.start();
374
375 int b = 0;
376 do {
377 // Note that the socket might be close from another thread (the tunnel)
378 try {
379 b = in.read();
380 if (b == -1) {
381 in.close();
382 out2.close();
383 return;
384 }
385 out2.write(b);
386 } catch(IOException ioe) {
387 }
388 } while (!client.isClosed());
389
390 }
391
392 // Handle the SOCKS v5 requests
393
394 private void getRequest() throws IOException {
395 int ver = in.read();
396 int cmd = in.read();
397 if (ver == -1 || cmd == -1) {
398 in.close();
399 out.close();
400 return;
401 }
402 int rsv = in.read();
403 int atyp = in.read();
404 String addr = null;
405 int port = 0;
406
407 switch(atyp) {
408 case IPV4:
409 {
410 byte[] buf = new byte[4];
411 readBuf(in, buf);
412 int i = 0;
413 StringBuffer sb = new StringBuffer();
414 for (i = 0; i < 4; i++) {
415 sb.append(buf[i]&0xff);
416 if (i < 3)
417 sb.append('.');
418 }
419 addr = sb.toString();
420 }
421 break;
422 case DOMAIN_NAME:
423 {
424 int i = in.read();
425 byte[] buf = new byte[i];
426 readBuf(in, buf);
427 addr = new String(buf);
428 }
429 break;
430 case IPV6:
431 {
432 byte[] buf = new byte[16];
433 readBuf(in, buf);
434 int i = 0;
435 StringBuffer sb = new StringBuffer();
436 for (i = 0; i<16; i++) {
437 sb.append(Integer.toHexString(buf[i]&0xff));
438 if (i < 15)
439 sb.append(':');
440 }
441 addr = sb.toString();
442 }
443 break;
444 }
445
446 port = ((in.read()&0xff) << 8);
447 port += (in.read()&0xff);
448
449 InetSocketAddress socAddr = new InetSocketAddress(addr, port);
450 switch(cmd) {
451 case CONNECT:
452 doConnect(socAddr);
453 break;
454 case BIND:
455 doBind(socAddr);
456 break;
457 case UDP_ASSOC:
458 // doUDP(socAddr);
459 break;
460 }
461 }
462
463 public void run() {
464 String line = null;
465 try {
466 if (useV4) {
467 getRequestV4();
468 } else {
469 negociate();
470 getRequest();
471 }
472 } catch (IOException ex) {
473 try {
474 sendError(GENERAL_FAILURE);
475 } catch (Exception e) {
476 }
477 } finally {
478 try {
479 client.close();
480 } catch (IOException e2) {
481 }
482 }
483 }
484
485 }
486
487 public SocksServer(int port, boolean v4) throws IOException {
488 this(port);
489 this.useV4 = v4;
490 }
491
492 public SocksServer(int port) throws IOException {
493 this.port = port;
494 server = new ServerSocket();
495 server.bind(new InetSocketAddress(port));
496 }
497
498 public SocksServer() throws IOException {
499 this (DEFAULT_PORT);
500 }
501
502 public void addUser(String user, String passwd) {
503 users.put(user, passwd);
504 }
505
506 public synchronized void terminate() {
507 done = true;
508 }
509
510 public void run() {
511 ClientHandler cl = null;
512 while (!done) {
513 try {
514 Socket s = server.accept();
515 cl = new ClientHandler(s);
516 cl.start();
517 } catch (IOException ex) {
518 if (cl != null)
519 cl.interrupt();
520 }
521 }
522 }
523}