• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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         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      */
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             /*
352              * Removed code that tried to establish proxy connection if
353              * ProxySelector#getDefault() is not null.
354              * This was never the case in previous android releases, was causing
355              * issues and therefore was removed.
356              */
357             super.connect(epoint, remainingMillis(deadlineMillis));
358             return;
359         } else {
360             // Connects to the SOCKS server
361             try {
362                 privilegedConnect(server, serverPort, remainingMillis(deadlineMillis));
363             } catch (IOException e) {
364                 throw new SocketException(e.getMessage());
365             }
366         }
367 
368         // cmdIn & cmdOut were initialized during the privilegedConnect() call
369         BufferedOutputStream out = new BufferedOutputStream(cmdOut, 512);
370         InputStream in = cmdIn;
371 
372         if (useV4) {
373             // SOCKS Protocol version 4 doesn't know how to deal with
374             // DOMAIN type of addresses (unresolved addresses here)
375             if (epoint.isUnresolved())
376                 throw new UnknownHostException(epoint.toString());
377             connectV4(in, out, epoint, deadlineMillis);
378             return;
379         }
380 
381         // This is SOCKS V5
382         out.write(PROTO_VERS);
383         out.write(2);
384         out.write(NO_AUTH);
385         out.write(USER_PASSW);
386         out.flush();
387         byte[] data = new byte[2];
388         int i = readSocksReply(in, data, deadlineMillis);
389         if (i != 2 || ((int)data[0]) != PROTO_VERS) {
390             // Maybe it's not a V5 sever after all
391             // Let's try V4 before we give up
392             // SOCKS Protocol version 4 doesn't know how to deal with
393             // DOMAIN type of addresses (unresolved addresses here)
394             if (epoint.isUnresolved())
395                 throw new UnknownHostException(epoint.toString());
396             connectV4(in, out, epoint, deadlineMillis);
397             return;
398         }
399         if (((int)data[1]) == NO_METHODS)
400             throw new SocketException("SOCKS : No acceptable methods");
401         if (!authenticate(data[1], in, out, deadlineMillis)) {
402             throw new SocketException("SOCKS : authentication failed");
403         }
404         out.write(PROTO_VERS);
405         out.write(CONNECT);
406         out.write(0);
407         /* Test for IPV4/IPV6/Unresolved */
408         if (epoint.isUnresolved()) {
409             out.write(DOMAIN_NAME);
410             out.write(epoint.getHostName().length());
411             try {
412                 out.write(epoint.getHostName().getBytes("ISO-8859-1"));
413             } catch (java.io.UnsupportedEncodingException uee) {
414                 assert false;
415             }
416             out.write((epoint.getPort() >> 8) & 0xff);
417             out.write((epoint.getPort() >> 0) & 0xff);
418         } else if (epoint.getAddress() instanceof Inet6Address) {
419             out.write(IPV6);
420             out.write(epoint.getAddress().getAddress());
421             out.write((epoint.getPort() >> 8) & 0xff);
422             out.write((epoint.getPort() >> 0) & 0xff);
423         } else {
424             out.write(IPV4);
425             out.write(epoint.getAddress().getAddress());
426             out.write((epoint.getPort() >> 8) & 0xff);
427             out.write((epoint.getPort() >> 0) & 0xff);
428         }
429         out.flush();
430         data = new byte[4];
431         i = readSocksReply(in, data, deadlineMillis);
432         if (i != 4)
433             throw new SocketException("Reply from SOCKS server has bad length");
434         SocketException ex = null;
435         int len;
436         byte[] addr;
437         switch (data[1]) {
438         case REQUEST_OK:
439             // success!
440             switch(data[3]) {
441             case IPV4:
442                 addr = new byte[4];
443                 i = readSocksReply(in, addr, deadlineMillis);
444                 if (i != 4)
445                     throw new SocketException("Reply from SOCKS server badly formatted");
446                 data = new byte[2];
447                 i = readSocksReply(in, data, deadlineMillis);
448                 if (i != 2)
449                     throw new SocketException("Reply from SOCKS server badly formatted");
450                 break;
451             case DOMAIN_NAME:
452                 len = data[1];
453                 byte[] host = new byte[len];
454                 i = readSocksReply(in, host, deadlineMillis);
455                 if (i != len)
456                     throw new SocketException("Reply from SOCKS server badly formatted");
457                 data = new byte[2];
458                 i = readSocksReply(in, data, deadlineMillis);
459                 if (i != 2)
460                     throw new SocketException("Reply from SOCKS server badly formatted");
461                 break;
462             case IPV6:
463                 len = data[1];
464                 addr = new byte[len];
465                 i = readSocksReply(in, addr, deadlineMillis);
466                 if (i != len)
467                     throw new SocketException("Reply from SOCKS server badly formatted");
468                 data = new byte[2];
469                 i = readSocksReply(in, data, deadlineMillis);
470                 if (i != 2)
471                     throw new SocketException("Reply from SOCKS server badly formatted");
472                 break;
473             default:
474                 ex = new SocketException("Reply from SOCKS server contains wrong code");
475                 break;
476             }
477             break;
478         case GENERAL_FAILURE:
479             ex = new SocketException("SOCKS server general failure");
480             break;
481         case NOT_ALLOWED:
482             ex = new SocketException("SOCKS: Connection not allowed by ruleset");
483             break;
484         case NET_UNREACHABLE:
485             ex = new SocketException("SOCKS: Network unreachable");
486             break;
487         case HOST_UNREACHABLE:
488             ex = new SocketException("SOCKS: Host unreachable");
489             break;
490         case CONN_REFUSED:
491             ex = new SocketException("SOCKS: Connection refused");
492             break;
493         case TTL_EXPIRED:
494             ex =  new SocketException("SOCKS: TTL expired");
495             break;
496         case CMD_NOT_SUPPORTED:
497             ex = new SocketException("SOCKS: Command not supported");
498             break;
499         case ADDR_TYPE_NOT_SUP:
500             ex = new SocketException("SOCKS: address type not supported");
501             break;
502         }
503         if (ex != null) {
504             in.close();
505             out.close();
506             throw ex;
507         }
508         external_address = epoint;
509     }
510 
511     /**
512      * Returns the value of this socket's {@code address} field.
513      *
514      * @return  the value of this socket's {@code address} field.
515      * @see     java.net.SocketImpl#address
516      */
517     @Override
getInetAddress()518     protected InetAddress getInetAddress() {
519         if (external_address != null)
520             return external_address.getAddress();
521         else
522             return super.getInetAddress();
523     }
524 
525     /**
526      * Returns the value of this socket's {@code port} field.
527      *
528      * @return  the value of this socket's {@code port} field.
529      * @see     java.net.SocketImpl#port
530      */
531     @Override
getPort()532     protected int getPort() {
533         if (external_address != null)
534             return external_address.getPort();
535         else
536             return super.getPort();
537     }
538 
539     @Override
getLocalPort()540     protected int getLocalPort() {
541         if (socket != null)
542             return super.getLocalPort();
543         if (external_address != null)
544             return external_address.getPort();
545         else
546             return super.getLocalPort();
547     }
548 
549     @Override
close()550     protected void close() throws IOException {
551         if (cmdsock != null)
552             cmdsock.close();
553         cmdsock = null;
554         super.close();
555     }
556 
getUserName()557     private String getUserName() {
558         String userName = "";
559         if (applicationSetProxy) {
560             try {
561                 userName = System.getProperty("user.name");
562             } catch (SecurityException se) { /* swallow Exception */ }
563         } else {
564             userName = java.security.AccessController.doPrivileged(
565                 new sun.security.action.GetPropertyAction("user.name"));
566         }
567         return userName;
568     }
569 }
570