1 /*
2 * Copyright 2000-2007 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25 package java.net;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.io.OutputStream;
29 import java.io.BufferedOutputStream;
30 import java.security.AccessController;
31 import java.security.PrivilegedExceptionAction;
32 import java.util.prefs.Preferences;
33 import sun.net.www.ParseUtil;
34 /* import org.ietf.jgss.*; */
35
36 /**
37 * SOCKS (V4 & V5) TCP socket implementation (RFC 1928).
38 * This is a subclass of PlainSocketImpl.
39 * Note this class should <b>NOT</b> be public.
40 */
41
42 class SocksSocketImpl extends PlainSocketImpl implements SocksConsts {
43 private String server = null;
44 private int port = DEFAULT_PORT;
45 private InetSocketAddress external_address;
46 private boolean useV4 = false;
47 private Socket cmdsock = null;
48 private InputStream cmdIn = null;
49 private OutputStream cmdOut = null;
50
51 SocksSocketImpl() {
52 // Nothing needed
53 }
54
55 SocksSocketImpl(String server, int port) {
56 this.server = server;
57 this.port = (port == -1 ? DEFAULT_PORT : port);
58 }
59
60 SocksSocketImpl(Proxy proxy) {
61 SocketAddress a = proxy.address();
62 if (a instanceof InetSocketAddress) {
63 InetSocketAddress ad = (InetSocketAddress) a;
64 // Use getHostString() to avoid reverse lookups
65 server = ad.getHostString();
66 port = ad.getPort();
67 }
68 }
69
70 void setV4() {
71 useV4 = true;
72 }
73
74 private synchronized void privilegedConnect(final String host,
75 final int port,
76 final int timeout)
77 throws IOException
78 {
79 try {
80 AccessController.doPrivileged(
81 new java.security.PrivilegedExceptionAction<Void>() {
82 public Void run() throws IOException {
83 superConnectServer(host, port, timeout);
84 cmdIn = getInputStream();
85 cmdOut = getOutputStream();
86 return null;
87 }
88 });
89 } catch (java.security.PrivilegedActionException pae) {
90 throw (IOException) pae.getException();
91 }
92 }
93
94 private void superConnectServer(String host, int port,
95 int timeout) throws IOException {
96 super.connect(new InetSocketAddress(host, port), timeout);
97 }
98
99 private int readSocksReply(InputStream in, byte[] data) throws IOException {
100 int len = data.length;
101 int received = 0;
102 for (int attempts = 0; received < len && attempts < 3; attempts++) {
103 int count = in.read(data, received, len - received);
104 if (count < 0)
105 throw new SocketException("Malformed reply from SOCKS server");
106 received += count;
107 }
108 return received;
109 }
110
111 /**
112 * Provides the authentication machanism required by the proxy.
113 */
114 private boolean authenticate(byte method, InputStream in,
115 BufferedOutputStream out) throws IOException {
116 byte[] data = null;
117 int i;
118 // No Authentication required. We're done then!
119 if (method == NO_AUTH)
120 return true;
121 /**
122 * User/Password authentication. Try, in that order :
123 * - The application provided Authenticator, if any
124 * - The user preferences java.net.socks.username &
125 * java.net.socks.password
126 * - the user.name & no password (backward compatibility behavior).
127 */
128 if (method == USER_PASSW) {
129 String userName;
130 String password = null;
131 final InetAddress addr = InetAddress.getByName(server);
132 PasswordAuthentication pw =
133 java.security.AccessController.doPrivileged(
134 new java.security.PrivilegedAction<PasswordAuthentication>() {
135 public PasswordAuthentication run() {
136 return Authenticator.requestPasswordAuthentication(
137 server, addr, port, "SOCKS5", "SOCKS authentication", null);
138 }
139 });
140 if (pw != null) {
141 userName = pw.getUserName();
142 password = new String(pw.getPassword());
143 } else {
144 final Preferences prefs = Preferences.userRoot().node("/java/net/socks");
145 try {
146 userName = AccessController.doPrivileged(
147 new java.security.PrivilegedExceptionAction<String>() {
148 public String run() throws IOException {
149 return prefs.get("username", null);
150 }
151 });
152 } catch (java.security.PrivilegedActionException pae) {
153 throw (IOException) pae.getException();
154 }
155
156 if (userName != null) {
157 try {
158 password = AccessController.doPrivileged(
159 new java.security.PrivilegedExceptionAction<String>() {
160 public String run() throws IOException {
161 return prefs.get("password", null);
162 }
163 });
164 } catch (java.security.PrivilegedActionException pae) {
165 throw (IOException) pae.getException();
166 }
167 } else {
168 userName = java.security.AccessController.doPrivileged(
169 new sun.security.action.GetPropertyAction("user.name"));
170 }
171 }
172 if (userName == null)
173 return false;
174 out.write(1);
175 out.write(userName.length());
176 try {
177 out.write(userName.getBytes("ISO-8859-1"));
178 } catch (java.io.UnsupportedEncodingException uee) {
179 assert false;
180 }
181 if (password != null) {
182 out.write(password.length());
183 try {
184 out.write(password.getBytes("ISO-8859-1"));
185 } catch (java.io.UnsupportedEncodingException uee) {
186 assert false;
187 }
188 } else
189 out.write(0);
190 out.flush();
191 data = new byte[2];
192 i = readSocksReply(in, data);
193 if (i != 2 || data[1] != 0) {
194 /* RFC 1929 specifies that the connection MUST be closed if
195 authentication fails */
196 out.close();
197 in.close();
198 return false;
199 }
200 /* Authentication succeeded */
201 return true;
202 }
203 /**
204 * GSSAPI authentication mechanism.
205 * Unfortunately the RFC seems out of sync with the Reference
206 * implementation. I'll leave this in for future completion.
207 */
208 // if (method == GSSAPI) {
209 // try {
210 // GSSManager manager = GSSManager.getInstance();
211 // GSSName name = manager.createName("SERVICE:socks@"+server,
212 // null);
213 // GSSContext context = manager.createContext(name, null, null,
214 // GSSContext.DEFAULT_LIFETIME);
215 // context.requestMutualAuth(true);
216 // context.requestReplayDet(true);
217 // context.requestSequenceDet(true);
218 // context.requestCredDeleg(true);
219 // byte []inToken = new byte[0];
220 // while (!context.isEstablished()) {
221 // byte[] outToken
222 // = context.initSecContext(inToken, 0, inToken.length);
223 // // send the output token if generated
224 // if (outToken != null) {
225 // out.write(1);
226 // out.write(1);
227 // out.writeShort(outToken.length);
228 // out.write(outToken);
229 // out.flush();
230 // data = new byte[2];
231 // i = readSocksReply(in, data);
232 // if (i != 2 || data[1] == 0xff) {
233 // in.close();
234 // out.close();
235 // return false;
236 // }
237 // i = readSocksReply(in, data);
238 // int len = 0;
239 // len = ((int)data[0] & 0xff) << 8;
240 // len += data[1];
241 // data = new byte[len];
242 // i = readSocksReply(in, data);
243 // if (i == len)
244 // return true;
245 // in.close();
246 // out.close();
247 // }
248 // }
249 // } catch (GSSException e) {
250 // /* RFC 1961 states that if Context initialisation fails the connection
251 // MUST be closed */
252 // e.printStackTrace();
253 // in.close();
254 // out.close();
255 // }
256 // }
257 return false;
258 }
259
260 private void connectV4(InputStream in, OutputStream out,
261 InetSocketAddress endpoint) throws IOException {
262 if (!(endpoint.getAddress() instanceof Inet4Address)) {
263 throw new SocketException("SOCKS V4 requires IPv4 only addresses");
264 }
265 out.write(PROTO_VERS4);
266 out.write(CONNECT);
267 out.write((endpoint.getPort() >> 8) & 0xff);
268 out.write((endpoint.getPort() >> 0) & 0xff);
269 out.write(endpoint.getAddress().getAddress());
270 String userName = java.security.AccessController.doPrivileged(
271 new sun.security.action.GetPropertyAction("user.name"));
272 try {
273 out.write(userName.getBytes("ISO-8859-1"));
274 } catch (java.io.UnsupportedEncodingException uee) {
275 assert false;
276 }
277 out.write(0);
278 out.flush();
279 byte[] data = new byte[8];
280 int n = readSocksReply(in, data);
281 if (n != 8)
282 throw new SocketException("Reply from SOCKS server has bad length: " + n);
283 if (data[0] != 0 && data[0] != 4)
284 throw new SocketException("Reply from SOCKS server has bad version");
285 SocketException ex = null;
286 switch (data[1]) {
287 case 90:
288 // Success!
289 external_address = endpoint;
290 break;
291 case 91:
292 ex = new SocketException("SOCKS request rejected");
293 break;
294 case 92:
295 ex = new SocketException("SOCKS server couldn't reach destination");
296 break;
297 case 93:
298 ex = new SocketException("SOCKS authentication failed");
299 break;
300 default:
301 ex = new SocketException("Reply from SOCKS server contains bad status");
302 break;
303 }
304 if (ex != null) {
305 in.close();
306 out.close();
307 throw ex;
308 }
309 }
310
311 /**
312 * Connects the Socks Socket to the specified endpoint. It will first
313 * connect to the SOCKS proxy and negotiate the access. If the proxy
314 * grants the connections, then the connect is successful and all
315 * further traffic will go to the "real" endpoint.
316 *
317 * @param endpoint the <code>SocketAddress</code> to connect to.
318 * @param timeout the timeout value in milliseconds
319 * @throws IOException if the connection can't be established.
320 * @throws SecurityException if there is a security manager and it
321 * doesn't allow the connection
322 * @throws IllegalArgumentException if endpoint is null or a
323 * SocketAddress subclass not supported by this socket
324 */
325 protected void connect(SocketAddress endpoint, int timeout) throws IOException {
326 SecurityManager security = System.getSecurityManager();
327 if (endpoint == null || !(endpoint instanceof InetSocketAddress))
328 throw new IllegalArgumentException("Unsupported address type");
329 InetSocketAddress epoint = (InetSocketAddress) endpoint;
330 if (security != null) {
331 if (epoint.isUnresolved())
332 security.checkConnect(epoint.getHostName(),
333 epoint.getPort());
334 else
335 security.checkConnect(epoint.getAddress().getHostAddress(),
336 epoint.getPort());
337 }
338 if (server == null) {
339 // This is the general case
340 // server is not null only when the socket was created with a
341 // specified proxy in which case it does bypass the ProxySelector
342 ProxySelector sel = java.security.AccessController.doPrivileged(
343 new java.security.PrivilegedAction<ProxySelector>() {
344 public ProxySelector run() {
345 return ProxySelector.getDefault();
346 }
347 });
348 if (sel == null) {
349 /*
350 * No default proxySelector --> direct connection
351 */
352 super.connect(epoint, timeout);
353 return;
354 }
355 URI uri = null;
356 // Use getHostString() to avoid reverse lookups
357 String host = epoint.getHostString();
358 // IPv6 litteral?
359 if (epoint.getAddress() instanceof Inet6Address &&
360 (!host.startsWith("[")) && (host.indexOf(":") >= 0)) {
361 host = "[" + host + "]";
362 }
363 try {
364 uri = new URI("socket://" + ParseUtil.encodePath(host) + ":"+ epoint.getPort());
365 } catch (URISyntaxException e) {
366 // This shouldn't happen
367 assert false : e;
368 }
369 Proxy p = null;
370 IOException savedExc = null;
371 java.util.Iterator<Proxy> iProxy = null;
372 iProxy = sel.select(uri).iterator();
373 if (iProxy == null || !(iProxy.hasNext())) {
374 super.connect(epoint, timeout);
375 return;
376 }
377 while (iProxy.hasNext()) {
378 p = iProxy.next();
379 if (p == null || p == Proxy.NO_PROXY) {
380 super.connect(epoint, timeout);
381 return;
382 }
383 if (p.type() != Proxy.Type.SOCKS)
384 throw new SocketException("Unknown proxy type : " + p.type());
385 if (!(p.address() instanceof InetSocketAddress))
386 throw new SocketException("Unknow address type for proxy: " + p);
387 // Use getHostString() to avoid reverse lookups
388 server = ((InetSocketAddress) p.address()).getHostString();
389 port = ((InetSocketAddress) p.address()).getPort();
390
391 // Connects to the SOCKS server
392 try {
393 privilegedConnect(server, port, timeout);
394 // Worked, let's get outta here
395 break;
396 } catch (IOException e) {
397 // Ooops, let's notify the ProxySelector
398 sel.connectFailed(uri,p.address(),e);
399 server = null;
400 port = -1;
401 savedExc = e;
402 // Will continue the while loop and try the next proxy
403 }
404 }
405
406 /*
407 * If server is still null at this point, none of the proxy
408 * worked
409 */
410 if (server == null) {
411 throw new SocketException("Can't connect to SOCKS proxy:"
412 + savedExc.getMessage());
413 }
414 } else {
415 // Connects to the SOCKS server
416 try {
417 privilegedConnect(server, port, timeout);
418 } catch (IOException e) {
419 throw new SocketException(e.getMessage());
420 }
421 }
422
423 // cmdIn & cmdOut were intialized during the privilegedConnect() call
424 BufferedOutputStream out = new BufferedOutputStream(cmdOut, 512);
425 InputStream in = cmdIn;
426
427 if (useV4) {
428 // SOCKS Protocol version 4 doesn't know how to deal with
429 // DOMAIN type of addresses (unresolved addresses here)
430 if (epoint.isUnresolved())
431 throw new UnknownHostException(epoint.toString());
432 connectV4(in, out, epoint);
433 return;
434 }
435
436 // This is SOCKS V5
437 out.write(PROTO_VERS);
438 out.write(2);
439 out.write(NO_AUTH);
440 out.write(USER_PASSW);
441 out.flush();
442 byte[] data = new byte[2];
443 int i = readSocksReply(in, data);
444 if (i != 2 || ((int)data[0]) != PROTO_VERS) {
445 // Maybe it's not a V5 sever after all
446 // Let's try V4 before we give up
447 // SOCKS Protocol version 4 doesn't know how to deal with
448 // DOMAIN type of addresses (unresolved addresses here)
449 if (epoint.isUnresolved())
450 throw new UnknownHostException(epoint.toString());
451 connectV4(in, out, epoint);
452 return;
453 }
454 if (((int)data[1]) == NO_METHODS)
455 throw new SocketException("SOCKS : No acceptable methods");
456 if (!authenticate(data[1], in, out)) {
457 throw new SocketException("SOCKS : authentication failed");
458 }
459 out.write(PROTO_VERS);
460 out.write(CONNECT);
461 out.write(0);
462 /* Test for IPV4/IPV6/Unresolved */
463 if (epoint.isUnresolved()) {
464 out.write(DOMAIN_NAME);
465 out.write(epoint.getHostName().length());
466 try {
467 out.write(epoint.getHostName().getBytes("ISO-8859-1"));
468 } catch (java.io.UnsupportedEncodingException uee) {
469 assert false;
470 }
471 out.write((epoint.getPort() >> 8) & 0xff);
472 out.write((epoint.getPort() >> 0) & 0xff);
473 } else if (epoint.getAddress() instanceof Inet6Address) {
474 out.write(IPV6);
475 out.write(epoint.getAddress().getAddress());
476 out.write((epoint.getPort() >> 8) & 0xff);
477 out.write((epoint.getPort() >> 0) & 0xff);
478 } else {
479 out.write(IPV4);
480 out.write(epoint.getAddress().getAddress());
481 out.write((epoint.getPort() >> 8) & 0xff);
482 out.write((epoint.getPort() >> 0) & 0xff);
483 }
484 out.flush();
485 data = new byte[4];
486 i = readSocksReply(in, data);
487 if (i != 4)
488 throw new SocketException("Reply from SOCKS server has bad length");
489 SocketException ex = null;
490 int nport, len;
491 byte[] addr;
492 switch (data[1]) {
493 case REQUEST_OK:
494 // success!
495 switch(data[3]) {
496 case IPV4:
497 addr = new byte[4];
498 i = readSocksReply(in, addr);
499 if (i != 4)
500 throw new SocketException("Reply from SOCKS server badly formatted");
501 data = new byte[2];
502 i = readSocksReply(in, data);
503 if (i != 2)
504 throw new SocketException("Reply from SOCKS server badly formatted");
505 nport = ((int)data[0] & 0xff) << 8;
506 nport += ((int)data[1] & 0xff);
507 break;
508 case DOMAIN_NAME:
509 len = data[1];
510 byte[] host = new byte[len];
511 i = readSocksReply(in, host);
512 if (i != len)
513 throw new SocketException("Reply from SOCKS server badly formatted");
514 data = new byte[2];
515 i = readSocksReply(in, data);
516 if (i != 2)
517 throw new SocketException("Reply from SOCKS server badly formatted");
518 nport = ((int)data[0] & 0xff) << 8;
519 nport += ((int)data[1] & 0xff);
520 break;
521 case IPV6:
522 len = data[1];
523 addr = new byte[len];
524 i = readSocksReply(in, addr);
525 if (i != len)
526 throw new SocketException("Reply from SOCKS server badly formatted");
527 data = new byte[2];
528 i = readSocksReply(in, data);
529 if (i != 2)
530 throw new SocketException("Reply from SOCKS server badly formatted");
531 nport = ((int)data[0] & 0xff) << 8;
532 nport += ((int)data[1] & 0xff);
533 break;
534 default:
535 ex = new SocketException("Reply from SOCKS server contains wrong code");
536 break;
537 }
538 break;
539 case GENERAL_FAILURE:
540 ex = new SocketException("SOCKS server general failure");
541 break;
542 case NOT_ALLOWED:
543 ex = new SocketException("SOCKS: Connection not allowed by ruleset");
544 break;
545 case NET_UNREACHABLE:
546 ex = new SocketException("SOCKS: Network unreachable");
547 break;
548 case HOST_UNREACHABLE:
549 ex = new SocketException("SOCKS: Host unreachable");
550 break;
551 case CONN_REFUSED:
552 ex = new SocketException("SOCKS: Connection refused");
553 break;
554 case TTL_EXPIRED:
555 ex = new SocketException("SOCKS: TTL expired");
556 break;
557 case CMD_NOT_SUPPORTED:
558 ex = new SocketException("SOCKS: Command not supported");
559 break;
560 case ADDR_TYPE_NOT_SUP:
561 ex = new SocketException("SOCKS: address type not supported");
562 break;
563 }
564 if (ex != null) {
565 in.close();
566 out.close();
567 throw ex;
568 }
569 external_address = epoint;
570 }
571
572 private void bindV4(InputStream in, OutputStream out,
573 InetAddress baddr,
574 int lport) throws IOException {
575 if (!(baddr instanceof Inet4Address)) {
576 throw new SocketException("SOCKS V4 requires IPv4 only addresses");
577 }
578 super.bind(baddr, lport);
579 byte[] addr1 = baddr.getAddress();
580 /* Test for AnyLocal */
581 InetAddress naddr = baddr;
582 if (naddr.isAnyLocalAddress()) {
583 naddr = cmdsock.getLocalAddress();
584 addr1 = naddr.getAddress();
585 }
586 out.write(PROTO_VERS4);
587 out.write(BIND);
588 out.write((super.getLocalPort() >> 8) & 0xff);
589 out.write((super.getLocalPort() >> 0) & 0xff);
590 out.write(addr1);
591 String userName = java.security.AccessController.doPrivileged(
592 new sun.security.action.GetPropertyAction("user.name"));
593 try {
594 out.write(userName.getBytes("ISO-8859-1"));
595 } catch (java.io.UnsupportedEncodingException uee) {
596 assert false;
597 }
598 out.write(0);
599 out.flush();
600 byte[] data = new byte[8];
601 int n = readSocksReply(in, data);
602 if (n != 8)
603 throw new SocketException("Reply from SOCKS server has bad length: " + n);
604 if (data[0] != 0 && data[0] != 4)
605 throw new SocketException("Reply from SOCKS server has bad version");
606 SocketException ex = null;
607 switch (data[1]) {
608 case 90:
609 // Success!
610 external_address = new InetSocketAddress(baddr, lport);
611 break;
612 case 91:
613 ex = new SocketException("SOCKS request rejected");
614 break;
615 case 92:
616 ex = new SocketException("SOCKS server couldn't reach destination");
617 break;
618 case 93:
619 ex = new SocketException("SOCKS authentication failed");
620 break;
621 default:
622 ex = new SocketException("Reply from SOCKS server contains bad status");
623 break;
624 }
625 if (ex != null) {
626 in.close();
627 out.close();
628 throw ex;
629 }
630
631 }
632
633 /**
634 * Sends the Bind request to the SOCKS proxy. In the SOCKS protocol, bind
635 * means "accept incoming connection from", so the SocketAddress is the
636 * the one of the host we do accept connection from.
637 *
638 * @param addr the Socket address of the remote host.
639 * @exception IOException if an I/O error occurs when binding this socket.
640 */
641 protected synchronized void socksBind(InetSocketAddress saddr) throws IOException {
642 if (socket != null) {
643 // this is a client socket, not a server socket, don't
644 // call the SOCKS proxy for a bind!
645 return;
646 }
647
648 // Connects to the SOCKS server
649
650 if (server == null) {
651 // This is the general case
652 // server is not null only when the socket was created with a
653 // specified proxy in which case it does bypass the ProxySelector
654 ProxySelector sel = java.security.AccessController.doPrivileged(
655 new java.security.PrivilegedAction<ProxySelector>() {
656 public ProxySelector run() {
657 return ProxySelector.getDefault();
658 }
659 });
660 if (sel == null) {
661 /*
662 * No default proxySelector --> direct connection
663 */
664 return;
665 }
666 URI uri = null;
667 // Use getHostString() to avoid reverse lookups
668 String host = saddr.getHostString();
669 // IPv6 litteral?
670 if (saddr.getAddress() instanceof Inet6Address &&
671 (!host.startsWith("[")) && (host.indexOf(":") >= 0)) {
672 host = "[" + host + "]";
673 }
674 try {
675 uri = new URI("serversocket://" + ParseUtil.encodePath(host) + ":"+ saddr.getPort());
676 } catch (URISyntaxException e) {
677 // This shouldn't happen
678 assert false : e;
679 }
680 Proxy p = null;
681 Exception savedExc = null;
682 java.util.Iterator<Proxy> iProxy = null;
683 iProxy = sel.select(uri).iterator();
684 if (iProxy == null || !(iProxy.hasNext())) {
685 return;
686 }
687 while (iProxy.hasNext()) {
688 p = iProxy.next();
689 if (p == null || p == Proxy.NO_PROXY) {
690 return;
691 }
692 if (p.type() != Proxy.Type.SOCKS)
693 throw new SocketException("Unknown proxy type : " + p.type());
694 if (!(p.address() instanceof InetSocketAddress))
695 throw new SocketException("Unknow address type for proxy: " + p);
696 // Use getHostString() to avoid reverse lookups
697 server = ((InetSocketAddress) p.address()).getHostString();
698 port = ((InetSocketAddress) p.address()).getPort();
699
700 // Connects to the SOCKS server
701 try {
702 AccessController.doPrivileged(
703 new PrivilegedExceptionAction<Void>() {
704 public Void run() throws Exception {
705 cmdsock = new Socket(new PlainSocketImpl());
706 cmdsock.connect(new InetSocketAddress(server, port));
707 cmdIn = cmdsock.getInputStream();
708 cmdOut = cmdsock.getOutputStream();
709 return null;
710 }
711 });
712 } catch (Exception e) {
713 // Ooops, let's notify the ProxySelector
714 sel.connectFailed(uri,p.address(),new SocketException(e.getMessage()));
715 server = null;
716 port = -1;
717 cmdsock = null;
718 savedExc = e;
719 // Will continue the while loop and try the next proxy
720 }
721 }
722
723 /*
724 * If server is still null at this point, none of the proxy
725 * worked
726 */
727 if (server == null || cmdsock == null) {
728 throw new SocketException("Can't connect to SOCKS proxy:"
729 + savedExc.getMessage());
730 }
731 } else {
732 try {
733 AccessController.doPrivileged(
734 new PrivilegedExceptionAction<Void>() {
735 public Void run() throws Exception {
736 cmdsock = new Socket(new PlainSocketImpl());
737 cmdsock.connect(new InetSocketAddress(server, port));
738 cmdIn = cmdsock.getInputStream();
739 cmdOut = cmdsock.getOutputStream();
740 return null;
741 }
742 });
743 } catch (Exception e) {
744 throw new SocketException(e.getMessage());
745 }
746 }
747 BufferedOutputStream out = new BufferedOutputStream(cmdOut, 512);
748 InputStream in = cmdIn;
749 if (useV4) {
750 bindV4(in, out, saddr.getAddress(), saddr.getPort());
751 return;
752 }
753 out.write(PROTO_VERS);
754 out.write(2);
755 out.write(NO_AUTH);
756 out.write(USER_PASSW);
757 out.flush();
758 byte[] data = new byte[2];
759 int i = readSocksReply(in, data);
760 if (i != 2 || ((int)data[0]) != PROTO_VERS) {
761 // Maybe it's not a V5 sever after all
762 // Let's try V4 before we give up
763 bindV4(in, out, saddr.getAddress(), saddr.getPort());
764 return;
765 }
766 if (((int)data[1]) == NO_METHODS)
767 throw new SocketException("SOCKS : No acceptable methods");
768 if (!authenticate(data[1], in, out)) {
769 throw new SocketException("SOCKS : authentication failed");
770 }
771 // We're OK. Let's issue the BIND command.
772 out.write(PROTO_VERS);
773 out.write(BIND);
774 out.write(0);
775 int lport = saddr.getPort();
776 if (saddr.isUnresolved()) {
777 out.write(DOMAIN_NAME);
778 out.write(saddr.getHostName().length());
779 try {
780 out.write(saddr.getHostName().getBytes("ISO-8859-1"));
781 } catch (java.io.UnsupportedEncodingException uee) {
782 assert false;
783 }
784 out.write((lport >> 8) & 0xff);
785 out.write((lport >> 0) & 0xff);
786 } else if (saddr.getAddress() instanceof Inet4Address) {
787 byte[] addr1 = saddr.getAddress().getAddress();
788 out.write(IPV4);
789 out.write(addr1);
790 out.write((lport >> 8) & 0xff);
791 out.write((lport >> 0) & 0xff);
792 out.flush();
793 } else if (saddr.getAddress() instanceof Inet6Address) {
794 byte[] addr1 = saddr.getAddress().getAddress();
795 out.write(IPV6);
796 out.write(addr1);
797 out.write((lport >> 8) & 0xff);
798 out.write((lport >> 0) & 0xff);
799 out.flush();
800 } else {
801 cmdsock.close();
802 throw new SocketException("unsupported address type : " + saddr);
803 }
804 data = new byte[4];
805 i = readSocksReply(in, data);
806 SocketException ex = null;
807 int len, nport;
808 byte[] addr;
809 switch (data[1]) {
810 case REQUEST_OK:
811 // success!
812 InetSocketAddress real_end = null;
813 switch(data[3]) {
814 case IPV4:
815 addr = new byte[4];
816 i = readSocksReply(in, addr);
817 if (i != 4)
818 throw new SocketException("Reply from SOCKS server badly formatted");
819 data = new byte[2];
820 i = readSocksReply(in, data);
821 if (i != 2)
822 throw new SocketException("Reply from SOCKS server badly formatted");
823 nport = ((int)data[0] & 0xff) << 8;
824 nport += ((int)data[1] & 0xff);
825 external_address =
826 new InetSocketAddress(new Inet4Address("", addr) , nport);
827 break;
828 case DOMAIN_NAME:
829 len = data[1];
830 byte[] host = new byte[len];
831 i = readSocksReply(in, host);
832 if (i != len)
833 throw new SocketException("Reply from SOCKS server badly formatted");
834 data = new byte[2];
835 i = readSocksReply(in, data);
836 if (i != 2)
837 throw new SocketException("Reply from SOCKS server badly formatted");
838 nport = ((int)data[0] & 0xff) << 8;
839 nport += ((int)data[1] & 0xff);
840 external_address = new InetSocketAddress(new String(host), nport);
841 break;
842 case IPV6:
843 len = data[1];
844 addr = new byte[len];
845 i = readSocksReply(in, addr);
846 if (i != len)
847 throw new SocketException("Reply from SOCKS server badly formatted");
848 data = new byte[2];
849 i = readSocksReply(in, data);
850 if (i != 2)
851 throw new SocketException("Reply from SOCKS server badly formatted");
852 nport = ((int)data[0] & 0xff) << 8;
853 nport += ((int)data[1] & 0xff);
854 external_address =
855 new InetSocketAddress(new Inet6Address("", addr), nport);
856 break;
857 }
858 break;
859 case GENERAL_FAILURE:
860 ex = new SocketException("SOCKS server general failure");
861 break;
862 case NOT_ALLOWED:
863 ex = new SocketException("SOCKS: Bind not allowed by ruleset");
864 break;
865 case NET_UNREACHABLE:
866 ex = new SocketException("SOCKS: Network unreachable");
867 break;
868 case HOST_UNREACHABLE:
869 ex = new SocketException("SOCKS: Host unreachable");
870 break;
871 case CONN_REFUSED:
872 ex = new SocketException("SOCKS: Connection refused");
873 break;
874 case TTL_EXPIRED:
875 ex = new SocketException("SOCKS: TTL expired");
876 break;
877 case CMD_NOT_SUPPORTED:
878 ex = new SocketException("SOCKS: Command not supported");
879 break;
880 case ADDR_TYPE_NOT_SUP:
881 ex = new SocketException("SOCKS: address type not supported");
882 break;
883 }
884 if (ex != null) {
885 in.close();
886 out.close();
887 cmdsock.close();
888 cmdsock = null;
889 throw ex;
890 }
891 cmdIn = in;
892 cmdOut = out;
893 }
894
895 /**
896 * Accepts a connection from a specific host.
897 *
898 * @param s the accepted connection.
899 * @param saddr the socket address of the host we do accept
900 * connection from
901 * @exception IOException if an I/O error occurs when accepting the
902 * connection.
903 */
904 protected void acceptFrom(SocketImpl s, InetSocketAddress saddr) throws IOException {
905 if (cmdsock == null) {
906 // Not a Socks ServerSocket.
907 return;
908 }
909 InputStream in = cmdIn;
910 // Sends the "SOCKS BIND" request.
911 socksBind(saddr);
912 in.read();
913 int i = in.read();
914 in.read();
915 SocketException ex = null;
916 int nport;
917 byte[] addr;
918 InetSocketAddress real_end = null;
919 switch (i) {
920 case REQUEST_OK:
921 // success!
922 i = in.read();
923 switch(i) {
924 case IPV4:
925 addr = new byte[4];
926 readSocksReply(in, addr);
927 nport = in.read() << 8;
928 nport += in.read();
929 real_end =
930 new InetSocketAddress(new Inet4Address("", addr) , nport);
931 break;
932 case DOMAIN_NAME:
933 int len = in.read();
934 addr = new byte[len];
935 readSocksReply(in, addr);
936 nport = in.read() << 8;
937 nport += in.read();
938 real_end = new InetSocketAddress(new String(addr), nport);
939 break;
940 case IPV6:
941 addr = new byte[16];
942 readSocksReply(in, addr);
943 nport = in.read() << 8;
944 nport += in.read();
945 real_end =
946 new InetSocketAddress(new Inet6Address("", addr), nport);
947 break;
948 }
949 break;
950 case GENERAL_FAILURE:
951 ex = new SocketException("SOCKS server general failure");
952 break;
953 case NOT_ALLOWED:
954 ex = new SocketException("SOCKS: Accept not allowed by ruleset");
955 break;
956 case NET_UNREACHABLE:
957 ex = new SocketException("SOCKS: Network unreachable");
958 break;
959 case HOST_UNREACHABLE:
960 ex = new SocketException("SOCKS: Host unreachable");
961 break;
962 case CONN_REFUSED:
963 ex = new SocketException("SOCKS: Connection refused");
964 break;
965 case TTL_EXPIRED:
966 ex = new SocketException("SOCKS: TTL expired");
967 break;
968 case CMD_NOT_SUPPORTED:
969 ex = new SocketException("SOCKS: Command not supported");
970 break;
971 case ADDR_TYPE_NOT_SUP:
972 ex = new SocketException("SOCKS: address type not supported");
973 break;
974 }
975 if (ex != null) {
976 cmdIn.close();
977 cmdOut.close();
978 cmdsock.close();
979 cmdsock = null;
980 throw ex;
981 }
982
983 /**
984 * This is where we have to do some fancy stuff.
985 * The datastream from the socket "accepted" by the proxy will
986 * come through the cmdSocket. So we have to swap the socketImpls
987 */
988 if (s instanceof SocksSocketImpl) {
989 ((SocksSocketImpl)s).external_address = real_end;
990 }
991 if (s instanceof PlainSocketImpl) {
992 PlainSocketImpl psi = (PlainSocketImpl) s;
993 psi.setInputStream((SocketInputStream) in);
994 psi.setFileDescriptor(cmdsock.getImpl().getFileDescriptor());
995 psi.setAddress(cmdsock.getImpl().getInetAddress());
996 psi.setPort(cmdsock.getImpl().getPort());
997 psi.setLocalPort(cmdsock.getImpl().getLocalPort());
998 } else {
999 s.fd = cmdsock.getImpl().fd;
1000 s.address = cmdsock.getImpl().address;
1001 s.port = cmdsock.getImpl().port;
1002 s.localport = cmdsock.getImpl().localport;
1003 }
1004
1005 // Need to do that so that the socket won't be closed
1006 // when the ServerSocket is closed by the user.
1007 // It kinds of detaches the Socket because it is now
1008 // used elsewhere.
1009 cmdsock = null;
1010 }
1011
1012
1013 /**
1014 * Returns the value of this socket's <code>address</code> field.
1015 *
1016 * @return the value of this socket's <code>address</code> field.
1017 * @see java.net.SocketImpl#address
1018 */
1019 protected InetAddress getInetAddress() {
1020 if (external_address != null)
1021 return external_address.getAddress();
1022 else
1023 return super.getInetAddress();
1024 }
1025
1026 /**
1027 * Returns the value of this socket's <code>port</code> field.
1028 *
1029 * @return the value of this socket's <code>port</code> field.
1030 * @see java.net.SocketImpl#port
1031 */
1032 protected int getPort() {
1033 if (external_address != null)
1034 return external_address.getPort();
1035 else
1036 return super.getPort();
1037 }
1038
1039 protected int getLocalPort() {
1040 if (socket != null)
1041 return super.getLocalPort();
1042 if (external_address != null)
1043 return external_address.getPort();
1044 else
1045 return super.getLocalPort();
1046 }
1047
1048 protected void close() throws IOException {
1049 if (cmdsock != null)
1050 cmdsock.close();
1051 cmdsock = null;
1052 super.close();
1053 }
1054
1055 }