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 
SocksSocketImpl()55     SocksSocketImpl() {
56         // Nothing needed
57     }
58 
SocksSocketImpl(String server, int port)59     SocksSocketImpl(String server, int port) {
60         this.server = server;
61         this.serverPort = (port == -1 ? DEFAULT_PORT : port);
62     }
63 
SocksSocketImpl(Proxy proxy)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 
setV4()74     void setV4() {
75         useV4 = true;
76     }
77 
privilegedConnect(final String host, final int port, final int timeout)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 
superConnectServer(String host, int port, int timeout)98     private void superConnectServer(String host, int port,
99                                     int timeout) throws IOException {
100         super.connect(new InetSocketAddress(host, port), timeout);
101     }
102 
remainingMillis(long deadlineMillis)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 
readSocksReply(InputStream in, byte[] data)114     private int readSocksReply(InputStream in, byte[] data) throws IOException {
115         return readSocksReply(in, data, 0L);
116     }
117 
readSocksReply(InputStream in, byte[] data, long deadlineMillis)118     private int readSocksReply(InputStream in, byte[] data, long deadlineMillis) throws IOException {
119         int len = data.length;
120         int received = 0;
121         while (received < len) {
122             int count;
123             try {
124                 count = ((SocketInputStream)in).read(data, received, len - received, remainingMillis(deadlineMillis));
125             } catch (SocketTimeoutException e) {
126                 throw new SocketTimeoutException("Connect timed out");
127             }
128             if (count < 0)
129                 throw new SocketException("Malformed reply from SOCKS server");
130             received += count;
131         }
132         return received;
133     }
134 
135     /**
136      * Provides the authentication machanism required by the proxy.
137      */
authenticate(byte method, InputStream in, BufferedOutputStream out)138     private boolean authenticate(byte method, InputStream in,
139                                  BufferedOutputStream out) throws IOException {
140         return authenticate(method, in, out, 0L);
141     }
142 
authenticate(byte method, InputStream in, BufferedOutputStream out, long deadlineMillis)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 
connectV4(InputStream in, OutputStream out, InetSocketAddress endpoint, long deadlineMillis)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
connect(SocketAddress endpoint, int timeout)327     protected void connect(SocketAddress endpoint, int timeout) throws IOException {
328         final long deadlineMillis;
329 
330         if (timeout == 0) {
331             deadlineMillis = 0L;
332         } else {
333             long finish = System.currentTimeMillis() + timeout;
334             deadlineMillis = finish < 0 ? Long.MAX_VALUE : finish;
335         }
336 
337         SecurityManager security = System.getSecurityManager();
338         if (endpoint == null || !(endpoint instanceof InetSocketAddress))
339             throw new IllegalArgumentException("Unsupported address type");
340         InetSocketAddress epoint = (InetSocketAddress) endpoint;
341         if (security != null) {
342             if (epoint.isUnresolved())
343                 security.checkConnect(epoint.getHostName(),
344                                       epoint.getPort());
345             else
346                 security.checkConnect(epoint.getAddress().getHostAddress(),
347                                       epoint.getPort());
348         }
349         if (server == null) {
350             // Android-removed: Logic to establish proxy connection based on default ProxySelector.
351             // Removed code that tried to establish proxy connection if ProxySelector#getDefault()
352             // is not null. This was never the case in previous Android releases, was causing
353             // issues and therefore was removed.
354             /*
355             // This is the general case
356             // server is not null only when the socket was created with a
357             // specified proxy in which case it does bypass the ProxySelector
358             ProxySelector sel = java.security.AccessController.doPrivileged(
359                 new java.security.PrivilegedAction<ProxySelector>() {
360                     public ProxySelector run() {
361                             return ProxySelector.getDefault();
362                         }
363                     });
364             if (sel == null) {
365                 /*
366                  * No default proxySelector --> direct connection
367                  *
368                 super.connect(epoint, remainingMillis(deadlineMillis));
369                 return;
370             }
371             URI uri;
372             // Use getHostString() to avoid reverse lookups
373             String host = epoint.getHostString();
374             // IPv6 litteral?
375             if (epoint.getAddress() instanceof Inet6Address &&
376                 (!host.startsWith("[")) && (host.indexOf(":") >= 0)) {
377                 host = "[" + host + "]";
378             }
379             try {
380                 uri = new URI("socket://" + ParseUtil.encodePath(host) + ":"+ epoint.getPort());
381             } catch (URISyntaxException e) {
382                 // This shouldn't happen
383                 assert false : e;
384                 uri = null;
385             }
386             Proxy p = null;
387             IOException savedExc = null;
388             java.util.Iterator<Proxy> iProxy = null;
389             iProxy = sel.select(uri).iterator();
390             if (iProxy == null || !(iProxy.hasNext())) {
391                 super.connect(epoint, remainingMillis(deadlineMillis));
392                 return;
393             }
394             while (iProxy.hasNext()) {
395                 p = iProxy.next();
396                 if (p == null || p.type() != Proxy.Type.SOCKS) {
397                     super.connect(epoint, remainingMillis(deadlineMillis));
398                     return;
399                 }
400 
401                 if (!(p.address() instanceof InetSocketAddress))
402                     throw new SocketException("Unknown address type for proxy: " + p);
403                 // Use getHostString() to avoid reverse lookups
404                 server = ((InetSocketAddress) p.address()).getHostString();
405                 serverPort = ((InetSocketAddress) p.address()).getPort();
406                 if (p instanceof SocksProxy) {
407                     if (((SocksProxy)p).protocolVersion() == 4) {
408                         useV4 = true;
409                     }
410                 }
411 
412                 // Connects to the SOCKS server
413                 try {
414                     privilegedConnect(server, serverPort, remainingMillis(deadlineMillis));
415                     // Worked, let's get outta here
416                     break;
417                 } catch (IOException e) {
418                     // Ooops, let's notify the ProxySelector
419                     sel.connectFailed(uri,p.address(),e);
420                     server = null;
421                     serverPort = -1;
422                     savedExc = e;
423                     // Will continue the while loop and try the next proxy
424                 }
425             }
426 
427             /*
428              * If server is still null at this point, none of the proxy
429              * worked
430              *
431             if (server == null) {
432                 throw new SocketException("Can't connect to SOCKS proxy:"
433                                           + savedExc.getMessage());
434             }
435              */
436             super.connect(epoint, remainingMillis(deadlineMillis));
437             return;
438         } else {
439             // Connects to the SOCKS server
440             try {
441                 privilegedConnect(server, serverPort, remainingMillis(deadlineMillis));
442             } catch (IOException e) {
443                 throw new SocketException(e.getMessage());
444             }
445         }
446 
447         // cmdIn & cmdOut were initialized during the privilegedConnect() call
448         BufferedOutputStream out = new BufferedOutputStream(cmdOut, 512);
449         InputStream in = cmdIn;
450 
451         if (useV4) {
452             // SOCKS Protocol version 4 doesn't know how to deal with
453             // DOMAIN type of addresses (unresolved addresses here)
454             if (epoint.isUnresolved())
455                 throw new UnknownHostException(epoint.toString());
456             connectV4(in, out, epoint, deadlineMillis);
457             return;
458         }
459 
460         // This is SOCKS V5
461         out.write(PROTO_VERS);
462         out.write(2);
463         out.write(NO_AUTH);
464         out.write(USER_PASSW);
465         out.flush();
466         byte[] data = new byte[2];
467         int i = readSocksReply(in, data, deadlineMillis);
468         if (i != 2 || ((int)data[0]) != PROTO_VERS) {
469             // Maybe it's not a V5 sever after all
470             // Let's try V4 before we give up
471             // SOCKS Protocol version 4 doesn't know how to deal with
472             // DOMAIN type of addresses (unresolved addresses here)
473             if (epoint.isUnresolved())
474                 throw new UnknownHostException(epoint.toString());
475             connectV4(in, out, epoint, deadlineMillis);
476             return;
477         }
478         if (((int)data[1]) == NO_METHODS)
479             throw new SocketException("SOCKS : No acceptable methods");
480         if (!authenticate(data[1], in, out, deadlineMillis)) {
481             throw new SocketException("SOCKS : authentication failed");
482         }
483         out.write(PROTO_VERS);
484         out.write(CONNECT);
485         out.write(0);
486         /* Test for IPV4/IPV6/Unresolved */
487         if (epoint.isUnresolved()) {
488             out.write(DOMAIN_NAME);
489             out.write(epoint.getHostName().length());
490             try {
491                 out.write(epoint.getHostName().getBytes("ISO-8859-1"));
492             } catch (java.io.UnsupportedEncodingException uee) {
493                 assert false;
494             }
495             out.write((epoint.getPort() >> 8) & 0xff);
496             out.write((epoint.getPort() >> 0) & 0xff);
497         } else if (epoint.getAddress() instanceof Inet6Address) {
498             out.write(IPV6);
499             out.write(epoint.getAddress().getAddress());
500             out.write((epoint.getPort() >> 8) & 0xff);
501             out.write((epoint.getPort() >> 0) & 0xff);
502         } else {
503             out.write(IPV4);
504             out.write(epoint.getAddress().getAddress());
505             out.write((epoint.getPort() >> 8) & 0xff);
506             out.write((epoint.getPort() >> 0) & 0xff);
507         }
508         out.flush();
509         data = new byte[4];
510         i = readSocksReply(in, data, deadlineMillis);
511         if (i != 4)
512             throw new SocketException("Reply from SOCKS server has bad length");
513         SocketException ex = null;
514         int len;
515         byte[] addr;
516         switch (data[1]) {
517         case REQUEST_OK:
518             // success!
519             switch(data[3]) {
520             case IPV4:
521                 addr = new byte[4];
522                 i = readSocksReply(in, addr, deadlineMillis);
523                 if (i != 4)
524                     throw new SocketException("Reply from SOCKS server badly formatted");
525                 data = new byte[2];
526                 i = readSocksReply(in, data, deadlineMillis);
527                 if (i != 2)
528                     throw new SocketException("Reply from SOCKS server badly formatted");
529                 break;
530             case DOMAIN_NAME:
531                 byte[] lenByte = new byte[1];
532                 i = readSocksReply(in, lenByte, deadlineMillis);
533                 if (i != 1)
534                     throw new SocketException("Reply from SOCKS server badly formatted");
535                 len = lenByte[0] & 0xFF;
536                 byte[] host = new byte[len];
537                 i = readSocksReply(in, host, 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             case IPV6:
546                 len = 16;
547                 addr = new byte[len];
548                 i = readSocksReply(in, addr, deadlineMillis);
549                 if (i != len)
550                     throw new SocketException("Reply from SOCKS server badly formatted");
551                 data = new byte[2];
552                 i = readSocksReply(in, data, deadlineMillis);
553                 if (i != 2)
554                     throw new SocketException("Reply from SOCKS server badly formatted");
555                 break;
556             default:
557                 ex = new SocketException("Reply from SOCKS server contains wrong code");
558                 break;
559             }
560             break;
561         case GENERAL_FAILURE:
562             ex = new SocketException("SOCKS server general failure");
563             break;
564         case NOT_ALLOWED:
565             ex = new SocketException("SOCKS: Connection not allowed by ruleset");
566             break;
567         case NET_UNREACHABLE:
568             ex = new SocketException("SOCKS: Network unreachable");
569             break;
570         case HOST_UNREACHABLE:
571             ex = new SocketException("SOCKS: Host unreachable");
572             break;
573         case CONN_REFUSED:
574             ex = new SocketException("SOCKS: Connection refused");
575             break;
576         case TTL_EXPIRED:
577             ex =  new SocketException("SOCKS: TTL expired");
578             break;
579         case CMD_NOT_SUPPORTED:
580             ex = new SocketException("SOCKS: Command not supported");
581             break;
582         case ADDR_TYPE_NOT_SUP:
583             ex = new SocketException("SOCKS: address type not supported");
584             break;
585         }
586         if (ex != null) {
587             in.close();
588             out.close();
589             throw ex;
590         }
591         external_address = epoint;
592     }
593 
594     // Android-removed: Dead code. bindV4, socksBind, acceptFrom methods.
595     /*
596     private void bindV4(InputStream in, OutputStream out,
597                         InetAddress baddr,
598                         int lport) throws IOException {
599         if (!(baddr instanceof Inet4Address)) {
600             throw new SocketException("SOCKS V4 requires IPv4 only addresses");
601         }
602         super.bind(baddr, lport);
603         byte[] addr1 = baddr.getAddress();
604         /* Test for AnyLocal *
605         InetAddress naddr = baddr;
606         if (naddr.isAnyLocalAddress()) {
607             naddr = AccessController.doPrivileged(
608                         new PrivilegedAction<InetAddress>() {
609                             public InetAddress run() {
610                                 return cmdsock.getLocalAddress();
611 
612                             }
613                         });
614             addr1 = naddr.getAddress();
615         }
616         out.write(PROTO_VERS4);
617         out.write(BIND);
618         out.write((super.getLocalPort() >> 8) & 0xff);
619         out.write((super.getLocalPort() >> 0) & 0xff);
620         out.write(addr1);
621         String userName = getUserName();
622         try {
623             out.write(userName.getBytes("ISO-8859-1"));
624         } catch (java.io.UnsupportedEncodingException uee) {
625             assert false;
626         }
627         out.write(0);
628         out.flush();
629         byte[] data = new byte[8];
630         int n = readSocksReply(in, data);
631         if (n != 8)
632             throw new SocketException("Reply from SOCKS server has bad length: " + n);
633         if (data[0] != 0 && data[0] != 4)
634             throw new SocketException("Reply from SOCKS server has bad version");
635         SocketException ex = null;
636         switch (data[1]) {
637         case 90:
638             // Success!
639             external_address = new InetSocketAddress(baddr, lport);
640             break;
641         case 91:
642             ex = new SocketException("SOCKS request rejected");
643             break;
644         case 92:
645             ex = new SocketException("SOCKS server couldn't reach destination");
646             break;
647         case 93:
648             ex = new SocketException("SOCKS authentication failed");
649             break;
650         default:
651             ex = new SocketException("Reply from SOCKS server contains bad status");
652             break;
653         }
654         if (ex != null) {
655             in.close();
656             out.close();
657             throw ex;
658         }
659 
660     }
661 
662     /**
663      * Sends the Bind request to the SOCKS proxy. In the SOCKS protocol, bind
664      * means "accept incoming connection from", so the SocketAddress is the
665      * the one of the host we do accept connection from.
666      *
667      * @param      saddr   the Socket address of the remote host.
668      * @exception  IOException  if an I/O error occurs when binding this socket.
669      *
670     protected synchronized void socksBind(InetSocketAddress saddr) throws IOException {
671         if (socket != null) {
672             // this is a client socket, not a server socket, don't
673             // call the SOCKS proxy for a bind!
674             return;
675         }
676 
677         // Connects to the SOCKS server
678 
679         if (server == null) {
680             // This is the general case
681             // server is not null only when the socket was created with a
682             // specified proxy in which case it does bypass the ProxySelector
683             ProxySelector sel = java.security.AccessController.doPrivileged(
684                 new java.security.PrivilegedAction<ProxySelector>() {
685                     public ProxySelector run() {
686                             return ProxySelector.getDefault();
687                         }
688                     });
689             if (sel == null) {
690                 /*
691                  * No default proxySelector --> direct connection
692                  *
693                 return;
694             }
695             URI uri;
696             // Use getHostString() to avoid reverse lookups
697             String host = saddr.getHostString();
698             // IPv6 litteral?
699             if (saddr.getAddress() instanceof Inet6Address &&
700                 (!host.startsWith("[")) && (host.indexOf(":") >= 0)) {
701                 host = "[" + host + "]";
702             }
703             try {
704                 uri = new URI("serversocket://" + ParseUtil.encodePath(host) + ":"+ saddr.getPort());
705             } catch (URISyntaxException e) {
706                 // This shouldn't happen
707                 assert false : e;
708                 uri = null;
709             }
710             Proxy p = null;
711             Exception savedExc = null;
712             java.util.Iterator<Proxy> iProxy = null;
713             iProxy = sel.select(uri).iterator();
714             if (iProxy == null || !(iProxy.hasNext())) {
715                 return;
716             }
717             while (iProxy.hasNext()) {
718                 p = iProxy.next();
719                 if (p == null || p.type() != Proxy.Type.SOCKS) {
720                     return;
721                 }
722 
723                 if (!(p.address() instanceof InetSocketAddress))
724                     throw new SocketException("Unknown address type for proxy: " + p);
725                 // Use getHostString() to avoid reverse lookups
726                 server = ((InetSocketAddress) p.address()).getHostString();
727                 serverPort = ((InetSocketAddress) p.address()).getPort();
728                 if (p instanceof SocksProxy) {
729                     if (((SocksProxy)p).protocolVersion() == 4) {
730                         useV4 = true;
731                     }
732                 }
733 
734                 // Connects to the SOCKS server
735                 try {
736                     AccessController.doPrivileged(
737                         new PrivilegedExceptionAction<Void>() {
738                             public Void run() throws Exception {
739                                 cmdsock = new Socket(new PlainSocketImpl());
740                                 cmdsock.connect(new InetSocketAddress(server, serverPort));
741                                 cmdIn = cmdsock.getInputStream();
742                                 cmdOut = cmdsock.getOutputStream();
743                                 return null;
744                             }
745                         });
746                 } catch (Exception e) {
747                     // Ooops, let's notify the ProxySelector
748                     sel.connectFailed(uri,p.address(),new SocketException(e.getMessage()));
749                     server = null;
750                     serverPort = -1;
751                     cmdsock = null;
752                     savedExc = e;
753                     // Will continue the while loop and try the next proxy
754                 }
755             }
756 
757             /*
758              * If server is still null at this point, none of the proxy
759              * worked
760              *
761             if (server == null || cmdsock == null) {
762                 throw new SocketException("Can't connect to SOCKS proxy:"
763                                           + savedExc.getMessage());
764             }
765         } else {
766             try {
767                 AccessController.doPrivileged(
768                     new PrivilegedExceptionAction<Void>() {
769                         public Void run() throws Exception {
770                             cmdsock = new Socket(new PlainSocketImpl());
771                             cmdsock.connect(new InetSocketAddress(server, serverPort));
772                             cmdIn = cmdsock.getInputStream();
773                             cmdOut = cmdsock.getOutputStream();
774                             return null;
775                         }
776                     });
777             } catch (Exception e) {
778                 throw new SocketException(e.getMessage());
779             }
780         }
781         BufferedOutputStream out = new BufferedOutputStream(cmdOut, 512);
782         InputStream in = cmdIn;
783         if (useV4) {
784             bindV4(in, out, saddr.getAddress(), saddr.getPort());
785             return;
786         }
787         out.write(PROTO_VERS);
788         out.write(2);
789         out.write(NO_AUTH);
790         out.write(USER_PASSW);
791         out.flush();
792         byte[] data = new byte[2];
793         int i = readSocksReply(in, data);
794         if (i != 2 || ((int)data[0]) != PROTO_VERS) {
795             // Maybe it's not a V5 sever after all
796             // Let's try V4 before we give up
797             bindV4(in, out, saddr.getAddress(), saddr.getPort());
798             return;
799         }
800         if (((int)data[1]) == NO_METHODS)
801             throw new SocketException("SOCKS : No acceptable methods");
802         if (!authenticate(data[1], in, out)) {
803             throw new SocketException("SOCKS : authentication failed");
804         }
805         // We're OK. Let's issue the BIND command.
806         out.write(PROTO_VERS);
807         out.write(BIND);
808         out.write(0);
809         int lport = saddr.getPort();
810         if (saddr.isUnresolved()) {
811             out.write(DOMAIN_NAME);
812             out.write(saddr.getHostName().length());
813             try {
814                 out.write(saddr.getHostName().getBytes("ISO-8859-1"));
815             } catch (java.io.UnsupportedEncodingException uee) {
816                 assert false;
817             }
818             out.write((lport >> 8) & 0xff);
819             out.write((lport >> 0) & 0xff);
820         } else if (saddr.getAddress() instanceof Inet4Address) {
821             byte[] addr1 = saddr.getAddress().getAddress();
822             out.write(IPV4);
823             out.write(addr1);
824             out.write((lport >> 8) & 0xff);
825             out.write((lport >> 0) & 0xff);
826             out.flush();
827         } else if (saddr.getAddress() instanceof Inet6Address) {
828             byte[] addr1 = saddr.getAddress().getAddress();
829             out.write(IPV6);
830             out.write(addr1);
831             out.write((lport >> 8) & 0xff);
832             out.write((lport >> 0) & 0xff);
833             out.flush();
834         } else {
835             cmdsock.close();
836             throw new SocketException("unsupported address type : " + saddr);
837         }
838         data = new byte[4];
839         i = readSocksReply(in, data);
840         SocketException ex = null;
841         int len, nport;
842         byte[] addr;
843         switch (data[1]) {
844         case REQUEST_OK:
845             // success!
846             switch(data[3]) {
847             case IPV4:
848                 addr = new byte[4];
849                 i = readSocksReply(in, addr);
850                 if (i != 4)
851                     throw new SocketException("Reply from SOCKS server badly formatted");
852                 data = new byte[2];
853                 i = readSocksReply(in, data);
854                 if (i != 2)
855                     throw new SocketException("Reply from SOCKS server badly formatted");
856                 nport = ((int)data[0] & 0xff) << 8;
857                 nport += ((int)data[1] & 0xff);
858                 external_address =
859                     new InetSocketAddress(new Inet4Address("", addr) , nport);
860                 break;
861             case DOMAIN_NAME:
862                 len = data[1];
863                 byte[] host = new byte[len];
864                 i = readSocksReply(in, host);
865                 if (i != len)
866                     throw new SocketException("Reply from SOCKS server badly formatted");
867                 data = new byte[2];
868                 i = readSocksReply(in, data);
869                 if (i != 2)
870                     throw new SocketException("Reply from SOCKS server badly formatted");
871                 nport = ((int)data[0] & 0xff) << 8;
872                 nport += ((int)data[1] & 0xff);
873                 external_address = new InetSocketAddress(new String(host), nport);
874                 break;
875             case IPV6:
876                 len = data[1];
877                 addr = new byte[len];
878                 i = readSocksReply(in, addr);
879                 if (i != len)
880                     throw new SocketException("Reply from SOCKS server badly formatted");
881                 data = new byte[2];
882                 i = readSocksReply(in, data);
883                 if (i != 2)
884                     throw new SocketException("Reply from SOCKS server badly formatted");
885                 nport = ((int)data[0] & 0xff) << 8;
886                 nport += ((int)data[1] & 0xff);
887                 external_address =
888                     new InetSocketAddress(new Inet6Address("", addr), nport);
889                 break;
890             }
891             break;
892         case GENERAL_FAILURE:
893             ex = new SocketException("SOCKS server general failure");
894             break;
895         case NOT_ALLOWED:
896             ex = new SocketException("SOCKS: Bind not allowed by ruleset");
897             break;
898         case NET_UNREACHABLE:
899             ex = new SocketException("SOCKS: Network unreachable");
900             break;
901         case HOST_UNREACHABLE:
902             ex = new SocketException("SOCKS: Host unreachable");
903             break;
904         case CONN_REFUSED:
905             ex = new SocketException("SOCKS: Connection refused");
906             break;
907         case TTL_EXPIRED:
908             ex =  new SocketException("SOCKS: TTL expired");
909             break;
910         case CMD_NOT_SUPPORTED:
911             ex = new SocketException("SOCKS: Command not supported");
912             break;
913         case ADDR_TYPE_NOT_SUP:
914             ex = new SocketException("SOCKS: address type not supported");
915             break;
916         }
917         if (ex != null) {
918             in.close();
919             out.close();
920             cmdsock.close();
921             cmdsock = null;
922             throw ex;
923         }
924         cmdIn = in;
925         cmdOut = out;
926     }
927 
928     /**
929      * Accepts a connection from a specific host.
930      *
931      * @param      s   the accepted connection.
932      * @param      saddr the socket address of the host we do accept
933      *               connection from
934      * @exception  IOException  if an I/O error occurs when accepting the
935      *               connection.
936      *
937     protected void acceptFrom(SocketImpl s, InetSocketAddress saddr) throws IOException {
938         if (cmdsock == null) {
939             // Not a Socks ServerSocket.
940             return;
941         }
942         InputStream in = cmdIn;
943         // Sends the "SOCKS BIND" request.
944         socksBind(saddr);
945         in.read();
946         int i = in.read();
947         in.read();
948         SocketException ex = null;
949         int nport;
950         byte[] addr;
951         InetSocketAddress real_end = null;
952         switch (i) {
953         case REQUEST_OK:
954             // success!
955             i = in.read();
956             switch(i) {
957             case IPV4:
958                 addr = new byte[4];
959                 readSocksReply(in, addr);
960                 nport = in.read() << 8;
961                 nport += in.read();
962                 real_end =
963                     new InetSocketAddress(new Inet4Address("", addr) , nport);
964                 break;
965             case DOMAIN_NAME:
966                 int len = in.read();
967                 addr = new byte[len];
968                 readSocksReply(in, addr);
969                 nport = in.read() << 8;
970                 nport += in.read();
971                 real_end = new InetSocketAddress(new String(addr), nport);
972                 break;
973             case IPV6:
974                 addr = new byte[16];
975                 readSocksReply(in, addr);
976                 nport = in.read() << 8;
977                 nport += in.read();
978                 real_end =
979                     new InetSocketAddress(new Inet6Address("", addr), nport);
980                 break;
981             }
982             break;
983         case GENERAL_FAILURE:
984             ex = new SocketException("SOCKS server general failure");
985             break;
986         case NOT_ALLOWED:
987             ex = new SocketException("SOCKS: Accept not allowed by ruleset");
988             break;
989         case NET_UNREACHABLE:
990             ex = new SocketException("SOCKS: Network unreachable");
991             break;
992         case HOST_UNREACHABLE:
993             ex = new SocketException("SOCKS: Host unreachable");
994             break;
995         case CONN_REFUSED:
996             ex = new SocketException("SOCKS: Connection refused");
997             break;
998         case TTL_EXPIRED:
999             ex =  new SocketException("SOCKS: TTL expired");
1000             break;
1001         case CMD_NOT_SUPPORTED:
1002             ex = new SocketException("SOCKS: Command not supported");
1003             break;
1004         case ADDR_TYPE_NOT_SUP:
1005             ex = new SocketException("SOCKS: address type not supported");
1006             break;
1007         }
1008         if (ex != null) {
1009             cmdIn.close();
1010             cmdOut.close();
1011             cmdsock.close();
1012             cmdsock = null;
1013             throw ex;
1014         }
1015 
1016         /**
1017          * This is where we have to do some fancy stuff.
1018          * The datastream from the socket "accepted" by the proxy will
1019          * come through the cmdSocket. So we have to swap the socketImpls
1020          *
1021         if (s instanceof SocksSocketImpl) {
1022             ((SocksSocketImpl)s).external_address = real_end;
1023         }
1024         if (s instanceof PlainSocketImpl) {
1025             PlainSocketImpl psi = (PlainSocketImpl) s;
1026             psi.setInputStream((SocketInputStream) in);
1027             psi.setFileDescriptor(cmdsock.getImpl().getFileDescriptor());
1028             psi.setAddress(cmdsock.getImpl().getInetAddress());
1029             psi.setPort(cmdsock.getImpl().getPort());
1030             psi.setLocalPort(cmdsock.getImpl().getLocalPort());
1031         } else {
1032             s.fd = cmdsock.getImpl().fd;
1033             s.address = cmdsock.getImpl().address;
1034             s.port = cmdsock.getImpl().port;
1035             s.localport = cmdsock.getImpl().localport;
1036         }
1037 
1038         // Need to do that so that the socket won't be closed
1039         // when the ServerSocket is closed by the user.
1040         // It kinds of detaches the Socket because it is now
1041         // used elsewhere.
1042         cmdsock = null;
1043     }
1044     */
1045 
1046     /**
1047      * Returns the value of this socket's {@code address} field.
1048      *
1049      * @return  the value of this socket's {@code address} field.
1050      * @see     java.net.SocketImpl#address
1051      */
1052     @Override
getInetAddress()1053     protected InetAddress getInetAddress() {
1054         if (external_address != null)
1055             return external_address.getAddress();
1056         else
1057             return super.getInetAddress();
1058     }
1059 
1060     /**
1061      * Returns the value of this socket's {@code port} field.
1062      *
1063      * @return  the value of this socket's {@code port} field.
1064      * @see     java.net.SocketImpl#port
1065      */
1066     @Override
getPort()1067     protected int getPort() {
1068         if (external_address != null)
1069             return external_address.getPort();
1070         else
1071             return super.getPort();
1072     }
1073 
1074     @Override
getLocalPort()1075     protected int getLocalPort() {
1076         if (socket != null)
1077             return super.getLocalPort();
1078         if (external_address != null)
1079             return external_address.getPort();
1080         else
1081             return super.getLocalPort();
1082     }
1083 
1084     @Override
close()1085     protected void close() throws IOException {
1086         if (cmdsock != null)
1087             cmdsock.close();
1088         cmdsock = null;
1089         super.close();
1090     }
1091 
getUserName()1092     private String getUserName() {
1093         String userName = "";
1094         if (applicationSetProxy) {
1095             try {
1096                 userName = System.getProperty("user.name");
1097             } catch (SecurityException se) { /* swallow Exception */ }
1098         } else {
1099             userName = java.security.AccessController.doPrivileged(
1100                 new sun.security.action.GetPropertyAction("user.name"));
1101         }
1102         return userName;
1103     }
1104 }
1105