Home | History | Annotate | Download | only in net
      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