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