| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package java.net; |
| |
| import dalvik.system.CloseGuard; |
| import java.io.FileDescriptor; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.net.ConnectException; |
| import java.net.InetAddress; |
| import java.net.InetSocketAddress; |
| import java.net.Proxy; |
| import java.net.SocketAddress; |
| import java.net.SocketException; |
| import java.net.SocketImpl; |
| import java.net.SocketTimeoutException; |
| import java.net.UnknownHostException; |
| import java.nio.ByteOrder; |
| import java.util.Arrays; |
| import libcore.io.ErrnoException; |
| import libcore.io.IoBridge; |
| import libcore.io.Libcore; |
| import libcore.io.Memory; |
| import libcore.io.Streams; |
| import static libcore.io.OsConstants.*; |
| |
| /** |
| * @hide used in java.nio. |
| */ |
| public class PlainSocketImpl extends SocketImpl { |
| |
| // For SOCKS support. A SOCKS bind() uses the last |
| // host connected to in its request. |
| private static InetAddress lastConnectedAddress; |
| |
| private static int lastConnectedPort; |
| |
| private boolean streaming = true; |
| |
| private boolean shutdownInput; |
| |
| private Proxy proxy; |
| |
| private final CloseGuard guard = CloseGuard.get(); |
| |
| public PlainSocketImpl(FileDescriptor fd) { |
| this.fd = fd; |
| if (fd.valid()) { |
| guard.open("close"); |
| } |
| } |
| |
| public PlainSocketImpl(Proxy proxy) { |
| this(new FileDescriptor()); |
| this.proxy = proxy; |
| } |
| |
| public PlainSocketImpl() { |
| this(new FileDescriptor()); |
| } |
| |
| public PlainSocketImpl(FileDescriptor fd, int localport, InetAddress addr, int port) { |
| this.fd = fd; |
| this.localport = localport; |
| this.address = addr; |
| this.port = port; |
| if (fd.valid()) { |
| guard.open("close"); |
| } |
| } |
| |
| @Override |
| protected void accept(SocketImpl newImpl) throws IOException { |
| if (usingSocks()) { |
| ((PlainSocketImpl) newImpl).socksBind(); |
| ((PlainSocketImpl) newImpl).socksAccept(); |
| return; |
| } |
| |
| try { |
| InetSocketAddress peerAddress = new InetSocketAddress(); |
| FileDescriptor clientFd = Libcore.os.accept(fd, peerAddress); |
| |
| // TODO: we can't just set newImpl.fd to clientFd because a nio SocketChannel may |
| // be sharing the FileDescriptor. http://b//4452981. |
| newImpl.fd.setInt$(clientFd.getInt$()); |
| |
| newImpl.address = peerAddress.getAddress(); |
| newImpl.port = peerAddress.getPort(); |
| } catch (ErrnoException errnoException) { |
| if (errnoException.errno == EAGAIN || errnoException.errno == EWOULDBLOCK) { |
| throw new SocketTimeoutException(errnoException); |
| } |
| throw errnoException.rethrowAsSocketException(); |
| } |
| |
| // Reset the client's inherited read timeout to the Java-specified default of 0. |
| newImpl.setOption(SocketOptions.SO_TIMEOUT, Integer.valueOf(0)); |
| |
| newImpl.localport = IoBridge.getSocketLocalPort(newImpl.fd); |
| } |
| |
| private boolean usingSocks() { |
| return proxy != null && proxy.type() == Proxy.Type.SOCKS; |
| } |
| |
| public void initLocalPort(int localPort) { |
| this.localport = localPort; |
| } |
| |
| public void initRemoteAddressAndPort(InetAddress remoteAddress, int remotePort) { |
| this.address = remoteAddress; |
| this.port = remotePort; |
| } |
| |
| private void checkNotClosed() throws IOException { |
| if (!fd.valid()) { |
| throw new SocketException("Socket is closed"); |
| } |
| } |
| |
| @Override |
| protected synchronized int available() throws IOException { |
| checkNotClosed(); |
| // we need to check if the input has been shutdown. If so |
| // we should return that there is no data to be read |
| if (shutdownInput) { |
| return 0; |
| } |
| return IoBridge.available(fd); |
| } |
| |
| @Override protected void bind(InetAddress address, int port) throws IOException { |
| IoBridge.bind(fd, address, port); |
| this.address = address; |
| if (port != 0) { |
| this.localport = port; |
| } else { |
| this.localport = IoBridge.getSocketLocalPort(fd); |
| } |
| } |
| |
| @Override |
| protected synchronized void close() throws IOException { |
| guard.close(); |
| IoBridge.closeSocket(fd); |
| } |
| |
| @Override |
| protected void connect(String aHost, int aPort) throws IOException { |
| connect(InetAddress.getByName(aHost), aPort); |
| } |
| |
| @Override |
| protected void connect(InetAddress anAddr, int aPort) throws IOException { |
| connect(anAddr, aPort, 0); |
| } |
| |
| /** |
| * Connects this socket to the specified remote host address/port. |
| * |
| * @param anAddr |
| * the remote host address to connect to |
| * @param aPort |
| * the remote port to connect to |
| * @param timeout |
| * a timeout where supported. 0 means no timeout |
| * @throws IOException |
| * if an error occurs while connecting |
| */ |
| private void connect(InetAddress anAddr, int aPort, int timeout) throws IOException { |
| InetAddress normalAddr = anAddr.isAnyLocalAddress() ? InetAddress.getLocalHost() : anAddr; |
| if (streaming && usingSocks()) { |
| socksConnect(anAddr, aPort, 0); |
| } else { |
| IoBridge.connect(fd, normalAddr, aPort, timeout); |
| } |
| super.address = normalAddr; |
| super.port = aPort; |
| } |
| |
| @Override |
| protected void create(boolean streaming) throws IOException { |
| this.streaming = streaming; |
| this.fd = IoBridge.socket(streaming); |
| } |
| |
| @Override protected void finalize() throws Throwable { |
| try { |
| if (guard != null) { |
| guard.warnIfOpen(); |
| } |
| close(); |
| } finally { |
| super.finalize(); |
| } |
| } |
| |
| @Override protected synchronized InputStream getInputStream() throws IOException { |
| checkNotClosed(); |
| return new PlainSocketInputStream(this); |
| } |
| |
| private static class PlainSocketInputStream extends InputStream { |
| private final PlainSocketImpl socketImpl; |
| |
| public PlainSocketInputStream(PlainSocketImpl socketImpl) { |
| this.socketImpl = socketImpl; |
| } |
| |
| @Override public int available() throws IOException { |
| return socketImpl.available(); |
| } |
| |
| @Override public void close() throws IOException { |
| socketImpl.close(); |
| } |
| |
| @Override public int read() throws IOException { |
| return Streams.readSingleByte(this); |
| } |
| |
| @Override public int read(byte[] buffer, int offset, int byteCount) throws IOException { |
| return socketImpl.read(buffer, offset, byteCount); |
| } |
| } |
| |
| @Override public Object getOption(int option) throws SocketException { |
| return IoBridge.getSocketOption(fd, option); |
| } |
| |
| @Override protected synchronized OutputStream getOutputStream() throws IOException { |
| checkNotClosed(); |
| return new PlainSocketOutputStream(this); |
| } |
| |
| private static class PlainSocketOutputStream extends OutputStream { |
| private final PlainSocketImpl socketImpl; |
| |
| public PlainSocketOutputStream(PlainSocketImpl socketImpl) { |
| this.socketImpl = socketImpl; |
| } |
| |
| @Override public void close() throws IOException { |
| socketImpl.close(); |
| } |
| |
| @Override public void write(int oneByte) throws IOException { |
| Streams.writeSingleByte(this, oneByte); |
| } |
| |
| @Override public void write(byte[] buffer, int offset, int byteCount) throws IOException { |
| socketImpl.write(buffer, offset, byteCount); |
| } |
| } |
| |
| @Override |
| protected void listen(int backlog) throws IOException { |
| if (usingSocks()) { |
| // Do nothing for a SOCKS connection. The listen occurs on the |
| // server during the bind. |
| return; |
| } |
| try { |
| Libcore.os.listen(fd, backlog); |
| } catch (ErrnoException errnoException) { |
| throw errnoException.rethrowAsSocketException(); |
| } |
| } |
| |
| @Override |
| public void setOption(int option, Object value) throws SocketException { |
| IoBridge.setSocketOption(fd, option, value); |
| } |
| |
| /** |
| * Gets the SOCKS proxy server port. |
| */ |
| private int socksGetServerPort() { |
| // get socks server port from proxy. It is unnecessary to check |
| // "socksProxyPort" property, since proxy setting should only be |
| // determined by ProxySelector. |
| InetSocketAddress addr = (InetSocketAddress) proxy.address(); |
| return addr.getPort(); |
| } |
| |
| /** |
| * Gets the InetAddress of the SOCKS proxy server. |
| */ |
| private InetAddress socksGetServerAddress() throws UnknownHostException { |
| String proxyName; |
| // get socks server address from proxy. It is unnecessary to check |
| // "socksProxyHost" property, since all proxy setting should be |
| // determined by ProxySelector. |
| InetSocketAddress addr = (InetSocketAddress) proxy.address(); |
| proxyName = addr.getHostName(); |
| if (proxyName == null) { |
| proxyName = addr.getAddress().getHostAddress(); |
| } |
| return InetAddress.getByName(proxyName); |
| } |
| |
| /** |
| * Connect using a SOCKS server. |
| */ |
| private void socksConnect(InetAddress applicationServerAddress, int applicationServerPort, int timeout) throws IOException { |
| try { |
| IoBridge.connect(fd, socksGetServerAddress(), socksGetServerPort(), timeout); |
| } catch (Exception e) { |
| throw new SocketException("SOCKS connection failed", e); |
| } |
| |
| socksRequestConnection(applicationServerAddress, applicationServerPort); |
| |
| lastConnectedAddress = applicationServerAddress; |
| lastConnectedPort = applicationServerPort; |
| } |
| |
| /** |
| * Request a SOCKS connection to the application server given. If the |
| * request fails to complete successfully, an exception is thrown. |
| */ |
| private void socksRequestConnection(InetAddress applicationServerAddress, |
| int applicationServerPort) throws IOException { |
| socksSendRequest(Socks4Message.COMMAND_CONNECT, |
| applicationServerAddress, applicationServerPort); |
| Socks4Message reply = socksReadReply(); |
| if (reply.getCommandOrResult() != Socks4Message.RETURN_SUCCESS) { |
| throw new IOException(reply.getErrorString(reply.getCommandOrResult())); |
| } |
| } |
| |
| /** |
| * Perform an accept for a SOCKS bind. |
| */ |
| public void socksAccept() throws IOException { |
| Socks4Message reply = socksReadReply(); |
| if (reply.getCommandOrResult() != Socks4Message.RETURN_SUCCESS) { |
| throw new IOException(reply.getErrorString(reply.getCommandOrResult())); |
| } |
| } |
| |
| /** |
| * Shutdown the input portion of the socket. |
| */ |
| @Override |
| protected void shutdownInput() throws IOException { |
| shutdownInput = true; |
| try { |
| Libcore.os.shutdown(fd, SHUT_RD); |
| } catch (ErrnoException errnoException) { |
| throw errnoException.rethrowAsSocketException(); |
| } |
| } |
| |
| /** |
| * Shutdown the output portion of the socket. |
| */ |
| @Override |
| protected void shutdownOutput() throws IOException { |
| try { |
| Libcore.os.shutdown(fd, SHUT_WR); |
| } catch (ErrnoException errnoException) { |
| throw errnoException.rethrowAsSocketException(); |
| } |
| } |
| |
| /** |
| * Bind using a SOCKS server. |
| */ |
| private void socksBind() throws IOException { |
| try { |
| IoBridge.connect(fd, socksGetServerAddress(), socksGetServerPort()); |
| } catch (Exception e) { |
| throw new IOException("Unable to connect to SOCKS server", e); |
| } |
| |
| // There must be a connection to an application host for the bind to work. |
| if (lastConnectedAddress == null) { |
| throw new SocketException("Invalid SOCKS client"); |
| } |
| |
| // Use the last connected address and port in the bind request. |
| socksSendRequest(Socks4Message.COMMAND_BIND, lastConnectedAddress, |
| lastConnectedPort); |
| Socks4Message reply = socksReadReply(); |
| |
| if (reply.getCommandOrResult() != Socks4Message.RETURN_SUCCESS) { |
| throw new IOException(reply.getErrorString(reply.getCommandOrResult())); |
| } |
| |
| // A peculiarity of socks 4 - if the address returned is 0, use the |
| // original socks server address. |
| if (reply.getIP() == 0) { |
| address = socksGetServerAddress(); |
| } else { |
| // IPv6 support not yet required as |
| // currently the Socks4Message.getIP() only returns int, |
| // so only works with IPv4 4byte addresses |
| byte[] replyBytes = new byte[4]; |
| Memory.pokeInt(replyBytes, 0, reply.getIP(), ByteOrder.BIG_ENDIAN); |
| address = InetAddress.getByAddress(replyBytes); |
| } |
| localport = reply.getPort(); |
| } |
| |
| /** |
| * Send a SOCKS V4 request. |
| */ |
| private void socksSendRequest(int command, InetAddress address, int port) throws IOException { |
| Socks4Message request = new Socks4Message(); |
| request.setCommandOrResult(command); |
| request.setPort(port); |
| request.setIP(address.getAddress()); |
| request.setUserId("default"); |
| |
| getOutputStream().write(request.getBytes(), 0, request.getLength()); |
| } |
| |
| /** |
| * Read a SOCKS V4 reply. |
| */ |
| private Socks4Message socksReadReply() throws IOException { |
| Socks4Message reply = new Socks4Message(); |
| int bytesRead = 0; |
| while (bytesRead < Socks4Message.REPLY_LENGTH) { |
| int count = getInputStream().read(reply.getBytes(), bytesRead, |
| Socks4Message.REPLY_LENGTH - bytesRead); |
| if (count == -1) { |
| break; |
| } |
| bytesRead += count; |
| } |
| if (Socks4Message.REPLY_LENGTH != bytesRead) { |
| throw new SocketException("Malformed reply from SOCKS server"); |
| } |
| return reply; |
| } |
| |
| @Override |
| protected void connect(SocketAddress remoteAddr, int timeout) throws IOException { |
| InetSocketAddress inetAddr = (InetSocketAddress) remoteAddr; |
| connect(inetAddr.getAddress(), inetAddr.getPort(), timeout); |
| } |
| |
| @Override |
| protected boolean supportsUrgentData() { |
| return true; |
| } |
| |
| @Override |
| protected void sendUrgentData(int value) throws IOException { |
| try { |
| byte[] buffer = new byte[] { (byte) value }; |
| Libcore.os.sendto(fd, buffer, 0, 1, MSG_OOB, null, 0); |
| } catch (ErrnoException errnoException) { |
| throw errnoException.rethrowAsSocketException(); |
| } |
| } |
| |
| /** |
| * For PlainSocketInputStream. |
| */ |
| private int read(byte[] buffer, int offset, int byteCount) throws IOException { |
| if (byteCount == 0) { |
| return 0; |
| } |
| Arrays.checkOffsetAndCount(buffer.length, offset, byteCount); |
| if (shutdownInput) { |
| return -1; |
| } |
| int readCount = IoBridge.recvfrom(true, fd, buffer, offset, byteCount, 0, null, false); |
| // Return of zero bytes for a blocking socket means a timeout occurred |
| if (readCount == 0) { |
| throw new SocketTimeoutException(); |
| } |
| // Return of -1 indicates the peer was closed |
| if (readCount == -1) { |
| shutdownInput = true; |
| } |
| return readCount; |
| } |
| |
| /** |
| * For PlainSocketOutputStream. |
| */ |
| private void write(byte[] buffer, int offset, int byteCount) throws IOException { |
| Arrays.checkOffsetAndCount(buffer.length, offset, byteCount); |
| if (streaming) { |
| while (byteCount > 0) { |
| int bytesWritten = IoBridge.sendto(fd, buffer, offset, byteCount, 0, null, 0); |
| byteCount -= bytesWritten; |
| offset += bytesWritten; |
| } |
| } else { |
| // Unlike writes to a streaming socket, writes to a datagram |
| // socket are all-or-nothing, so we don't need a loop here. |
| // http://code.google.com/p/android/issues/detail?id=15304 |
| IoBridge.sendto(fd, buffer, offset, byteCount, 0, address, port); |
| } |
| } |
| } |