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