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