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