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