1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package org.apache.harmony.luni.net; 19 20 import java.io.FileDescriptor; 21 import java.io.IOException; 22 import java.io.InputStream; 23 import java.io.InterruptedIOException; 24 import java.io.OutputStream; 25 import java.lang.reflect.Field; 26 import java.net.ConnectException; 27 import java.net.InetAddress; 28 import java.net.InetSocketAddress; 29 import java.net.Proxy; 30 import java.net.SocketAddress; 31 import java.net.SocketException; 32 import java.net.SocketImpl; 33 import java.net.SocketOptions; 34 import java.net.SocketTimeoutException; 35 import java.net.UnknownHostException; 36 import java.security.AccessController; 37 import java.security.PrivilegedAction; 38 39 import org.apache.harmony.luni.platform.INetworkSystem; 40 import org.apache.harmony.luni.platform.Platform; 41 import org.apache.harmony.luni.util.Msg; 42 43 /** 44 * A concrete connected-socket implementation. 45 */ 46 public class PlainSocketImpl extends SocketImpl { 47 48 // Const copy from socket 49 50 static final int MULTICAST_IF = 1; 51 52 static final int MULTICAST_TTL = 2; 53 54 static final int TCP_NODELAY = 4; 55 56 static final int FLAG_SHUTDOWN = 8; 57 58 // For SOCKS support. A SOCKS bind() uses the last 59 // host connected to in its request. 60 static private InetAddress lastConnectedAddress; 61 62 static private int lastConnectedPort; 63 64 private static Field fdField; 65 66 private static Field localportField; 67 68 private boolean tcpNoDelay = true; 69 70 /** 71 * used to store the trafficClass value which is simply returned as the 72 * value that was set. We also need it to pass it to methods that specify an 73 * address packets are going to be sent to 74 */ 75 private int trafficClass; 76 77 protected INetworkSystem netImpl = Platform.getNetworkSystem(); 78 79 public int receiveTimeout = 0; 80 81 public boolean streaming = true; 82 83 public boolean shutdownInput; 84 85 Proxy proxy; 86 87 public PlainSocketImpl() { 88 super(); 89 fd = new FileDescriptor(); 90 } 91 92 public PlainSocketImpl(FileDescriptor fd) { 93 super(); 94 this.fd = fd; 95 } 96 97 /** 98 * creates an instance with specified proxy. 99 */ 100 public PlainSocketImpl(Proxy proxy) { 101 this(); 102 this.proxy = proxy; 103 } 104 105 public PlainSocketImpl(FileDescriptor fd, int localport, InetAddress addr, int port) { 106 super(); 107 this.fd = fd; 108 this.localport = localport; 109 this.address = addr; 110 this.port = port; 111 } 112 113 @Override 114 protected void accept(SocketImpl newImpl) throws IOException { 115 if (NetUtil.usingSocks(proxy)) { 116 ((PlainSocketImpl) newImpl).socksBind(); 117 ((PlainSocketImpl) newImpl).socksAccept(); 118 return; 119 } 120 121 try { 122 if (newImpl instanceof PlainSocketImpl) { 123 PlainSocketImpl newPlainSocketImpl = (PlainSocketImpl) newImpl; 124 // BEGIN android-changed 125 // call accept instead of acceptStreamImpl (native impl is identical) 126 netImpl.accept(fd, newImpl, newPlainSocketImpl 127 .getFileDescriptor(), receiveTimeout); 128 // END android-changed 129 newPlainSocketImpl.setLocalport(getLocalPort()); 130 } else { 131 // if newImpl is not an instance of PlainSocketImpl, use 132 // reflection to get/set protected fields. 133 if (null == fdField) { 134 fdField = getSocketImplField("fd"); //$NON-NLS-1$ 135 } 136 FileDescriptor newFd = (FileDescriptor) fdField.get(newImpl); 137 // BEGIN android-changed 138 // call accept instead of acceptStreamImpl (native impl is identical) 139 netImpl.accept(fd, newImpl, newFd, receiveTimeout); 140 // END android-cahnged 141 142 if (null == localportField) { 143 localportField = getSocketImplField("localport"); //$NON-NLS-1$ 144 } 145 localportField.setInt(newImpl, getLocalPort()); 146 } 147 } catch (InterruptedIOException e) { 148 throw new SocketTimeoutException(e.getMessage()); 149 } catch (IllegalAccessException e) { 150 // empty 151 } 152 } 153 154 /** 155 * gets SocketImpl field by reflection. 156 */ 157 private Field getSocketImplField(final String fieldName) { 158 return AccessController.doPrivileged(new PrivilegedAction<Field>() { 159 public Field run() { 160 Field field = null; 161 try { 162 field = SocketImpl.class.getDeclaredField(fieldName); 163 field.setAccessible(true); 164 } catch (NoSuchFieldException e) { 165 throw new Error(e); 166 } 167 return field; 168 } 169 }); 170 } 171 172 @Override 173 protected synchronized int available() throws IOException { 174 // we need to check if the input has been shutdown. If so 175 // we should return that there is no data to be read 176 if (shutdownInput == true) { 177 return 0; 178 } 179 return netImpl.availableStream(fd); 180 } 181 182 @Override 183 protected void bind(InetAddress anAddr, int aPort) throws IOException { 184 netImpl.bind(fd, anAddr, aPort); 185 // PlainSocketImpl2.socketBindImpl2(fd, aPort, anAddr); 186 address = anAddr; 187 if (0 != aPort) { 188 localport = aPort; 189 } else { 190 localport = netImpl.getSocketLocalPort(fd); 191 } 192 } 193 194 @Override 195 protected void close() throws IOException { 196 synchronized (fd) { 197 if (fd.valid()) { 198 if ((netImpl.getSocketFlags() & FLAG_SHUTDOWN) != 0) { 199 try { 200 shutdownOutput(); 201 } catch (Exception e) { 202 } 203 } 204 netImpl.socketClose(fd); 205 fd = new FileDescriptor(); 206 } 207 } 208 } 209 210 @Override 211 protected void connect(String aHost, int aPort) throws IOException { 212 // BEGIN android-changed: remove useless IPv6 check. 213 connect(netImpl.getHostByName(aHost), aPort); 214 // END android-changed 215 } 216 217 @Override 218 protected void connect(InetAddress anAddr, int aPort) throws IOException { 219 connect(anAddr, aPort, 0); 220 } 221 222 /** 223 * Connects this socket to the specified remote host address/port. 224 * 225 * @param anAddr 226 * the remote host address to connect to 227 * @param aPort 228 * the remote port to connect to 229 * @param timeout 230 * a timeout where supported. 0 means no timeout 231 * @throws IOException 232 * if an error occurs while connecting 233 */ 234 private void connect(InetAddress anAddr, int aPort, int timeout) 235 throws IOException { 236 237 InetAddress normalAddr = anAddr.isAnyLocalAddress() ? InetAddress.getLocalHost() : anAddr; 238 try { 239 if (streaming) { 240 if (NetUtil.usingSocks(proxy)) { 241 socksConnect(anAddr, aPort, 0); 242 } else { 243 if (timeout == 0) { 244 netImpl.connect(fd, trafficClass, normalAddr, aPort); 245 } else { 246 netImpl.connectStreamWithTimeoutSocket(fd, aPort, 247 timeout, trafficClass, normalAddr); 248 } 249 } 250 } else { 251 netImpl.connectDatagram(fd, aPort, trafficClass, normalAddr); 252 } 253 } catch (ConnectException e) { 254 throw new ConnectException(anAddr + ":" + aPort + " - " 255 + e.getMessage()); 256 } 257 super.address = normalAddr; 258 super.port = aPort; 259 } 260 261 @Override 262 protected void create(boolean streaming) throws IOException { 263 this.streaming = streaming; 264 if (streaming) { 265 netImpl.createStreamSocket(fd, NetUtil.preferIPv4Stack()); 266 } else { 267 netImpl.createDatagramSocket(fd, NetUtil.preferIPv4Stack()); 268 } 269 } 270 271 @Override 272 protected void finalize() throws IOException { 273 close(); 274 } 275 276 @Override 277 protected synchronized InputStream getInputStream() throws IOException { 278 if (!fd.valid()) { 279 throw new SocketException(Msg.getString("K003d")); 280 } 281 282 return new SocketInputStream(this); 283 } 284 285 @Override 286 public Object getOption(int optID) throws SocketException { 287 if (optID == SocketOptions.SO_TIMEOUT) { 288 return Integer.valueOf(receiveTimeout); 289 } else if (optID == SocketOptions.IP_TOS) { 290 return Integer.valueOf(trafficClass); 291 } else { 292 // Call the native first so there will be 293 // an exception if the socket if closed. 294 Object result = netImpl.getSocketOption(fd, optID); 295 if (optID == SocketOptions.TCP_NODELAY 296 && (netImpl.getSocketFlags() & TCP_NODELAY) != 0) { 297 return Boolean.valueOf(tcpNoDelay); 298 } 299 return result; 300 } 301 } 302 303 @Override 304 protected synchronized OutputStream getOutputStream() throws IOException { 305 if (!fd.valid()) { 306 throw new SocketException(Msg.getString("K003d")); //$NON-NLS-1$ 307 } 308 return new SocketOutputStream(this); 309 } 310 311 @Override 312 protected void listen(int backlog) throws IOException { 313 if (NetUtil.usingSocks(proxy)) { 314 // Do nothing for a SOCKS connection. The listen occurs on the 315 // server during the bind. 316 return; 317 } 318 netImpl.listenStreamSocket(fd, backlog); 319 } 320 321 @Override 322 public void setOption(int optID, Object val) throws SocketException { 323 if (optID == SocketOptions.SO_TIMEOUT) { 324 receiveTimeout = ((Integer) val).intValue(); 325 } else { 326 try { 327 netImpl.setSocketOption(fd, optID, val); 328 if (optID == SocketOptions.TCP_NODELAY 329 && (netImpl.getSocketFlags() & TCP_NODELAY) != 0) { 330 tcpNoDelay = ((Boolean) val).booleanValue(); 331 } 332 } catch (SocketException e) { 333 // we don't throw an exception for IP_TOS even if the platform 334 // won't let us set the requested value 335 if (optID != SocketOptions.IP_TOS) { 336 throw e; 337 } 338 } 339 340 /* 341 * save this value as it is actually used differently for IPv4 and 342 * IPv6 so we cannot get the value using the getOption. The option 343 * is actually only set for IPv4 and a masked version of the value 344 * will be set as only a subset of the values are allowed on the 345 * socket. Therefore we need to retain it to return the value that 346 * was set. We also need the value to be passed into a number of 347 * natives so that it can be used properly with IPv6 348 */ 349 if (optID == SocketOptions.IP_TOS) { 350 trafficClass = ((Integer) val).intValue(); 351 } 352 } 353 } 354 355 /** 356 * Gets the SOCKS proxy server port. 357 */ 358 private int socksGetServerPort() { 359 // get socks server port from proxy. It is unnecessary to check 360 // "socksProxyPort" property, since proxy setting should only be 361 // determined by ProxySelector. 362 InetSocketAddress addr = (InetSocketAddress) proxy.address(); 363 return addr.getPort(); 364 365 } 366 367 /** 368 * Gets the InetAddress of the SOCKS proxy server. 369 */ 370 private InetAddress socksGetServerAddress() throws UnknownHostException { 371 String proxyName; 372 // get socks server address from proxy. It is unnecessary to check 373 // "socksProxyHost" property, since all proxy setting should be 374 // determined by ProxySelector. 375 InetSocketAddress addr = (InetSocketAddress) proxy.address(); 376 proxyName = addr.getHostName(); 377 if (null == proxyName) { 378 proxyName = addr.getAddress().getHostAddress(); 379 } 380 // BEGIN android-changed: remove useless IPv6 check. 381 return netImpl.getHostByName(proxyName); 382 // END android-changed 383 } 384 385 /** 386 * Connect using a SOCKS server. 387 */ 388 private void socksConnect(InetAddress applicationServerAddress, 389 int applicationServerPort, int timeout) throws IOException { 390 try { 391 if (timeout == 0) { 392 netImpl.connect(fd, trafficClass, socksGetServerAddress(), 393 socksGetServerPort()); 394 } else { 395 netImpl.connectStreamWithTimeoutSocket(fd, 396 socksGetServerPort(), timeout, trafficClass, 397 socksGetServerAddress()); 398 } 399 400 } catch (Exception e) { 401 throw new SocketException(Msg.getString("K003e", e)); //$NON-NLS-1$ 402 } 403 404 socksRequestConnection(applicationServerAddress, applicationServerPort); 405 406 lastConnectedAddress = applicationServerAddress; 407 lastConnectedPort = applicationServerPort; 408 } 409 410 /** 411 * Request a SOCKS connection to the application server given. If the 412 * request fails to complete successfully, an exception is thrown. 413 */ 414 private void socksRequestConnection(InetAddress applicationServerAddress, 415 int applicationServerPort) throws IOException { 416 socksSendRequest(Socks4Message.COMMAND_CONNECT, 417 applicationServerAddress, applicationServerPort); 418 Socks4Message reply = socksReadReply(); 419 if (reply.getCommandOrResult() != Socks4Message.RETURN_SUCCESS) { 420 throw new IOException(reply.getErrorString(reply 421 .getCommandOrResult())); 422 } 423 } 424 425 /** 426 * Perform an accept for a SOCKS bind. 427 */ 428 public void socksAccept() throws IOException { 429 Socks4Message reply = socksReadReply(); 430 if (reply.getCommandOrResult() != Socks4Message.RETURN_SUCCESS) { 431 throw new IOException(reply.getErrorString(reply 432 .getCommandOrResult())); 433 } 434 } 435 436 /** 437 * Shutdown the input portion of the socket. 438 */ 439 @Override 440 protected void shutdownInput() throws IOException { 441 shutdownInput = true; 442 netImpl.shutdownInput(fd); 443 } 444 445 /** 446 * Shutdown the output portion of the socket. 447 */ 448 @Override 449 protected void shutdownOutput() throws IOException { 450 netImpl.shutdownOutput(fd); 451 } 452 453 /** 454 * Bind using a SOCKS server. 455 */ 456 private void socksBind() throws IOException { 457 try { 458 netImpl.connect(fd, trafficClass, socksGetServerAddress(), 459 socksGetServerPort()); 460 } catch (Exception e) { 461 throw new IOException(Msg.getString("K003f", e)); //$NON-NLS-1$ 462 } 463 464 // There must be a connection to an application host for the bind to 465 // work. 466 if (lastConnectedAddress == null) { 467 throw new SocketException(Msg.getString("K0040")); //$NON-NLS-1$ 468 } 469 470 // Use the last connected address and port in the bind request. 471 socksSendRequest(Socks4Message.COMMAND_BIND, lastConnectedAddress, 472 lastConnectedPort); 473 Socks4Message reply = socksReadReply(); 474 475 if (reply.getCommandOrResult() != Socks4Message.RETURN_SUCCESS) { 476 throw new IOException(reply.getErrorString(reply 477 .getCommandOrResult())); 478 } 479 480 // A peculiarity of socks 4 - if the address returned is 0, use the 481 // original socks server address. 482 if (reply.getIP() == 0) { 483 address = socksGetServerAddress(); 484 } else { 485 // IPv6 support not yet required as 486 // currently the Socks4Message.getIP() only returns int, 487 // so only works with IPv4 4byte addresses 488 byte[] replyBytes = new byte[4]; 489 NetUtil.intToBytes(reply.getIP(), replyBytes, 0); 490 address = InetAddress.getByAddress(replyBytes); 491 } 492 localport = reply.getPort(); 493 } 494 495 /** 496 * Send a SOCKS V4 request. 497 */ 498 private void socksSendRequest(int command, InetAddress address, int port) 499 throws IOException { 500 Socks4Message request = new Socks4Message(); 501 request.setCommandOrResult(command); 502 request.setPort(port); 503 request.setIP(address.getAddress()); 504 request.setUserId("default"); //$NON-NLS-1$ 505 506 getOutputStream().write(request.getBytes(), 0, request.getLength()); 507 } 508 509 /** 510 * Read a SOCKS V4 reply. 511 */ 512 private Socks4Message socksReadReply() throws IOException { 513 Socks4Message reply = new Socks4Message(); 514 int bytesRead = 0; 515 while (bytesRead < Socks4Message.REPLY_LENGTH) { 516 int count = getInputStream().read(reply.getBytes(), bytesRead, 517 Socks4Message.REPLY_LENGTH - bytesRead); 518 if (-1 == count) { 519 break; 520 } 521 bytesRead += count; 522 } 523 if (Socks4Message.REPLY_LENGTH != bytesRead) { 524 throw new SocketException(Msg.getString("KA011")); //$NON-NLS-1$ 525 } 526 return reply; 527 } 528 529 @Override 530 protected void connect(SocketAddress remoteAddr, int timeout) 531 throws IOException { 532 InetSocketAddress inetAddr = (InetSocketAddress) remoteAddr; 533 connect(inetAddr.getAddress(), inetAddr.getPort(), timeout); 534 } 535 536 /** 537 * Answer if the socket supports urgent data. 538 */ 539 @Override 540 protected boolean supportsUrgentData() { 541 return !streaming || netImpl.supportsUrgentData(fd); 542 } 543 544 @Override 545 protected void sendUrgentData(int value) throws IOException { 546 netImpl.sendUrgentData(fd, (byte) value); 547 } 548 549 FileDescriptor getFD() { 550 return fd; 551 } 552 553 private void setLocalport(int localport) { 554 this.localport = localport; 555 } 556 557 int read(byte[] buffer, int offset, int count) throws IOException { 558 if (shutdownInput) { 559 return -1; 560 } 561 int read = netImpl.read(fd, buffer, offset, count, receiveTimeout); 562 // Return of zero bytes for a blocking socket means a timeout occurred 563 if (read == 0) { 564 throw new SocketTimeoutException(); 565 } 566 // Return of -1 indicates the peer was closed 567 if (read == -1) { 568 shutdownInput = true; 569 } 570 return read; 571 } 572 573 int write(byte[] buffer, int offset, int count) throws IOException { 574 if (!streaming) { 575 return netImpl.sendDatagram2(fd, buffer, offset, count, port, 576 address); 577 } 578 return netImpl.write(fd, buffer, offset, count); 579 } 580 } 581