Home | History | Annotate | Download | only in net
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  * Copyright (c) 1995, 2011, Oracle and/or its affiliates. All rights reserved.
      4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      5  *
      6  * This code is free software; you can redistribute it and/or modify it
      7  * under the terms of the GNU General Public License version 2 only, as
      8  * published by the Free Software Foundation.  Oracle designates this
      9  * particular file as subject to the "Classpath" exception as provided
     10  * by Oracle in the LICENSE file that accompanied this code.
     11  *
     12  * This code is distributed in the hope that it will be useful, but WITHOUT
     13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     15  * version 2 for more details (a copy is included in the LICENSE file that
     16  * accompanied this code).
     17  *
     18  * You should have received a copy of the GNU General Public License version
     19  * 2 along with this work; if not, write to the Free Software Foundation,
     20  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
     21  *
     22  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
     23  * or visit www.oracle.com if you need additional information or have any
     24  * questions.
     25  */
     26 
     27 package java.net;
     28 
     29 import java.io.IOException;
     30 import java.io.InputStream;
     31 import java.io.OutputStream;
     32 import java.io.FileDescriptor;
     33 
     34 import dalvik.system.BlockGuard;
     35 import dalvik.system.CloseGuard;
     36 import sun.net.ConnectionResetException;
     37 import sun.net.NetHooks;
     38 import sun.net.ResourceManager;
     39 
     40 /**
     41  * Default Socket Implementation. This implementation does
     42  * not implement any security checks.
     43  * Note this class should <b>NOT</b> be public.
     44  *
     45  * @author  Steven B. Byrne
     46  */
     47 abstract class AbstractPlainSocketImpl extends SocketImpl
     48 {
     49     /* instance variable for SO_TIMEOUT */
     50     int timeout;   // timeout in millisec
     51     // traffic class
     52     private int trafficClass;
     53 
     54     private boolean shut_rd = false;
     55     private boolean shut_wr = false;
     56 
     57     private SocketInputStream socketInputStream = null;
     58 
     59     /* lock when accessing fd */
     60     protected final Object fdLock = new Object();
     61 
     62     /* indicates a close is pending on the file descriptor */
     63     protected boolean closePending = false;
     64 
     65     /* indicates connection reset state */
     66     private int CONNECTION_NOT_RESET = 0;
     67     private int CONNECTION_RESET_PENDING = 1;
     68     private int CONNECTION_RESET = 2;
     69     private int resetState;
     70     private final Object resetLock = new Object();
     71 
     72    /* whether this Socket is a stream (TCP) socket or not (UDP)
     73     */
     74     protected boolean stream;
     75 
     76     private final CloseGuard guard = CloseGuard.get();
     77 
     78     /**
     79      * Creates a socket with a boolean that specifies whether this
     80      * is a stream socket (true) or an unconnected UDP socket (false).
     81      */
     82     protected synchronized void create(boolean stream) throws IOException {
     83         this.stream = stream;
     84 
     85         if (!stream) {
     86             ResourceManager.beforeUdpCreate();
     87             try {
     88                 socketCreate(false);
     89             } catch (IOException ioe) {
     90                 ResourceManager.afterUdpClose();
     91                 throw ioe;
     92             }
     93         } else {
     94             socketCreate(true);
     95         }
     96         if (socket != null)
     97             socket.setCreated();
     98         if (serverSocket != null)
     99             serverSocket.setCreated();
    100 
    101         // socketCreate will set |fd| if it succeeds.
    102         if (fd != null && fd.valid()) {
    103             guard.open("close");
    104         }
    105     }
    106 
    107     /**
    108      * Creates a socket and connects it to the specified port on
    109      * the specified host.
    110      * @param host the specified host
    111      * @param port the specified port
    112      */
    113     protected void connect(String host, int port)
    114         throws UnknownHostException, IOException
    115     {
    116         boolean connected = false;
    117         try {
    118             InetAddress address = InetAddress.getByName(host);
    119             this.port = port;
    120             this.address = address;
    121 
    122             connectToAddress(address, port, timeout);
    123             connected = true;
    124         } finally {
    125             if (!connected) {
    126                 try {
    127                     close();
    128                 } catch (IOException ioe) {
    129                     /* Do nothing. If connect threw an exception then
    130                        it will be passed up the call stack */
    131                 }
    132             }
    133         }
    134     }
    135 
    136     /**
    137      * Creates a socket and connects it to the specified address on
    138      * the specified port.
    139      * @param address the address
    140      * @param port the specified port
    141      */
    142     protected void connect(InetAddress address, int port) throws IOException {
    143         this.port = port;
    144         this.address = address;
    145 
    146         try {
    147             connectToAddress(address, port, timeout);
    148             return;
    149         } catch (IOException e) {
    150             // everything failed
    151             close();
    152             throw e;
    153         }
    154     }
    155 
    156     /**
    157      * Creates a socket and connects it to the specified address on
    158      * the specified port.
    159      * @param address the address
    160      * @param timeout the timeout value in milliseconds, or zero for no timeout.
    161      * @throws IOException if connection fails
    162      * @throws  IllegalArgumentException if address is null or is a
    163      *          SocketAddress subclass not supported by this socket
    164      * @since 1.4
    165      */
    166     protected void connect(SocketAddress address, int timeout)
    167             throws IOException {
    168         boolean connected = false;
    169         try {
    170             if (address == null || !(address instanceof InetSocketAddress))
    171                 throw new IllegalArgumentException("unsupported address type");
    172             InetSocketAddress addr = (InetSocketAddress) address;
    173             if (addr.isUnresolved())
    174                 throw new UnknownHostException(addr.getHostName());
    175             this.port = addr.getPort();
    176             this.address = addr.getAddress();
    177 
    178             connectToAddress(this.address, port, timeout);
    179             connected = true;
    180         } finally {
    181             if (!connected) {
    182                 try {
    183                     close();
    184                 } catch (IOException ioe) {
    185                     /* Do nothing. If connect threw an exception then
    186                        it will be passed up the call stack */
    187                 }
    188             }
    189         }
    190     }
    191 
    192     private void connectToAddress(InetAddress address, int port, int timeout) throws IOException {
    193         if (address.isAnyLocalAddress()) {
    194             doConnect(InetAddress.getLocalHost(), port, timeout);
    195         } else {
    196             doConnect(address, port, timeout);
    197         }
    198     }
    199 
    200     public void setOption(int opt, Object val) throws SocketException {
    201         if (isClosedOrPending()) {
    202             throw new SocketException("Socket Closed");
    203         }
    204         boolean on = true;
    205         switch (opt) {
    206             /* check type safety b4 going native.  These should never
    207              * fail, since only java.Socket* has access to
    208              * PlainSocketImpl.setOption().
    209              */
    210         case SO_LINGER:
    211             if (val == null || (!(val instanceof Integer) && !(val instanceof Boolean)))
    212                 throw new SocketException("Bad parameter for option");
    213             if (val instanceof Boolean) {
    214                 /* true only if disabling - enabling should be Integer */
    215                 on = false;
    216             }
    217             break;
    218         case SO_TIMEOUT:
    219             if (val == null || (!(val instanceof Integer)))
    220                 throw new SocketException("Bad parameter for SO_TIMEOUT");
    221             int tmp = ((Integer) val).intValue();
    222             if (tmp < 0)
    223                 throw new IllegalArgumentException("timeout < 0");
    224             timeout = tmp;
    225             break;
    226         case IP_TOS:
    227              if (val == null || !(val instanceof Integer)) {
    228                  throw new SocketException("bad argument for IP_TOS");
    229              }
    230              trafficClass = ((Integer)val).intValue();
    231              break;
    232         case SO_BINDADDR:
    233             throw new SocketException("Cannot re-bind socket");
    234         case TCP_NODELAY:
    235             if (val == null || !(val instanceof Boolean))
    236                 throw new SocketException("bad parameter for TCP_NODELAY");
    237             on = ((Boolean)val).booleanValue();
    238             break;
    239         case SO_SNDBUF:
    240         case SO_RCVBUF:
    241             if (val == null || !(val instanceof Integer) ||
    242                 !(((Integer)val).intValue() > 0)) {
    243                 throw new SocketException("bad parameter for SO_SNDBUF " +
    244                                           "or SO_RCVBUF");
    245             }
    246             break;
    247         case SO_KEEPALIVE:
    248             if (val == null || !(val instanceof Boolean))
    249                 throw new SocketException("bad parameter for SO_KEEPALIVE");
    250             on = ((Boolean)val).booleanValue();
    251             break;
    252         case SO_OOBINLINE:
    253             if (val == null || !(val instanceof Boolean))
    254                 throw new SocketException("bad parameter for SO_OOBINLINE");
    255             on = ((Boolean)val).booleanValue();
    256             break;
    257         case SO_REUSEADDR:
    258             if (val == null || !(val instanceof Boolean))
    259                 throw new SocketException("bad parameter for SO_REUSEADDR");
    260             on = ((Boolean)val).booleanValue();
    261             break;
    262         default:
    263             throw new SocketException("unrecognized TCP option: " + opt);
    264         }
    265         socketSetOption(opt, on, val);
    266     }
    267     public Object getOption(int opt) throws SocketException {
    268         if (isClosedOrPending()) {
    269             throw new SocketException("Socket Closed");
    270         }
    271         if (opt == SO_TIMEOUT) {
    272             return new Integer(timeout);
    273         }
    274         int ret = 0;
    275         /*
    276          * The native socketGetOption() knows about 3 options.
    277          * The 32 bit value it returns will be interpreted according
    278          * to what we're asking.  A return of -1 means it understands
    279          * the option but its turned off.  It will raise a SocketException
    280          * if "opt" isn't one it understands.
    281          */
    282 
    283         switch (opt) {
    284         case TCP_NODELAY:
    285             ret = socketGetOption(opt, null);
    286             return Boolean.valueOf(ret != -1);
    287         case SO_OOBINLINE:
    288             ret = socketGetOption(opt, null);
    289             return Boolean.valueOf(ret != -1);
    290         case SO_LINGER:
    291             ret = socketGetOption(opt, null);
    292             return (ret == -1) ? Boolean.FALSE: (Object)(new Integer(ret));
    293         case SO_REUSEADDR:
    294             ret = socketGetOption(opt, null);
    295             return Boolean.valueOf(ret != -1);
    296         case SO_BINDADDR:
    297             InetAddressContainer in = new InetAddressContainer();
    298             ret = socketGetOption(opt, in);
    299             return in.addr;
    300         case SO_SNDBUF:
    301         case SO_RCVBUF:
    302             ret = socketGetOption(opt, null);
    303             return new Integer(ret);
    304         case IP_TOS:
    305             ret = socketGetOption(opt, null);
    306             if (ret == -1) { // ipv6 tos
    307                 return new Integer(trafficClass);
    308             } else {
    309                 return new Integer(ret);
    310             }
    311         case SO_KEEPALIVE:
    312             ret = socketGetOption(opt, null);
    313             return Boolean.valueOf(ret != -1);
    314         // should never get here
    315         default:
    316             return null;
    317         }
    318     }
    319 
    320     /**
    321      * The workhorse of the connection operation.  Tries several times to
    322      * establish a connection to the given <host, port>.  If unsuccessful,
    323      * throws an IOException indicating what went wrong.
    324      */
    325 
    326     synchronized void doConnect(InetAddress address, int port, int timeout) throws IOException {
    327         synchronized (fdLock) {
    328             if (!closePending && (socket == null || !socket.isBound())) {
    329                 NetHooks.beforeTcpConnect(fd, address, port);
    330             }
    331         }
    332         try {
    333             BlockGuard.getThreadPolicy().onNetwork();
    334             socketConnect(address, port, timeout);
    335             /* socket may have been closed during poll/select */
    336             synchronized (fdLock) {
    337                 if (closePending) {
    338                     throw new SocketException ("Socket closed");
    339                 }
    340             }
    341             // If we have a ref. to the Socket, then sets the flags
    342             // created, bound & connected to true.
    343             // This is normally done in Socket.connect() but some
    344             // subclasses of Socket may call impl.connect() directly!
    345             if (socket != null) {
    346                 socket.setBound();
    347                 socket.setConnected();
    348             }
    349         } catch (IOException e) {
    350             close();
    351             throw e;
    352         }
    353     }
    354 
    355     /**
    356      * Binds the socket to the specified address of the specified local port.
    357      * @param address the address
    358      * @param lport the port
    359      */
    360     protected synchronized void bind(InetAddress address, int lport)
    361         throws IOException
    362     {
    363        synchronized (fdLock) {
    364             if (!closePending && (socket == null || !socket.isBound())) {
    365                 NetHooks.beforeTcpBind(fd, address, lport);
    366             }
    367         }
    368         socketBind(address, lport);
    369         if (socket != null)
    370             socket.setBound();
    371         if (serverSocket != null)
    372             serverSocket.setBound();
    373     }
    374 
    375     /**
    376      * Listens, for a specified amount of time, for connections.
    377      * @param count the amount of time to listen for connections
    378      */
    379     protected synchronized void listen(int count) throws IOException {
    380         socketListen(count);
    381     }
    382 
    383     /**
    384      * Accepts connections.
    385      * @param s the connection
    386      */
    387     protected void accept(SocketImpl s) throws IOException {
    388         BlockGuard.getThreadPolicy().onNetwork();
    389         socketAccept(s);
    390     }
    391 
    392     /**
    393      * Gets an InputStream for this socket.
    394      */
    395     protected synchronized InputStream getInputStream() throws IOException {
    396         if (isClosedOrPending()) {
    397             throw new IOException("Socket Closed");
    398         }
    399         if (shut_rd) {
    400             throw new IOException("Socket input is shutdown");
    401         }
    402         if (socketInputStream == null) {
    403             socketInputStream = new SocketInputStream(this);
    404         }
    405         return socketInputStream;
    406     }
    407 
    408     void setInputStream(SocketInputStream in) {
    409         socketInputStream = in;
    410     }
    411 
    412     /**
    413      * Gets an OutputStream for this socket.
    414      */
    415     protected synchronized OutputStream getOutputStream() throws IOException {
    416         if (isClosedOrPending()) {
    417             throw new IOException("Socket Closed");
    418         }
    419         if (shut_wr) {
    420             throw new IOException("Socket output is shutdown");
    421         }
    422         return new SocketOutputStream(this);
    423     }
    424 
    425     void setFileDescriptor(FileDescriptor fd) {
    426         this.fd = fd;
    427     }
    428 
    429     void setAddress(InetAddress address) {
    430         this.address = address;
    431     }
    432 
    433     void setPort(int port) {
    434         this.port = port;
    435     }
    436 
    437     void setLocalPort(int localport) {
    438         this.localport = localport;
    439     }
    440 
    441     /**
    442      * Returns the number of bytes that can be read without blocking.
    443      */
    444     protected synchronized int available() throws IOException {
    445         if (isClosedOrPending()) {
    446             throw new IOException("Stream closed.");
    447         }
    448 
    449         /*
    450          * If connection has been reset then return 0 to indicate
    451          * there are no buffered bytes.
    452          */
    453         if (isConnectionReset()) {
    454             return 0;
    455         }
    456 
    457         /*
    458          * If no bytes available and we were previously notified
    459          * of a connection reset then we move to the reset state.
    460          *
    461          * If are notified of a connection reset then check
    462          * again if there are bytes buffered on the socket.
    463          */
    464         int n = 0;
    465         try {
    466             n = socketAvailable();
    467             if (n == 0 && isConnectionResetPending()) {
    468                 setConnectionReset();
    469             }
    470         } catch (ConnectionResetException exc1) {
    471             setConnectionResetPending();
    472             try {
    473                 n = socketAvailable();
    474                 if (n == 0) {
    475                     setConnectionReset();
    476                 }
    477             } catch (ConnectionResetException exc2) {
    478             }
    479         }
    480         return n;
    481     }
    482 
    483     /**
    484      * Closes the socket.
    485      */
    486     protected void close() throws IOException {
    487         synchronized(fdLock) {
    488             if (fd != null && fd.valid()) {
    489                 if (!stream) {
    490                     ResourceManager.afterUdpClose();
    491                 }
    492                 if (closePending) {
    493                   return;
    494                 }
    495                 closePending = true;
    496                 socketClose();
    497                 return;
    498             }
    499         }
    500     }
    501 
    502     void reset() throws IOException {
    503         if (fd != null && fd.valid()) {
    504             socketClose();
    505         }
    506         super.reset();
    507     }
    508 
    509 
    510     /**
    511      * Shutdown read-half of the socket connection;
    512      */
    513     protected void shutdownInput() throws IOException {
    514       if (fd != null && fd.valid()) {
    515           socketShutdown(SHUT_RD);
    516           if (socketInputStream != null) {
    517               socketInputStream.setEOF(true);
    518           }
    519           shut_rd = true;
    520       }
    521     }
    522 
    523     /**
    524      * Shutdown write-half of the socket connection;
    525      */
    526     protected void shutdownOutput() throws IOException {
    527       if (fd != null && fd.valid()) {
    528           socketShutdown(SHUT_WR);
    529           shut_wr = true;
    530       }
    531     }
    532 
    533     protected boolean supportsUrgentData () {
    534         return true;
    535     }
    536 
    537     protected void sendUrgentData (int data) throws IOException {
    538         if (fd == null || !fd.valid()) {
    539             throw new IOException("Socket Closed");
    540         }
    541         socketSendUrgentData (data);
    542     }
    543 
    544     /**
    545      * Cleans up if the user forgets to close it.
    546      */
    547     protected void finalize() throws IOException {
    548         if (guard != null) {
    549             guard.warnIfOpen();
    550         }
    551 
    552         close();
    553     }
    554 
    555     /*
    556      * "Acquires" and returns the FileDescriptor for this impl
    557      *
    558      * A corresponding releaseFD is required to "release" the
    559      * FileDescriptor.
    560      */
    561     FileDescriptor acquireFD() {
    562         synchronized (fdLock) {
    563             return fd;
    564         }
    565     }
    566 
    567     public boolean isConnectionReset() {
    568         synchronized (resetLock) {
    569             return (resetState == CONNECTION_RESET);
    570         }
    571     }
    572 
    573     public boolean isConnectionResetPending() {
    574         synchronized (resetLock) {
    575             return (resetState == CONNECTION_RESET_PENDING);
    576         }
    577     }
    578 
    579     public void setConnectionReset() {
    580         synchronized (resetLock) {
    581             resetState = CONNECTION_RESET;
    582         }
    583     }
    584 
    585     public void setConnectionResetPending() {
    586         synchronized (resetLock) {
    587             if (resetState == CONNECTION_NOT_RESET) {
    588                 resetState = CONNECTION_RESET_PENDING;
    589             }
    590         }
    591     }
    592 
    593     /*
    594      * Return true if already closed or close is pending
    595      */
    596     public boolean isClosedOrPending() {
    597         /*
    598          * Lock on fdLock to ensure that we wait if a
    599          * close is in progress.
    600          */
    601         synchronized (fdLock) {
    602             if (closePending || (fd == null) || !fd.valid()) {
    603                 return true;
    604             } else {
    605                 return false;
    606             }
    607         }
    608     }
    609 
    610     /*
    611      * Return the current value of SO_TIMEOUT
    612      */
    613     public int getTimeout() {
    614         return timeout;
    615     }
    616 
    617     /*
    618      * Close the socket (and release the file descriptor).
    619      */
    620     protected void socketClose() throws IOException {
    621         guard.close();
    622 
    623         socketClose0();
    624     }
    625 
    626     abstract void socketCreate(boolean isServer) throws IOException;
    627     abstract void socketConnect(InetAddress address, int port, int timeout)
    628         throws IOException;
    629     abstract void socketBind(InetAddress address, int port)
    630         throws IOException;
    631     abstract void socketListen(int count)
    632         throws IOException;
    633     abstract void socketAccept(SocketImpl s)
    634         throws IOException;
    635     abstract int socketAvailable()
    636         throws IOException;
    637     abstract void socketClose0()
    638         throws IOException;
    639     abstract void socketShutdown(int howto)
    640         throws IOException;
    641     abstract void socketSetOption(int cmd, boolean on, Object value)
    642         throws SocketException;
    643     abstract int socketGetOption(int opt, Object iaContainerObj) throws SocketException;
    644     abstract void socketSendUrgentData(int data)
    645         throws IOException;
    646 
    647     public final static int SHUT_RD = 0;
    648     public final static int SHUT_WR = 1;
    649 }
    650 
    651 class InetAddressContainer {
    652     InetAddress addr;
    653 }
    654