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