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  */

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
34 import jdk.internal.util.StaticProperty;
35 import sun.net.SocksProxy;
36 import sun.net.spi.DefaultProxySelector;
37 import sun.net.www.ParseUtil;
38 /* import org.ietf.jgss.*; */
39
40 /**
41  * SOCKS (V4 & V5) TCP socket implementation (RFC 1928).
42  * This is a subclass of PlainSocketImpl.
43  * Note this class should <b>NOT</b> be public.
44  */

45
46 class SocksSocketImpl extends PlainSocketImpl implements SocksConsts {
47     private String server = null;
48     private int serverPort = DEFAULT_PORT;
49     private InetSocketAddress external_address;
50     private boolean useV4 = false;
51     private Socket cmdsock = null;
52     private InputStream cmdIn = null;
53     private OutputStream cmdOut = null;
54     /* true if the Proxy has been set programatically */
55     private boolean applicationSetProxy;  /* false */
56
57
58     SocksSocketImpl() {
59         // Nothing needed
60     }
61
62     SocksSocketImpl(String server, int port) {
63         this.server = server;
64         this.serverPort = (port == -1 ? DEFAULT_PORT : port);
65     }
66
67     SocksSocketImpl(Proxy proxy) {
68         SocketAddress a = proxy.address();
69         if (a instanceof InetSocketAddress) {
70             InetSocketAddress ad = (InetSocketAddress) a;
71             // Use getHostString() to avoid reverse lookups
72             server = ad.getHostString();
73             serverPort = ad.getPort();
74         }
75         useV4 = useV4(proxy);
76     }
77
78     void setV4() {
79         useV4 = true;
80     }
81
82     private static boolean useV4(Proxy proxy) {
83         if (proxy instanceof SocksProxy
84             && ((SocksProxy)proxy).protocolVersion() == 4) {
85             return true;
86         }
87         return DefaultProxySelector.socksProxyVersion() == 4;
88     }
89
90     private synchronized void privilegedConnect(final String host,
91                                               final int port,
92                                               final int timeout)
93          throws IOException
94     {
95         try {
96             AccessController.doPrivileged(
97                 new java.security.PrivilegedExceptionAction<>() {
98                     public Void run() throws IOException {
99                               superConnectServer(host, port, timeout);
100                               cmdIn = getInputStream();
101                               cmdOut = getOutputStream();
102                               return null;
103                           }
104                       });
105         } catch (java.security.PrivilegedActionException pae) {
106             throw (IOException) pae.getException();
107         }
108     }
109
110     private void superConnectServer(String host, int port,
111                                     int timeout) throws IOException {
112         super.connect(new InetSocketAddress(host, port), timeout);
113     }
114
115     private static int remainingMillis(long deadlineMillis) throws IOException {
116         if (deadlineMillis == 0L)
117             return 0;
118
119         final long remaining = deadlineMillis - System.currentTimeMillis();
120         if (remaining > 0)
121             return (int) remaining;
122
123         throw new SocketTimeoutException();
124     }
125
126     private int readSocksReply(InputStream in, byte[] data) throws IOException {
127         return readSocksReply(in, data, 0L);
128     }
129
130     private int readSocksReply(InputStream in, byte[] data, long deadlineMillis) throws IOException {
131         int len = data.length;
132         int received = 0;
133         while (received < len) {
134             int count;
135             try {
136                 count = ((SocketInputStream)in).read(data, received, len - received, remainingMillis(deadlineMillis));
137             } catch (SocketTimeoutException e) {
138                 throw new SocketTimeoutException("Connect timed out");
139             }
140             if (count < 0)
141                 throw new SocketException("Malformed reply from SOCKS server");
142             received += count;
143         }
144         return received;
145     }
146
147     /**
148      * Provides the authentication machanism required by the proxy.
149      */

150     private boolean authenticate(byte method, InputStream in,
151                                  BufferedOutputStream out) throws IOException {
152         return authenticate(method, in, out, 0L);
153     }
154
155     private boolean authenticate(byte method, InputStream in,
156                                  BufferedOutputStream out,
157                                  long deadlineMillis) throws IOException {
158         // No Authentication required. We're done then!
159         if (method == NO_AUTH)
160             return true;
161         /**
162          * User/Password authentication. Try, in that order :
163          * - The application provided Authenticator, if any
164          * - the user.name & no password (backward compatibility behavior).
165          */

166         if (method == USER_PASSW) {
167             String userName;
168             String password = null;
169             final InetAddress addr = InetAddress.getByName(server);
170             PasswordAuthentication pw =
171                 java.security.AccessController.doPrivileged(
172                     new java.security.PrivilegedAction<>() {
173                         public PasswordAuthentication run() {
174                                 return Authenticator.requestPasswordAuthentication(
175                                        server, addr, serverPort, "SOCKS5""SOCKS authentication"null);
176                             }
177                         });
178             if (pw != null) {
179                 userName = pw.getUserName();
180                 password = new String(pw.getPassword());
181             } else {
182                 userName = StaticProperty.userName();
183             }
184             if (userName == null)
185                 return false;
186             out.write(1);
187             out.write(userName.length());
188             try {
189                 out.write(userName.getBytes("ISO-8859-1"));
190             } catch (java.io.UnsupportedEncodingException uee) {
191                 assert false;
192             }
193             if (password != null) {
194                 out.write(password.length());
195                 try {
196                     out.write(password.getBytes("ISO-8859-1"));
197                 } catch (java.io.UnsupportedEncodingException uee) {
198                     assert false;
199                 }
200             } else
201                 out.write(0);
202             out.flush();
203             byte[] data = new byte[2];
204             int i = readSocksReply(in, data, deadlineMillis);
205             if (i != 2 || data[1] != 0) {
206                 /* RFC 1929 specifies that the connection MUST be closed if
207                    authentication fails */

208                 out.close();
209                 in.close();
210                 return false;
211             }
212             /* Authentication succeeded */
213             return true;
214         }
215         /**
216          * GSSAPI authentication mechanism.
217          * Unfortunately the RFC seems out of sync with the Reference
218          * implementation. I'll leave this in for future completion.
219          */

220 //      if (method == GSSAPI) {
221 //          try {
222 //              GSSManager manager = GSSManager.getInstance();
223 //              GSSName name = manager.createName("SERVICE:socks@"+server,
224 //                                                   null);
225 //              GSSContext context = manager.createContext(name, nullnull,
226 //                                                         GSSContext.DEFAULT_LIFETIME);
227 //              context.requestMutualAuth(true);
228 //              context.requestReplayDet(true);
229 //              context.requestSequenceDet(true);
230 //              context.requestCredDeleg(true);
231 //              byte []inToken = new byte[0];
232 //              while (!context.isEstablished()) {
233 //                  byte[] outToken
234 //                      = context.initSecContext(inToken, 0, inToken.length);
235 //                  // send the output token if generated
236 //                  if (outToken != null) {
237 //                      out.write(1);
238 //                      out.write(1);
239 //                      out.writeShort(outToken.length);
240 //                      out.write(outToken);
241 //                      out.flush();
242 //                      data = new byte[2];
243 //                      i = readSocksReply(in, data, deadlineMillis);
244 //                      if (i != 2 || data[1] == 0xff) {
245 //                          in.close();
246 //                          out.close();
247 //                          return false;
248 //                      }
249 //                      i = readSocksReply(in, data, deadlineMillis);
250 //                      int len = 0;
251 //                      len = ((int)data[0] & 0xff) << 8;
252 //                      len += data[1];
253 //                      data = new byte[len];
254 //                      i = readSocksReply(in, data, deadlineMillis);
255 //                      if (i == len)
256 //                          return true;
257 //                      in.close();
258 //                      out.close();
259 //                  }
260 //              }
261 //          } catch (GSSException e) {
262 //              /* RFC 1961 states that if Context initialisation fails the connection
263 //                 MUST be closed */

264 //              e.printStackTrace();
265 //              in.close();
266 //              out.close();
267 //          }
268 //      }
269         return false;
270     }
271
272     private void connectV4(InputStream in, OutputStream out,
273                            InetSocketAddress endpoint,
274                            long deadlineMillis) throws IOException {
275         if (!(endpoint.getAddress() instanceof Inet4Address)) {
276             throw new SocketException("SOCKS V4 requires IPv4 only addresses");
277         }
278         out.write(PROTO_VERS4);
279         out.write(CONNECT);
280         out.write((endpoint.getPort() >> 8) & 0xff);
281         out.write((endpoint.getPort() >> 0) & 0xff);
282         out.write(endpoint.getAddress().getAddress());
283         String userName = getUserName();
284         try {
285             out.write(userName.getBytes("ISO-8859-1"));
286         } catch (java.io.UnsupportedEncodingException uee) {
287             assert false;
288         }
289         out.write(0);
290         out.flush();
291         byte[] data = new byte[8];
292         int n = readSocksReply(in, data, deadlineMillis);
293         if (n != 8)
294             throw new SocketException("Reply from SOCKS server has bad length: " + n);
295         if (data[0] != 0 && data[0] != 4)
296             throw new SocketException("Reply from SOCKS server has bad version");
297         SocketException ex = null;
298         switch (data[1]) {
299         case 90:
300             // Success!
301             external_address = endpoint;
302             break;
303         case 91:
304             ex = new SocketException("SOCKS request rejected");
305             break;
306         case 92:
307             ex = new SocketException("SOCKS server couldn't reach destination");
308             break;
309         case 93:
310             ex = new SocketException("SOCKS authentication failed");
311             break;
312         default:
313             ex = new SocketException("Reply from SOCKS server contains bad status");
314             break;
315         }
316         if (ex != null) {
317             in.close();
318             out.close();
319             throw ex;
320         }
321     }
322
323     /**
324      * Connects the Socks Socket to the specified endpoint. It will first
325      * connect to the SOCKS proxy and negotiate the access. If the proxy
326      * grants the connections, then the connect is successful and all
327      * further traffic will go to the "real" endpoint.
328      *
329      * @param   endpoint        the {@code SocketAddress} to connect to.
330      * @param   timeout         the timeout value in milliseconds
331      * @throws  IOException     if the connection can't be established.
332      * @throws  SecurityException if there is a security manager and it
333      *                          doesn't allow the connection
334      * @throws  IllegalArgumentException if endpoint is null or a
335      *          SocketAddress subclass not supported by this socket
336      */

337     @Override
338     protected void connect(SocketAddress endpoint, int timeout) throws IOException {
339         final long deadlineMillis;
340
341         if (timeout == 0) {
342             deadlineMillis = 0L;
343         } else {
344             long finish = System.currentTimeMillis() + timeout;
345             deadlineMillis = finish < 0 ? Long.MAX_VALUE : finish;
346         }
347
348         SecurityManager security = System.getSecurityManager();
349         if (endpoint == null || !(endpoint instanceof InetSocketAddress))
350             throw new IllegalArgumentException("Unsupported address type");
351         InetSocketAddress epoint = (InetSocketAddress) endpoint;
352         if (security != null) {
353             if (epoint.isUnresolved())
354                 security.checkConnect(epoint.getHostName(),
355                                       epoint.getPort());
356             else
357                 security.checkConnect(epoint.getAddress().getHostAddress(),
358                                       epoint.getPort());
359         }
360         if (server == null) {
361             // This is the general case
362             // server is not null only when the socket was created with a
363             // specified proxy in which case it does bypass the ProxySelector
364             ProxySelector sel = java.security.AccessController.doPrivileged(
365                 new java.security.PrivilegedAction<>() {
366                     public ProxySelector run() {
367                             return ProxySelector.getDefault();
368                         }
369                     });
370             if (sel == null) {
371                 /*
372                  * No default proxySelector --> direct connection
373                  */

374                 super.connect(epoint, remainingMillis(deadlineMillis));
375                 return;
376             }
377             URI uri;
378             // Use getHostString() to avoid reverse lookups
379             String host = epoint.getHostString();
380             // IPv6 litteral?
381             if (epoint.getAddress() instanceof Inet6Address &&
382                 (!host.startsWith("[")) && (host.indexOf(':') >= 0)) {
383                 host = "[" + host + "]";
384             }
385             try {
386                 uri = new URI("socket://" + ParseUtil.encodePath(host) + ":"+ epoint.getPort());
387             } catch (URISyntaxException e) {
388                 // This shouldn't happen
389                 assert false : e;
390                 uri = null;
391             }
392             Proxy p = null;
393             IOException savedExc = null;
394             java.util.Iterator<Proxy> iProxy = null;
395             iProxy = sel.select(uri).iterator();
396             if (iProxy == null || !(iProxy.hasNext())) {
397                 super.connect(epoint, remainingMillis(deadlineMillis));
398                 return;
399             }
400             while (iProxy.hasNext()) {
401                 p = iProxy.next();
402                 if (p == null || p.type() != Proxy.Type.SOCKS) {
403                     super.connect(epoint, remainingMillis(deadlineMillis));
404                     return;
405                 }
406
407                 if (!(p.address() instanceof InetSocketAddress))
408                     throw new SocketException("Unknown address type for proxy: " + p);
409                 // Use getHostString() to avoid reverse lookups
410                 server = ((InetSocketAddress) p.address()).getHostString();
411                 serverPort = ((InetSocketAddress) p.address()).getPort();
412                 useV4 = useV4(p);
413
414                 // Connects to the SOCKS server
415                 try {
416                     privilegedConnect(server, serverPort, remainingMillis(deadlineMillis));
417                     // Worked, let's get outta here
418                     break;
419                 } catch (IOException e) {
420                     // Ooops, let's notify the ProxySelector
421                     sel.connectFailed(uri,p.address(),e);
422                     server = null;
423                     serverPort = -1;
424                     savedExc = e;
425                     // Will continue the while loop and try the next proxy
426                 }
427             }
428
429             /*
430              * If server is still null at this point, none of the proxy
431              * worked
432              */

433             if (server == null) {
434                 throw new SocketException("Can't connect to SOCKS proxy:"
435                                           + savedExc.getMessage());
436             }
437         } else {
438             // Connects to the SOCKS server
439             try {
440                 privilegedConnect(server, serverPort, remainingMillis(deadlineMillis));
441             } catch (IOException e) {
442                 throw new SocketException(e.getMessage());
443             }
444         }
445
446         // cmdIn & cmdOut were initialized during the privilegedConnect() call
447         BufferedOutputStream out = new BufferedOutputStream(cmdOut, 512);
448         InputStream in = cmdIn;
449
450         if (useV4) {
451             // SOCKS Protocol version 4 doesn't know how to deal with
452             // DOMAIN type of addresses (unresolved addresses here)
453             if (epoint.isUnresolved())
454                 throw new UnknownHostException(epoint.toString());
455             connectV4(in, out, epoint, deadlineMillis);
456             return;
457         }
458
459         // This is SOCKS V5
460         out.write(PROTO_VERS);
461         out.write(2);
462         out.write(NO_AUTH);
463         out.write(USER_PASSW);
464         out.flush();
465         byte[] data = new byte[2];
466         int i = readSocksReply(in, data, deadlineMillis);
467         if (i != 2 || ((int)data[0]) != PROTO_VERS) {
468             // Maybe it's not a V5 sever after all
469             // Let's try V4 before we give up
470             // SOCKS Protocol version 4 doesn't know how to deal with
471             // DOMAIN type of addresses (unresolved addresses here)
472             if (epoint.isUnresolved())
473                 throw new UnknownHostException(epoint.toString());
474             connectV4(in, out, epoint, deadlineMillis);
475             return;
476         }
477         if (((int)data[1]) == NO_METHODS)
478             throw new SocketException("SOCKS : No acceptable methods");
479         if (!authenticate(data[1], in, out, deadlineMillis)) {
480             throw new SocketException("SOCKS : authentication failed");
481         }
482         out.write(PROTO_VERS);
483         out.write(CONNECT);
484         out.write(0);
485         /* Test for IPV4/IPV6/Unresolved */
486         if (epoint.isUnresolved()) {
487             out.write(DOMAIN_NAME);
488             out.write(epoint.getHostName().length());
489             try {
490                 out.write(epoint.getHostName().getBytes("ISO-8859-1"));
491             } catch (java.io.UnsupportedEncodingException uee) {
492                 assert false;
493             }
494             out.write((epoint.getPort() >> 8) & 0xff);
495             out.write((epoint.getPort() >> 0) & 0xff);
496         } else if (epoint.getAddress() instanceof Inet6Address) {
497             out.write(IPV6);
498             out.write(epoint.getAddress().getAddress());
499             out.write((epoint.getPort() >> 8) & 0xff);
500             out.write((epoint.getPort() >> 0) & 0xff);
501         } else {
502             out.write(IPV4);
503             out.write(epoint.getAddress().getAddress());
504             out.write((epoint.getPort() >> 8) & 0xff);
505             out.write((epoint.getPort() >> 0) & 0xff);
506         }
507         out.flush();
508         data = new byte[4];
509         i = readSocksReply(in, data, deadlineMillis);
510         if (i != 4)
511             throw new SocketException("Reply from SOCKS server has bad length");
512         SocketException ex = null;
513         int len;
514         byte[] addr;
515         switch (data[1]) {
516         case REQUEST_OK:
517             // success!
518             switch(data[3]) {
519             case IPV4:
520                 addr = new byte[4];
521                 i = readSocksReply(in, addr, deadlineMillis);
522                 if (i != 4)
523                     throw new SocketException("Reply from SOCKS server badly formatted");
524                 data = new byte[2];
525                 i = readSocksReply(in, data, deadlineMillis);
526                 if (i != 2)
527                     throw new SocketException("Reply from SOCKS server badly formatted");
528                 break;
529             case DOMAIN_NAME:
530                 byte[] lenByte = new byte[1];
531                 i = readSocksReply(in, lenByte, deadlineMillis);
532                 if (i != 1)
533                     throw new SocketException("Reply from SOCKS server badly formatted");
534                 len = lenByte[0] & 0xFF;
535                 byte[] host = new byte[len];
536                 i = readSocksReply(in, host, deadlineMillis);
537                 if (i != len)
538                     throw new SocketException("Reply from SOCKS server badly formatted");
539                 data = new byte[2];
540                 i = readSocksReply(in, data, deadlineMillis);
541                 if (i != 2)
542                     throw new SocketException("Reply from SOCKS server badly formatted");
543                 break;
544             case IPV6:
545                 len = 16;
546                 addr = new byte[len];
547                 i = readSocksReply(in, addr, deadlineMillis);
548                 if (i != len)
549                     throw new SocketException("Reply from SOCKS server badly formatted");
550                 data = new byte[2];
551                 i = readSocksReply(in, data, deadlineMillis);
552                 if (i != 2)
553                     throw new SocketException("Reply from SOCKS server badly formatted");
554                 break;
555             default:
556                 ex = new SocketException("Reply from SOCKS server contains wrong code");
557                 break;
558             }
559             break;
560         case GENERAL_FAILURE:
561             ex = new SocketException("SOCKS server general failure");
562             break;
563         case NOT_ALLOWED:
564             ex = new SocketException("SOCKS: Connection not allowed by ruleset");
565             break;
566         case NET_UNREACHABLE:
567             ex = new SocketException("SOCKS: Network unreachable");
568             break;
569         case HOST_UNREACHABLE:
570             ex = new SocketException("SOCKS: Host unreachable");
571             break;
572         case CONN_REFUSED:
573             ex = new SocketException("SOCKS: Connection refused");
574             break;
575         case TTL_EXPIRED:
576             ex =  new SocketException("SOCKS: TTL expired");
577             break;
578         case CMD_NOT_SUPPORTED:
579             ex = new SocketException("SOCKS: Command not supported");
580             break;
581         case ADDR_TYPE_NOT_SUP:
582             ex = new SocketException("SOCKS: address type not supported");
583             break;
584         }
585         if (ex != null) {
586             in.close();
587             out.close();
588             throw ex;
589         }
590         external_address = epoint;
591     }
592
593     private void bindV4(InputStream in, OutputStream out,
594                         InetAddress baddr,
595                         int lport) throws IOException {
596         if (!(baddr instanceof Inet4Address)) {
597             throw new SocketException("SOCKS V4 requires IPv4 only addresses");
598         }
599         super.bind(baddr, lport);
600         byte[] addr1 = baddr.getAddress();
601         /* Test for AnyLocal */
602         InetAddress naddr = baddr;
603         if (naddr.isAnyLocalAddress()) {
604             naddr = AccessController.doPrivileged(
605                         new PrivilegedAction<>() {
606                             public InetAddress run() {
607                                 return cmdsock.getLocalAddress();
608
609                             }
610                         });
611             addr1 = naddr.getAddress();
612         }
613         out.write(PROTO_VERS4);
614         out.write(BIND);
615         out.write((super.getLocalPort() >> 8) & 0xff);
616         out.write((super.getLocalPort() >> 0) & 0xff);
617         out.write(addr1);
618         String userName = getUserName();
619         try {
620             out.write(userName.getBytes("ISO-8859-1"));
621         } catch (java.io.UnsupportedEncodingException uee) {
622             assert false;
623         }
624         out.write(0);
625         out.flush();
626         byte[] data = new byte[8];
627         int n = readSocksReply(in, data);
628         if (n != 8)
629             throw new SocketException("Reply from SOCKS server has bad length: " + n);
630         if (data[0] != 0 && data[0] != 4)
631             throw new SocketException("Reply from SOCKS server has bad version");
632         SocketException ex = null;
633         switch (data[1]) {
634         case 90:
635             // Success!
636             external_address = new InetSocketAddress(baddr, lport);
637             break;
638         case 91:
639             ex = new SocketException("SOCKS request rejected");
640             break;
641         case 92:
642             ex = new SocketException("SOCKS server couldn't reach destination");
643             break;
644         case 93:
645             ex = new SocketException("SOCKS authentication failed");
646             break;
647         default:
648             ex = new SocketException("Reply from SOCKS server contains bad status");
649             break;
650         }
651         if (ex != null) {
652             in.close();
653             out.close();
654             throw ex;
655         }
656
657     }
658
659     /**
660      * Sends the Bind request to the SOCKS proxy. In the SOCKS protocol, bind
661      * means "accept incoming connection from", so the SocketAddress is
662      * the one of the host we do accept connection from.
663      *
664      * @param      saddr   the Socket address of the remote host.
665      * @exception  IOException  if an I/O error occurs when binding this socket.
666      */

667     protected synchronized void socksBind(InetSocketAddress saddr) throws IOException {
668         if (socket != null) {
669             // this is a client socket, not a server socket, don't
670             // call the SOCKS proxy for a bind!
671             return;
672         }
673
674         // Connects to the SOCKS server
675
676         if (server == null) {
677             // This is the general case
678             // server is not null only when the socket was created with a
679             // specified proxy in which case it does bypass the ProxySelector
680             ProxySelector sel = java.security.AccessController.doPrivileged(
681                 new java.security.PrivilegedAction<>() {
682                     public ProxySelector run() {
683                             return ProxySelector.getDefault();
684                         }
685                     });
686             if (sel == null) {
687                 /*
688                  * No default proxySelector --> direct connection
689                  */

690                 return;
691             }
692             URI uri;
693             // Use getHostString() to avoid reverse lookups
694             String host = saddr.getHostString();
695             // IPv6 litteral?
696             if (saddr.getAddress() instanceof Inet6Address &&
697                 (!host.startsWith("[")) && (host.indexOf(':') >= 0)) {
698                 host = "[" + host + "]";
699             }
700             try {
701                 uri = new URI("serversocket://" + ParseUtil.encodePath(host) + ":"+ saddr.getPort());
702             } catch (URISyntaxException e) {
703                 // This shouldn't happen
704                 assert false : e;
705                 uri = null;
706             }
707             Proxy p = null;
708             Exception savedExc = null;
709             java.util.Iterator<Proxy> iProxy = null;
710             iProxy = sel.select(uri).iterator();
711             if (iProxy == null || !(iProxy.hasNext())) {
712                 return;
713             }
714             while (iProxy.hasNext()) {
715                 p = iProxy.next();
716                 if (p == null || p.type() != Proxy.Type.SOCKS) {
717                     return;
718                 }
719
720                 if (!(p.address() instanceof InetSocketAddress))
721                     throw new SocketException("Unknown address type for proxy: " + p);
722                 // Use getHostString() to avoid reverse lookups
723                 server = ((InetSocketAddress) p.address()).getHostString();
724                 serverPort = ((InetSocketAddress) p.address()).getPort();
725                 useV4 = useV4(p);
726
727                 // Connects to the SOCKS server
728                 try {
729                     AccessController.doPrivileged(
730                         new PrivilegedExceptionAction<>() {
731                             public Void run() throws Exception {
732                                 cmdsock = new Socket(new PlainSocketImpl());
733                                 cmdsock.connect(new InetSocketAddress(server, serverPort));
734                                 cmdIn = cmdsock.getInputStream();
735                                 cmdOut = cmdsock.getOutputStream();
736                                 return null;
737                             }
738                         });
739                 } catch (Exception e) {
740                     // Ooops, let's notify the ProxySelector
741                     sel.connectFailed(uri,p.address(),new SocketException(e.getMessage()));
742                     server = null;
743                     serverPort = -1;
744                     cmdsock = null;
745                     savedExc = e;
746                     // Will continue the while loop and try the next proxy
747                 }
748             }
749
750             /*
751              * If server is still null at this point, none of the proxy
752              * worked
753              */

754             if (server == null || cmdsock == null) {
755                 throw new SocketException("Can't connect to SOCKS proxy:"
756                                           + savedExc.getMessage());
757             }
758         } else {
759             try {
760                 AccessController.doPrivileged(
761                     new PrivilegedExceptionAction<>() {
762                         public Void run() throws Exception {
763                             cmdsock = new Socket(new PlainSocketImpl());
764                             cmdsock.connect(new InetSocketAddress(server, serverPort));
765                             cmdIn = cmdsock.getInputStream();
766                             cmdOut = cmdsock.getOutputStream();
767                             return null;
768                         }
769                     });
770             } catch (Exception e) {
771                 throw new SocketException(e.getMessage());
772             }
773         }
774         BufferedOutputStream out = new BufferedOutputStream(cmdOut, 512);
775         InputStream in = cmdIn;
776         if (useV4) {
777             bindV4(in, out, saddr.getAddress(), saddr.getPort());
778             return;
779         }
780         out.write(PROTO_VERS);
781         out.write(2);
782         out.write(NO_AUTH);
783         out.write(USER_PASSW);
784         out.flush();
785         byte[] data = new byte[2];
786         int i = readSocksReply(in, data);
787         if (i != 2 || ((int)data[0]) != PROTO_VERS) {
788             // Maybe it's not a V5 sever after all
789             // Let's try V4 before we give up
790             bindV4(in, out, saddr.getAddress(), saddr.getPort());
791             return;
792         }
793         if (((int)data[1]) == NO_METHODS)
794             throw new SocketException("SOCKS : No acceptable methods");
795         if (!authenticate(data[1], in, out)) {
796             throw new SocketException("SOCKS : authentication failed");
797         }
798         // We're OK. Let's issue the BIND command.
799         out.write(PROTO_VERS);
800         out.write(BIND);
801         out.write(0);
802         int lport = saddr.getPort();
803         if (saddr.isUnresolved()) {
804             out.write(DOMAIN_NAME);
805             out.write(saddr.getHostName().length());
806             try {
807                 out.write(saddr.getHostName().getBytes("ISO-8859-1"));
808             } catch (java.io.UnsupportedEncodingException uee) {
809                 assert false;
810             }
811             out.write((lport >> 8) & 0xff);
812             out.write((lport >> 0) & 0xff);
813         } else if (saddr.getAddress() instanceof Inet4Address) {
814             byte[] addr1 = saddr.getAddress().getAddress();
815             out.write(IPV4);
816             out.write(addr1);
817             out.write((lport >> 8) & 0xff);
818             out.write((lport >> 0) & 0xff);
819             out.flush();
820         } else if (saddr.getAddress() instanceof Inet6Address) {
821             byte[] addr1 = saddr.getAddress().getAddress();
822             out.write(IPV6);
823             out.write(addr1);
824             out.write((lport >> 8) & 0xff);
825             out.write((lport >> 0) & 0xff);
826             out.flush();
827         } else {
828             cmdsock.close();
829             throw new SocketException("unsupported address type : " + saddr);
830         }
831         data = new byte[4];
832         i = readSocksReply(in, data);
833         SocketException ex = null;
834         int len, nport;
835         byte[] addr;
836         switch (data[1]) {
837         case REQUEST_OK:
838             // success!
839             switch(data[3]) {
840             case IPV4:
841                 addr = new byte[4];
842                 i = readSocksReply(in, addr);
843                 if (i != 4)
844                     throw new SocketException("Reply from SOCKS server badly formatted");
845                 data = new byte[2];
846                 i = readSocksReply(in, data);
847                 if (i != 2)
848                     throw new SocketException("Reply from SOCKS server badly formatted");
849                 nport = ((int)data[0] & 0xff) << 8;
850                 nport += ((int)data[1] & 0xff);
851                 external_address =
852                     new InetSocketAddress(new Inet4Address("", addr) , nport);
853                 break;
854             case DOMAIN_NAME:
855                 len = data[1];
856                 byte[] host = new byte[len];
857                 i = readSocksReply(in, host);
858                 if (i != len)
859                     throw new SocketException("Reply from SOCKS server badly formatted");
860                 data = new byte[2];
861                 i = readSocksReply(in, data);
862                 if (i != 2)
863                     throw new SocketException("Reply from SOCKS server badly formatted");
864                 nport = ((int)data[0] & 0xff) << 8;
865                 nport += ((int)data[1] & 0xff);
866                 external_address = new InetSocketAddress(new String(host), nport);
867                 break;
868             case IPV6:
869                 len = data[1];
870                 addr = new byte[len];
871                 i = readSocksReply(in, addr);
872                 if (i != len)
873                     throw new SocketException("Reply from SOCKS server badly formatted");
874                 data = new byte[2];
875                 i = readSocksReply(in, data);
876                 if (i != 2)
877                     throw new SocketException("Reply from SOCKS server badly formatted");
878                 nport = ((int)data[0] & 0xff) << 8;
879                 nport += ((int)data[1] & 0xff);
880                 external_address =
881                     new InetSocketAddress(new Inet6Address("", addr), nport);
882                 break;
883             }
884             break;
885         case GENERAL_FAILURE:
886             ex = new SocketException("SOCKS server general failure");
887             break;
888         case NOT_ALLOWED:
889             ex = new SocketException("SOCKS: Bind not allowed by ruleset");
890             break;
891         case NET_UNREACHABLE:
892             ex = new SocketException("SOCKS: Network unreachable");
893             break;
894         case HOST_UNREACHABLE:
895             ex = new SocketException("SOCKS: Host unreachable");
896             break;
897         case CONN_REFUSED:
898             ex = new SocketException("SOCKS: Connection refused");
899             break;
900         case TTL_EXPIRED:
901             ex =  new SocketException("SOCKS: TTL expired");
902             break;
903         case CMD_NOT_SUPPORTED:
904             ex = new SocketException("SOCKS: Command not supported");
905             break;
906         case ADDR_TYPE_NOT_SUP:
907             ex = new SocketException("SOCKS: address type not supported");
908             break;
909         }
910         if (ex != null) {
911             in.close();
912             out.close();
913             cmdsock.close();
914             cmdsock = null;
915             throw ex;
916         }
917         cmdIn = in;
918         cmdOut = out;
919     }
920
921     /**
922      * Accepts a connection from a specific host.
923      *
924      * @param      s   the accepted connection.
925      * @param      saddr the socket address of the host we do accept
926      *               connection from
927      * @exception  IOException  if an I/O error occurs when accepting the
928      *               connection.
929      */

930     protected void acceptFrom(SocketImpl s, InetSocketAddress saddr) throws IOException {
931         if (cmdsock == null) {
932             // Not a Socks ServerSocket.
933             return;
934         }
935         InputStream in = cmdIn;
936         // Sends the "SOCKS BIND" request.
937         socksBind(saddr);
938         in.read();
939         int i = in.read();
940         in.read();
941         SocketException ex = null;
942         int nport;
943         byte[] addr;
944         InetSocketAddress real_end = null;
945         switch (i) {
946         case REQUEST_OK:
947             // success!
948             i = in.read();
949             switch(i) {
950             case IPV4:
951                 addr = new byte[4];
952                 readSocksReply(in, addr);
953                 nport = in.read() << 8;
954                 nport += in.read();
955                 real_end =
956                     new InetSocketAddress(new Inet4Address("", addr) , nport);
957                 break;
958             case DOMAIN_NAME:
959                 int len = in.read();
960                 addr = new byte[len];
961                 readSocksReply(in, addr);
962                 nport = in.read() << 8;
963                 nport += in.read();
964                 real_end = new InetSocketAddress(new String(addr), nport);
965                 break;
966             case IPV6:
967                 addr = new byte[16];
968                 readSocksReply(in, addr);
969                 nport = in.read() << 8;
970                 nport += in.read();
971                 real_end =
972                     new InetSocketAddress(new Inet6Address("", addr), nport);
973                 break;
974             }
975             break;
976         case GENERAL_FAILURE:
977             ex = new SocketException("SOCKS server general failure");
978             break;
979         case NOT_ALLOWED:
980             ex = new SocketException("SOCKS: Accept not allowed by ruleset");
981             break;
982         case NET_UNREACHABLE:
983             ex = new SocketException("SOCKS: Network unreachable");
984             break;
985         case HOST_UNREACHABLE:
986             ex = new SocketException("SOCKS: Host unreachable");
987             break;
988         case CONN_REFUSED:
989             ex = new SocketException("SOCKS: Connection refused");
990             break;
991         case TTL_EXPIRED:
992             ex =  new SocketException("SOCKS: TTL expired");
993             break;
994         case CMD_NOT_SUPPORTED:
995             ex = new SocketException("SOCKS: Command not supported");
996             break;
997         case ADDR_TYPE_NOT_SUP:
998             ex = new SocketException("SOCKS: address type not supported");
999             break;
1000         }
1001         if (ex != null) {
1002             cmdIn.close();
1003             cmdOut.close();
1004             cmdsock.close();
1005             cmdsock = null;
1006             throw ex;
1007         }
1008
1009         /**
1010          * This is where we have to do some fancy stuff.
1011          * The datastream from the socket "accepted" by the proxy will
1012          * come through the cmdSocket. So we have to swap the socketImpls
1013          */

1014         if (s instanceof SocksSocketImpl) {
1015             ((SocksSocketImpl)s).external_address = real_end;
1016         }
1017         if (s instanceof PlainSocketImpl) {
1018             PlainSocketImpl psi = (PlainSocketImpl) s;
1019             psi.setInputStream((SocketInputStream) in);
1020             psi.setFileDescriptor(cmdsock.getImpl().getFileDescriptor());
1021             psi.setAddress(cmdsock.getImpl().getInetAddress());
1022             psi.setPort(cmdsock.getImpl().getPort());
1023             psi.setLocalPort(cmdsock.getImpl().getLocalPort());
1024         } else {
1025             s.fd = cmdsock.getImpl().fd;
1026             s.address = cmdsock.getImpl().address;
1027             s.port = cmdsock.getImpl().port;
1028             s.localport = cmdsock.getImpl().localport;
1029         }
1030
1031         // Need to do that so that the socket won't be closed
1032         // when the ServerSocket is closed by the user.
1033         // It kinds of detaches the Socket because it is now
1034         // used elsewhere.
1035         cmdsock = null;
1036     }
1037
1038
1039     /**
1040      * Returns the value of this socket's {@code address} field.
1041      *
1042      * @return  the value of this socket's {@code address} field.
1043      * @see     java.net.SocketImpl#address
1044      */

1045     @Override
1046     protected InetAddress getInetAddress() {
1047         if (external_address != null)
1048             return external_address.getAddress();
1049         else
1050             return super.getInetAddress();
1051     }
1052
1053     /**
1054      * Returns the value of this socket's {@code port} field.
1055      *
1056      * @return  the value of this socket's {@code port} field.
1057      * @see     java.net.SocketImpl#port
1058      */

1059     @Override
1060     protected int getPort() {
1061         if (external_address != null)
1062             return external_address.getPort();
1063         else
1064             return super.getPort();
1065     }
1066
1067     @Override
1068     protected int getLocalPort() {
1069         if (socket != null)
1070             return super.getLocalPort();
1071         if (external_address != null)
1072             return external_address.getPort();
1073         else
1074             return super.getLocalPort();
1075     }
1076
1077     @Override
1078     protected void close() throws IOException {
1079         if (cmdsock != null)
1080             cmdsock.close();
1081         cmdsock = null;
1082         super.close();
1083     }
1084
1085     private String getUserName() {
1086         String userName = "";
1087         if (applicationSetProxy) {
1088             try {
1089                 userName = System.getProperty("user.name");
1090             } catch (SecurityException se) { /* swallow Exception */ }
1091         } else {
1092             userName = StaticProperty.userName();
1093         }
1094         return userName;
1095     }
1096 }
1097