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