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