1 /* 2 * Copyright (c) 2000, 2019, 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 27 import java.io.IOException; 28 import java.io.InputStream; 29 import java.io.OutputStream; 30 import java.io.BufferedOutputStream; 31 import java.security.AccessController; 32 33 import jdk.internal.util.StaticProperty; 34 import sun.net.SocksProxy; 35 import sun.net.spi.DefaultProxySelector; 36 import sun.net.www.ParseUtil; 37 38 /** 39 * SOCKS (V4 & V5) TCP socket implementation (RFC 1928). 40 */ 41 42 class SocksSocketImpl extends DelegatingSocketImpl implements SocksConsts { 43 private String server = null; 44 private int serverPort = DEFAULT_PORT; 45 private InetSocketAddress external_address; 46 private boolean useV4 = false; 47 private Socket cmdsock = null; 48 private InputStream cmdIn = null; 49 private OutputStream cmdOut = null; 50 51 SocksSocketImpl(SocketImpl delegate) { 52 super(delegate); 53 } 54 55 SocksSocketImpl(Proxy proxy, SocketImpl delegate) { 56 super(delegate); 57 SocketAddress a = proxy.address(); 58 if (a instanceof InetSocketAddress) { 59 InetSocketAddress ad = (InetSocketAddress) a; 60 // Use getHostString() to avoid reverse lookups 61 server = ad.getHostString(); 62 serverPort = ad.getPort(); 63 } 64 useV4 = useV4(proxy); 65 } 66 67 private static boolean useV4(Proxy proxy) { 68 if (proxy instanceof SocksProxy 69 && ((SocksProxy)proxy).protocolVersion() == 4) { 70 return true; 71 } 72 return DefaultProxySelector.socksProxyVersion() == 4; 73 } 74 75 private synchronized void privilegedConnect(final String host, 76 final int port, 77 final int timeout) 78 throws IOException 79 { 80 try { 81 AccessController.doPrivileged( 82 new java.security.PrivilegedExceptionAction<>() { 83 public Void run() throws IOException { 84 superConnectServer(host, port, timeout); 85 cmdIn = getInputStream(); 86 cmdOut = getOutputStream(); 87 return null; 88 } 89 }); 90 } catch (java.security.PrivilegedActionException pae) { 91 throw (IOException) pae.getException(); 92 } 93 } 94 95 private void superConnectServer(String host, int port, 96 int timeout) throws IOException { 97 delegate.connect(new InetSocketAddress(host, port), timeout); 98 } 99 100 private static int remainingMillis(long deadlineMillis) throws IOException { 101 if (deadlineMillis == 0L) 102 return 0; 103 104 final long remaining = deadlineMillis - System.currentTimeMillis(); 105 if (remaining > 0) 106 return (int) remaining; 107 108 throw new SocketTimeoutException(); 109 } 110 111 private int readSocksReply(InputStream in, byte[] data, long deadlineMillis) throws IOException { 112 int len = data.length; 113 int received = 0; 114 int originalTimeout = (int) getOption(SocketOptions.SO_TIMEOUT); 115 try { 116 while (received < len) { 117 int count; 118 int remaining = remainingMillis(deadlineMillis); 119 setOption(SocketOptions.SO_TIMEOUT, remaining); 120 try { 121 count = in.read(data, received, len - received); 122 } catch (SocketTimeoutException e) { 123 throw new SocketTimeoutException("Connect timed out"); 124 } 125 if (count < 0) 126 throw new SocketException("Malformed reply from SOCKS server"); 127 received += count; 128 } 129 } finally { 130 setOption(SocketOptions.SO_TIMEOUT, originalTimeout); 131 } 132 return received; 133 } 134 135 private boolean authenticate(byte method, InputStream in, 136 BufferedOutputStream out, 137 long deadlineMillis) throws IOException { 138 // No Authentication required. We're done then! 139 if (method == NO_AUTH) 140 return true; 141 /* 142 * User/Password authentication. Try, in that order : 143 * - The application provided Authenticator, if any 144 * - the user.name & no password (backward compatibility behavior). 145 */ 146 if (method == USER_PASSW) { 147 String userName; 148 String password = null; 149 final InetAddress addr = InetAddress.getByName(server); 150 PasswordAuthentication pw = 151 java.security.AccessController.doPrivileged( 152 new java.security.PrivilegedAction<>() { 153 public PasswordAuthentication run() { 154 return Authenticator.requestPasswordAuthentication( 155 server, addr, serverPort, "SOCKS5", "SOCKS authentication", null); 156 } 157 }); 158 if (pw != null) { 159 userName = pw.getUserName(); 160 password = new String(pw.getPassword()); 161 } else { 162 userName = StaticProperty.userName(); 163 } 164 if (userName == null) 165 return false; 166 out.write(1); 167 out.write(userName.length()); 168 try { 169 out.write(userName.getBytes("ISO-8859-1")); 170 } catch (java.io.UnsupportedEncodingException uee) { 171 assert false; 172 } 173 if (password != null) { 174 out.write(password.length()); 175 try { 176 out.write(password.getBytes("ISO-8859-1")); 177 } catch (java.io.UnsupportedEncodingException uee) { 178 assert false; 179 } 180 } else 181 out.write(0); 182 out.flush(); 183 byte[] data = new byte[2]; 184 int i = readSocksReply(in, data, deadlineMillis); 185 if (i != 2 || data[1] != 0) { 186 /* RFC 1929 specifies that the connection MUST be closed if 187 authentication fails */ 188 out.close(); 189 in.close(); 190 return false; 191 } 192 /* Authentication succeeded */ 193 return true; 194 } 195 return false; 196 } 197 198 private void connectV4(InputStream in, OutputStream out, 199 InetSocketAddress endpoint, 200 long deadlineMillis) throws IOException { 201 if (!(endpoint.getAddress() instanceof Inet4Address)) { 202 throw new SocketException("SOCKS V4 requires IPv4 only addresses"); 203 } 204 out.write(PROTO_VERS4); 205 out.write(CONNECT); 206 out.write((endpoint.getPort() >> 8) & 0xff); 207 out.write((endpoint.getPort() >> 0) & 0xff); 208 out.write(endpoint.getAddress().getAddress()); 209 String userName = getUserName(); 210 try { 211 out.write(userName.getBytes("ISO-8859-1")); 212 } catch (java.io.UnsupportedEncodingException uee) { 213 assert false; 214 } 215 out.write(0); 216 out.flush(); 217 byte[] data = new byte[8]; 218 int n = readSocksReply(in, data, deadlineMillis); 219 if (n != 8) 220 throw new SocketException("Reply from SOCKS server has bad length: " + n); 221 if (data[0] != 0 && data[0] != 4) 222 throw new SocketException("Reply from SOCKS server has bad version"); 223 SocketException ex = null; 224 switch (data[1]) { 225 case 90: 226 // Success! 227 external_address = endpoint; 228 break; 229 case 91: 230 ex = new SocketException("SOCKS request rejected"); 231 break; 232 case 92: 233 ex = new SocketException("SOCKS server couldn't reach destination"); 234 break; 235 case 93: 236 ex = new SocketException("SOCKS authentication failed"); 237 break; 238 default: 239 ex = new SocketException("Reply from SOCKS server contains bad status"); 240 break; 241 } 242 if (ex != null) { 243 in.close(); 244 out.close(); 245 throw ex; 246 } 247 } 248 249 @Override 250 protected void connect(String host, int port) throws IOException { 251 connect(new InetSocketAddress(host, port), 0); 252 } 253 254 @Override 255 protected void connect(InetAddress address, int port) throws IOException { 256 connect(new InetSocketAddress(address, port), 0); 257 } 258 259 /** 260 * Connects the Socks Socket to the specified endpoint. It will first 261 * connect to the SOCKS proxy and negotiate the access. If the proxy 262 * grants the connections, then the connect is successful and all 263 * further traffic will go to the "real" endpoint. 264 * 265 * @param endpoint the {@code SocketAddress} to connect to. 266 * @param timeout the timeout value in milliseconds 267 * @throws IOException if the connection can't be established. 268 * @throws SecurityException if there is a security manager and it 269 * doesn't allow the connection 270 * @throws IllegalArgumentException if endpoint is null or a 271 * SocketAddress subclass not supported by this socket 272 */ 273 @Override 274 protected void connect(SocketAddress endpoint, int timeout) throws IOException { 275 final long deadlineMillis; 276 277 if (timeout == 0) { 278 deadlineMillis = 0L; 279 } else { 280 long finish = System.currentTimeMillis() + timeout; 281 deadlineMillis = finish < 0 ? Long.MAX_VALUE : finish; 282 } 283 284 SecurityManager security = System.getSecurityManager(); 285 if (endpoint == null || !(endpoint instanceof InetSocketAddress)) 286 throw new IllegalArgumentException("Unsupported address type"); 287 InetSocketAddress epoint = (InetSocketAddress) endpoint; 288 if (security != null) { 289 if (epoint.isUnresolved()) 290 security.checkConnect(epoint.getHostName(), 291 epoint.getPort()); 292 else 293 security.checkConnect(epoint.getAddress().getHostAddress(), 294 epoint.getPort()); 295 } 296 if (server == null) { 297 // This is the general case 298 // server is not null only when the socket was created with a 299 // specified proxy in which case it does bypass the ProxySelector 300 ProxySelector sel = java.security.AccessController.doPrivileged( 301 new java.security.PrivilegedAction<>() { 302 public ProxySelector run() { 303 return ProxySelector.getDefault(); 304 } 305 }); 306 if (sel == null) { 307 /* 308 * No default proxySelector --> direct connection 309 */ 310 delegate.connect(epoint, remainingMillis(deadlineMillis)); 311 return; 312 } 313 URI uri; 314 // Use getHostString() to avoid reverse lookups 315 String host = epoint.getHostString(); 316 // IPv6 literal? 317 if (epoint.getAddress() instanceof Inet6Address && 318 (!host.startsWith("[")) && (host.indexOf(':') >= 0)) { 319 host = "[" + host + "]"; 320 } 321 try { 322 uri = new URI("socket://" + ParseUtil.encodePath(host) + ":"+ epoint.getPort()); 323 } catch (URISyntaxException e) { 324 // This shouldn't happen 325 assert false : e; 326 uri = null; 327 } 328 Proxy p = null; 329 IOException savedExc = null; 330 java.util.Iterator<Proxy> iProxy = null; 331 iProxy = sel.select(uri).iterator(); 332 if (iProxy == null || !(iProxy.hasNext())) { 333 delegate.connect(epoint, remainingMillis(deadlineMillis)); 334 return; 335 } 336 while (iProxy.hasNext()) { 337 p = iProxy.next(); 338 if (p == null || p.type() != Proxy.Type.SOCKS) { 339 delegate.connect(epoint, remainingMillis(deadlineMillis)); 340 return; 341 } 342 343 if (!(p.address() instanceof InetSocketAddress)) 344 throw new SocketException("Unknown address type for proxy: " + p); 345 // Use getHostString() to avoid reverse lookups 346 server = ((InetSocketAddress) p.address()).getHostString(); 347 serverPort = ((InetSocketAddress) p.address()).getPort(); 348 useV4 = useV4(p); 349 350 // Connects to the SOCKS server 351 try { 352 privilegedConnect(server, serverPort, remainingMillis(deadlineMillis)); 353 // Worked, let's get outta here 354 break; 355 } catch (IOException e) { 356 // Ooops, let's notify the ProxySelector 357 sel.connectFailed(uri,p.address(),e); 358 server = null; 359 serverPort = -1; 360 savedExc = e; 361 // Will continue the while loop and try the next proxy 362 } 363 } 364 365 /* 366 * If server is still null at this point, none of the proxy 367 * worked 368 */ 369 if (server == null) { 370 throw new SocketException("Can't connect to SOCKS proxy:" 371 + savedExc.getMessage()); 372 } 373 } else { 374 // Connects to the SOCKS server 375 try { 376 privilegedConnect(server, serverPort, remainingMillis(deadlineMillis)); 377 } catch (IOException e) { 378 throw new SocketException(e.getMessage()); 379 } 380 } 381 382 // cmdIn & cmdOut were initialized during the privilegedConnect() call 383 BufferedOutputStream out = new BufferedOutputStream(cmdOut, 512); 384 InputStream in = cmdIn; 385 386 if (useV4) { 387 // SOCKS Protocol version 4 doesn't know how to deal with 388 // DOMAIN type of addresses (unresolved addresses here) 389 if (epoint.isUnresolved()) 390 throw new UnknownHostException(epoint.toString()); 391 connectV4(in, out, epoint, deadlineMillis); 392 return; 393 } 394 395 // This is SOCKS V5 396 out.write(PROTO_VERS); 397 out.write(2); 398 out.write(NO_AUTH); 399 out.write(USER_PASSW); 400 out.flush(); 401 byte[] data = new byte[2]; 402 int i = readSocksReply(in, data, deadlineMillis); 403 if (i != 2 || ((int)data[0]) != PROTO_VERS) { 404 // Maybe it's not a V5 sever after all 405 // Let's try V4 before we give up 406 // SOCKS Protocol version 4 doesn't know how to deal with 407 // DOMAIN type of addresses (unresolved addresses here) 408 if (epoint.isUnresolved()) 409 throw new UnknownHostException(epoint.toString()); 410 connectV4(in, out, epoint, deadlineMillis); 411 return; 412 } 413 if (((int)data[1]) == NO_METHODS) 414 throw new SocketException("SOCKS : No acceptable methods"); 415 if (!authenticate(data[1], in, out, deadlineMillis)) { 416 throw new SocketException("SOCKS : authentication failed"); 417 } 418 out.write(PROTO_VERS); 419 out.write(CONNECT); 420 out.write(0); 421 /* Test for IPV4/IPV6/Unresolved */ 422 if (epoint.isUnresolved()) { 423 out.write(DOMAIN_NAME); 424 out.write(epoint.getHostName().length()); 425 try { 426 out.write(epoint.getHostName().getBytes("ISO-8859-1")); 427 } catch (java.io.UnsupportedEncodingException uee) { 428 assert false; 429 } 430 out.write((epoint.getPort() >> 8) & 0xff); 431 out.write((epoint.getPort() >> 0) & 0xff); 432 } else if (epoint.getAddress() instanceof Inet6Address) { 433 out.write(IPV6); 434 out.write(epoint.getAddress().getAddress()); 435 out.write((epoint.getPort() >> 8) & 0xff); 436 out.write((epoint.getPort() >> 0) & 0xff); 437 } else { 438 out.write(IPV4); 439 out.write(epoint.getAddress().getAddress()); 440 out.write((epoint.getPort() >> 8) & 0xff); 441 out.write((epoint.getPort() >> 0) & 0xff); 442 } 443 out.flush(); 444 data = new byte[4]; 445 i = readSocksReply(in, data, deadlineMillis); 446 if (i != 4) 447 throw new SocketException("Reply from SOCKS server has bad length"); 448 SocketException ex = null; 449 int len; 450 byte[] addr; 451 switch (data[1]) { 452 case REQUEST_OK: 453 // success! 454 switch(data[3]) { 455 case IPV4: 456 addr = new byte[4]; 457 i = readSocksReply(in, addr, deadlineMillis); 458 if (i != 4) 459 throw new SocketException("Reply from SOCKS server badly formatted"); 460 data = new byte[2]; 461 i = readSocksReply(in, data, deadlineMillis); 462 if (i != 2) 463 throw new SocketException("Reply from SOCKS server badly formatted"); 464 break; 465 case DOMAIN_NAME: 466 byte[] lenByte = new byte[1]; 467 i = readSocksReply(in, lenByte, deadlineMillis); 468 if (i != 1) 469 throw new SocketException("Reply from SOCKS server badly formatted"); 470 len = lenByte[0] & 0xFF; 471 byte[] host = new byte[len]; 472 i = readSocksReply(in, host, deadlineMillis); 473 if (i != len) 474 throw new SocketException("Reply from SOCKS server badly formatted"); 475 data = new byte[2]; 476 i = readSocksReply(in, data, deadlineMillis); 477 if (i != 2) 478 throw new SocketException("Reply from SOCKS server badly formatted"); 479 break; 480 case IPV6: 481 len = 16; 482 addr = new byte[len]; 483 i = readSocksReply(in, addr, deadlineMillis); 484 if (i != len) 485 throw new SocketException("Reply from SOCKS server badly formatted"); 486 data = new byte[2]; 487 i = readSocksReply(in, data, deadlineMillis); 488 if (i != 2) 489 throw new SocketException("Reply from SOCKS server badly formatted"); 490 break; 491 default: 492 ex = new SocketException("Reply from SOCKS server contains wrong code"); 493 break; 494 } 495 break; 496 case GENERAL_FAILURE: 497 ex = new SocketException("SOCKS server general failure"); 498 break; 499 case NOT_ALLOWED: 500 ex = new SocketException("SOCKS: Connection not allowed by ruleset"); 501 break; 502 case NET_UNREACHABLE: 503 ex = new SocketException("SOCKS: Network unreachable"); 504 break; 505 case HOST_UNREACHABLE: 506 ex = new SocketException("SOCKS: Host unreachable"); 507 break; 508 case CONN_REFUSED: 509 ex = new SocketException("SOCKS: Connection refused"); 510 break; 511 case TTL_EXPIRED: 512 ex = new SocketException("SOCKS: TTL expired"); 513 break; 514 case CMD_NOT_SUPPORTED: 515 ex = new SocketException("SOCKS: Command not supported"); 516 break; 517 case ADDR_TYPE_NOT_SUP: 518 ex = new SocketException("SOCKS: address type not supported"); 519 break; 520 } 521 if (ex != null) { 522 in.close(); 523 out.close(); 524 throw ex; 525 } 526 external_address = epoint; 527 } 528 529 @Override 530 protected void listen(int backlog) { 531 throw new InternalError("should not get here"); 532 } 533 534 @Override 535 protected void accept(SocketImpl s) { 536 throw new InternalError("should not get here"); 537 } 538 539 /** 540 * Returns the value of this socket's {@code address} field. 541 * 542 * @return the value of this socket's {@code address} field. 543 * @see java.net.SocketImpl#address 544 */ 545 @Override 546 protected InetAddress getInetAddress() { 547 if (external_address != null) 548 return external_address.getAddress(); 549 else 550 return delegate.getInetAddress(); 551 } 552 553 /** 554 * Returns the value of this socket's {@code port} field. 555 * 556 * @return the value of this socket's {@code port} field. 557 * @see java.net.SocketImpl#port 558 */ 559 @Override 560 protected int getPort() { 561 if (external_address != null) 562 return external_address.getPort(); 563 else 564 return delegate.getPort(); 565 } 566 567 @Override 568 protected void close() throws IOException { 569 if (cmdsock != null) 570 cmdsock.close(); 571 cmdsock = null; 572 delegate.close(); 573 } 574 575 private String getUserName() { 576 return StaticProperty.userName(); 577 } 578 579 @Override 580 void reset() { 581 throw new InternalError("should not get here"); 582 } 583 }