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 @Override 260 void setSocket(Socket soc) { 261 delegate.socket = soc; 262 super.setSocket(soc); 263 } 264 265 @Override 266 void setServerSocket(ServerSocket soc) { 267 throw new InternalError("should not get here"); 268 } 269 270 /** 271 * Connects the Socks Socket to the specified endpoint. It will first 272 * connect to the SOCKS proxy and negotiate the access. If the proxy 273 * grants the connections, then the connect is successful and all 274 * further traffic will go to the "real" endpoint. 275 * 276 * @param endpoint the {@code SocketAddress} to connect to. 277 * @param timeout the timeout value in milliseconds 278 * @throws IOException if the connection can't be established. 279 * @throws SecurityException if there is a security manager and it 280 * doesn't allow the connection 281 * @throws IllegalArgumentException if endpoint is null or a 282 * SocketAddress subclass not supported by this socket 283 */ 284 @Override 285 protected void connect(SocketAddress endpoint, int timeout) throws IOException { 286 final long deadlineMillis; 287 288 if (timeout == 0) { 289 deadlineMillis = 0L; 290 } else { 291 long finish = System.currentTimeMillis() + timeout; 292 deadlineMillis = finish < 0 ? Long.MAX_VALUE : finish; 293 } 294 295 SecurityManager security = System.getSecurityManager(); 296 if (endpoint == null || !(endpoint instanceof InetSocketAddress)) 297 throw new IllegalArgumentException("Unsupported address type"); 298 InetSocketAddress epoint = (InetSocketAddress) endpoint; 299 if (security != null) { 300 if (epoint.isUnresolved()) 301 security.checkConnect(epoint.getHostName(), 302 epoint.getPort()); 303 else 304 security.checkConnect(epoint.getAddress().getHostAddress(), 305 epoint.getPort()); 306 } 307 if (server == null) { 308 // This is the general case 309 // server is not null only when the socket was created with a 310 // specified proxy in which case it does bypass the ProxySelector 311 ProxySelector sel = java.security.AccessController.doPrivileged( 312 new java.security.PrivilegedAction<>() { 313 public ProxySelector run() { 314 return ProxySelector.getDefault(); 315 } 316 }); 317 if (sel == null) { 318 /* 319 * No default proxySelector --> direct connection 320 */ 321 delegate.connect(epoint, remainingMillis(deadlineMillis)); 322 return; 323 } 324 URI uri; 325 // Use getHostString() to avoid reverse lookups 326 String host = epoint.getHostString(); 327 // IPv6 literal? 328 if (epoint.getAddress() instanceof Inet6Address && 329 (!host.startsWith("[")) && (host.indexOf(':') >= 0)) { 330 host = "[" + host + "]"; 331 } 332 try { 333 uri = new URI("socket://" + ParseUtil.encodePath(host) + ":"+ epoint.getPort()); 334 } catch (URISyntaxException e) { 335 // This shouldn't happen 336 assert false : e; 337 uri = null; 338 } 339 Proxy p = null; 340 IOException savedExc = null; 341 java.util.Iterator<Proxy> iProxy = null; 342 iProxy = sel.select(uri).iterator(); 343 if (iProxy == null || !(iProxy.hasNext())) { 344 delegate.connect(epoint, remainingMillis(deadlineMillis)); 345 return; 346 } 347 while (iProxy.hasNext()) { 348 p = iProxy.next(); 349 if (p == null || p.type() != Proxy.Type.SOCKS) { 350 delegate.connect(epoint, remainingMillis(deadlineMillis)); 351 return; 352 } 353 354 if (!(p.address() instanceof InetSocketAddress)) 355 throw new SocketException("Unknown address type for proxy: " + p); 356 // Use getHostString() to avoid reverse lookups 357 server = ((InetSocketAddress) p.address()).getHostString(); 358 serverPort = ((InetSocketAddress) p.address()).getPort(); 359 useV4 = useV4(p); 360 361 // Connects to the SOCKS server 362 try { 363 privilegedConnect(server, serverPort, remainingMillis(deadlineMillis)); 364 // Worked, let's get outta here 365 break; 366 } catch (IOException e) { 367 // Ooops, let's notify the ProxySelector 368 sel.connectFailed(uri,p.address(),e); 369 server = null; 370 serverPort = -1; 371 savedExc = e; 372 // Will continue the while loop and try the next proxy 373 } 374 } 375 376 /* 377 * If server is still null at this point, none of the proxy 378 * worked 379 */ 380 if (server == null) { 381 throw new SocketException("Can't connect to SOCKS proxy:" 382 + savedExc.getMessage()); 383 } 384 } else { 385 // Connects to the SOCKS server 386 try { 387 privilegedConnect(server, serverPort, remainingMillis(deadlineMillis)); 388 } catch (IOException e) { 389 throw new SocketException(e.getMessage()); 390 } 391 } 392 393 // cmdIn & cmdOut were initialized during the privilegedConnect() call 394 BufferedOutputStream out = new BufferedOutputStream(cmdOut, 512); 395 InputStream in = cmdIn; 396 397 if (useV4) { 398 // SOCKS Protocol version 4 doesn't know how to deal with 399 // DOMAIN type of addresses (unresolved addresses here) 400 if (epoint.isUnresolved()) 401 throw new UnknownHostException(epoint.toString()); 402 connectV4(in, out, epoint, deadlineMillis); 403 return; 404 } 405 406 // This is SOCKS V5 407 out.write(PROTO_VERS); 408 out.write(2); 409 out.write(NO_AUTH); 410 out.write(USER_PASSW); 411 out.flush(); 412 byte[] data = new byte[2]; 413 int i = readSocksReply(in, data, deadlineMillis); 414 if (i != 2 || ((int)data[0]) != PROTO_VERS) { 415 // Maybe it's not a V5 sever after all 416 // Let's try V4 before we give up 417 // SOCKS Protocol version 4 doesn't know how to deal with 418 // DOMAIN type of addresses (unresolved addresses here) 419 if (epoint.isUnresolved()) 420 throw new UnknownHostException(epoint.toString()); 421 connectV4(in, out, epoint, deadlineMillis); 422 return; 423 } 424 if (((int)data[1]) == NO_METHODS) 425 throw new SocketException("SOCKS : No acceptable methods"); 426 if (!authenticate(data[1], in, out, deadlineMillis)) { 427 throw new SocketException("SOCKS : authentication failed"); 428 } 429 out.write(PROTO_VERS); 430 out.write(CONNECT); 431 out.write(0); 432 /* Test for IPV4/IPV6/Unresolved */ 433 if (epoint.isUnresolved()) { 434 out.write(DOMAIN_NAME); 435 out.write(epoint.getHostName().length()); 436 try { 437 out.write(epoint.getHostName().getBytes("ISO-8859-1")); 438 } catch (java.io.UnsupportedEncodingException uee) { 439 assert false; 440 } 441 out.write((epoint.getPort() >> 8) & 0xff); 442 out.write((epoint.getPort() >> 0) & 0xff); 443 } else if (epoint.getAddress() instanceof Inet6Address) { 444 out.write(IPV6); 445 out.write(epoint.getAddress().getAddress()); 446 out.write((epoint.getPort() >> 8) & 0xff); 447 out.write((epoint.getPort() >> 0) & 0xff); 448 } else { 449 out.write(IPV4); 450 out.write(epoint.getAddress().getAddress()); 451 out.write((epoint.getPort() >> 8) & 0xff); 452 out.write((epoint.getPort() >> 0) & 0xff); 453 } 454 out.flush(); 455 data = new byte[4]; 456 i = readSocksReply(in, data, deadlineMillis); 457 if (i != 4) 458 throw new SocketException("Reply from SOCKS server has bad length"); 459 SocketException ex = null; 460 int len; 461 byte[] addr; 462 switch (data[1]) { 463 case REQUEST_OK: 464 // success! 465 switch(data[3]) { 466 case IPV4: 467 addr = new byte[4]; 468 i = readSocksReply(in, addr, deadlineMillis); 469 if (i != 4) 470 throw new SocketException("Reply from SOCKS server badly formatted"); 471 data = new byte[2]; 472 i = readSocksReply(in, data, deadlineMillis); 473 if (i != 2) 474 throw new SocketException("Reply from SOCKS server badly formatted"); 475 break; 476 case DOMAIN_NAME: 477 byte[] lenByte = new byte[1]; 478 i = readSocksReply(in, lenByte, deadlineMillis); 479 if (i != 1) 480 throw new SocketException("Reply from SOCKS server badly formatted"); 481 len = lenByte[0] & 0xFF; 482 byte[] host = new byte[len]; 483 i = readSocksReply(in, host, 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 case IPV6: 492 len = 16; 493 addr = new byte[len]; 494 i = readSocksReply(in, addr, deadlineMillis); 495 if (i != len) 496 throw new SocketException("Reply from SOCKS server badly formatted"); 497 data = new byte[2]; 498 i = readSocksReply(in, data, deadlineMillis); 499 if (i != 2) 500 throw new SocketException("Reply from SOCKS server badly formatted"); 501 break; 502 default: 503 ex = new SocketException("Reply from SOCKS server contains wrong code"); 504 break; 505 } 506 break; 507 case GENERAL_FAILURE: 508 ex = new SocketException("SOCKS server general failure"); 509 break; 510 case NOT_ALLOWED: 511 ex = new SocketException("SOCKS: Connection not allowed by ruleset"); 512 break; 513 case NET_UNREACHABLE: 514 ex = new SocketException("SOCKS: Network unreachable"); 515 break; 516 case HOST_UNREACHABLE: 517 ex = new SocketException("SOCKS: Host unreachable"); 518 break; 519 case CONN_REFUSED: 520 ex = new SocketException("SOCKS: Connection refused"); 521 break; 522 case TTL_EXPIRED: 523 ex = new SocketException("SOCKS: TTL expired"); 524 break; 525 case CMD_NOT_SUPPORTED: 526 ex = new SocketException("SOCKS: Command not supported"); 527 break; 528 case ADDR_TYPE_NOT_SUP: 529 ex = new SocketException("SOCKS: address type not supported"); 530 break; 531 } 532 if (ex != null) { 533 in.close(); 534 out.close(); 535 throw ex; 536 } 537 external_address = epoint; 538 } 539 540 @Override 541 protected void listen(int backlog) { 542 throw new InternalError("should not get here"); 543 } 544 545 @Override 546 protected void accept(SocketImpl s) { 547 throw new InternalError("should not get here"); 548 } 549 550 /** 551 * Returns the value of this socket's {@code address} field. 552 * 553 * @return the value of this socket's {@code address} field. 554 * @see java.net.SocketImpl#address 555 */ 556 @Override 557 protected InetAddress getInetAddress() { 558 if (external_address != null) 559 return external_address.getAddress(); 560 else 561 return delegate.getInetAddress(); 562 } 563 564 /** 565 * Returns the value of this socket's {@code port} field. 566 * 567 * @return the value of this socket's {@code port} field. 568 * @see java.net.SocketImpl#port 569 */ 570 @Override 571 protected int getPort() { 572 if (external_address != null) 573 return external_address.getPort(); 574 else 575 return delegate.getPort(); 576 } 577 578 @Override 579 protected void close() throws IOException { 580 if (cmdsock != null) 581 cmdsock.close(); 582 cmdsock = null; 583 delegate.close(); 584 } 585 586 private String getUserName() { 587 return StaticProperty.userName(); 588 } 589 590 @Override 591 void reset() { 592 throw new InternalError("should not get here"); 593 } 594 }