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