1 /*
   2  * Copyright (c) 2007, 2018, 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.FileDescriptor;
  29 import sun.net.ResourceManager;
  30 import jdk.internal.misc.SharedSecrets;
  31 import jdk.internal.misc.JavaIOFileDescriptorAccess;
  32 
  33 /*
  34  * This class defines the plain SocketImpl that is used when
  35  * the System property java.net.preferIPv4Stack is set to true.
  36  *
  37  * @author Chris Hegarty
  38  */
  39 
  40 class TwoStacksPlainSocketImpl extends AbstractPlainSocketImpl {
  41 
  42     private static final JavaIOFileDescriptorAccess fdAccess =
  43         SharedSecrets.getJavaIOFileDescriptorAccess();
  44 
  45     // true if this socket is exclusively bound
  46     private final boolean exclusiveBind;
  47 
  48     // emulates SO_REUSEADDR when exclusiveBind is true
  49     private boolean isReuseAddress;
  50 
  51     public TwoStacksPlainSocketImpl(boolean exclBind) {
  52         exclusiveBind = exclBind;
  53     }
  54 
  55     public TwoStacksPlainSocketImpl(FileDescriptor fd, boolean exclBind) {
  56         this.fd = fd;
  57         exclusiveBind = exclBind;
  58     }
  59 
  60     void socketCreate(boolean stream) throws IOException {
  61         if (fd == null)
  62             throw new SocketException("Socket closed");
  63 
  64         int newfd = socket0(stream, false /*v6 Only*/);
  65 
  66         fdAccess.set(fd, newfd);
  67     }
  68 
  69     @Override
  70     void socketConnect(InetAddress address, int port, int timeout)
  71         throws IOException {
  72         int nativefd = checkAndReturnNativeFD();
  73 
  74         if (address == null)
  75             throw new NullPointerException("inet address argument is null.");
  76 
  77         int connectResult;
  78         if (timeout <= 0) {
  79             connectResult = connect0(nativefd, address, port);
  80         } else {
  81             configureBlocking(nativefd, false);
  82             try {
  83                 connectResult = connect0(nativefd, address, port);
  84                 if (connectResult == WOULDBLOCK) {
  85                     waitForConnect(nativefd, timeout);
  86                 }
  87             } finally {
  88                 configureBlocking(nativefd, true);
  89             }
  90         }
  91         /*
  92          * We need to set the local port field. If bind was called
  93          * previous to the connect (by the client) then localport field
  94          * will already be set.
  95          */
  96         if (localport == 0)
  97             localport = localPort0(nativefd);
  98     }
  99 
 100     @Override
 101     void socketBind(InetAddress address, int port) throws IOException {
 102         int nativefd = checkAndReturnNativeFD();
 103 
 104         if (address == null)
 105             throw new NullPointerException("inet address argument is null.");
 106 
 107         bind0(nativefd, address, port, exclusiveBind);
 108         if (port == 0) {
 109             localport = localPort0(nativefd);
 110         } else {
 111             localport = port;
 112         }
 113 
 114         this.address = address;
 115     }
 116 
 117     @Override
 118     void socketListen(int backlog) throws IOException {
 119         int nativefd = checkAndReturnNativeFD();
 120 
 121         listen0(nativefd, backlog);
 122     }
 123 
 124     @Override
 125     void socketAccept(SocketImpl s) throws IOException {
 126         int nativefd = checkAndReturnNativeFD();
 127 
 128         if (s == null)
 129             throw new NullPointerException("socket is null");
 130 
 131         int newfd = -1;
 132         InetSocketAddress[] isaa = new InetSocketAddress[1];
 133         if (timeout <= 0) {
 134             newfd = accept0(nativefd, isaa);
 135         } else {
 136             configureBlocking(nativefd, false);
 137             try {
 138                 waitForNewConnection(nativefd, timeout);
 139                 newfd = accept0(nativefd, isaa);
 140                 if (newfd != -1) {
 141                     configureBlocking(newfd, true);
 142                 }
 143             } finally {
 144                 configureBlocking(nativefd, true);
 145             }
 146         }
 147         /* Update (SocketImpl)s' fd */
 148         fdAccess.set(s.fd, newfd);
 149         /* Update socketImpls remote port, address and localport */
 150         InetSocketAddress isa = isaa[0];
 151         s.port = isa.getPort();
 152         s.address = isa.getAddress();
 153         s.localport = localport;
 154     }
 155 
 156     @Override
 157     int socketAvailable() throws IOException {
 158         int nativefd = checkAndReturnNativeFD();
 159         return available0(nativefd);
 160     }
 161 
 162     @Override
 163     void socketClose0(boolean useDeferredClose/*unused*/) throws IOException {
 164         if (fd == null)
 165             throw new SocketException("Socket closed");
 166 
 167         if (!fd.valid())
 168             return;
 169 
 170         final int nativefd = fdAccess.get(fd);
 171         fdAccess.set(fd, -1);
 172         close0(nativefd);
 173     }
 174 
 175     @Override
 176     void socketShutdown(int howto) throws IOException {
 177         int nativefd = checkAndReturnNativeFD();
 178         shutdown0(nativefd, howto);
 179     }
 180 
 181     // Intentional fallthrough after SO_REUSEADDR
 182     @SuppressWarnings("fallthrough")
 183     @Override
 184     void socketSetOption(int opt, boolean on, Object value)
 185         throws SocketException {
 186         int nativefd = checkAndReturnNativeFD();
 187 
 188         if (opt == SO_TIMEOUT) {
 189             // Don't enable the socket option on ServerSocket as it's
 190             // meaningless (we don't receive on a ServerSocket).
 191             if (serverSocket == null) {
 192                 setSoTimeout0(nativefd, ((Integer)value).intValue());
 193             }
 194             return;
 195         }
 196         // SO_REUSEPORT is not supported on Windows.
 197         if (opt == SO_REUSEPORT) {
 198             throw new UnsupportedOperationException("unsupported option");
 199         }
 200 
 201         int optionValue = 0;
 202 
 203         switch(opt) {
 204             case SO_REUSEADDR :
 205                 if (exclusiveBind) {
 206                     // SO_REUSEADDR emulated when using exclusive bind
 207                     isReuseAddress = on;
 208                     return;
 209                 }
 210                 // intentional fallthrough
 211             case TCP_NODELAY :
 212             case SO_OOBINLINE :
 213             case SO_KEEPALIVE :
 214                 optionValue = on ? 1 : 0;
 215                 break;
 216             case SO_SNDBUF :
 217             case SO_RCVBUF :
 218             case IP_TOS :
 219                 optionValue = ((Integer)value).intValue();
 220                 break;
 221             case SO_LINGER :
 222                 if (on) {
 223                     optionValue =  ((Integer)value).intValue();
 224                 } else {
 225                     optionValue = -1;
 226                 }
 227                 break;
 228             default :/* shouldn't get here */
 229                 throw new SocketException("Option not supported");
 230         }
 231 
 232         setIntOption(nativefd, opt, optionValue);
 233     }
 234 
 235     @Override
 236     int socketGetOption(int opt, Object iaContainerObj) throws SocketException {
 237         int nativefd = checkAndReturnNativeFD();
 238 
 239         // SO_BINDADDR is not a socket option.
 240         if (opt == SO_BINDADDR) {
 241             localAddress(nativefd, (InetAddressContainer)iaContainerObj);
 242             return 0;  // return value doesn't matter.
 243         }
 244         // SO_REUSEPORT is not supported on Windows.
 245         if (opt == SO_REUSEPORT) {
 246             throw new UnsupportedOperationException("unsupported option");
 247         }
 248 
 249         // SO_REUSEADDR emulated when using exclusive bind
 250         if (opt == SO_REUSEADDR && exclusiveBind)
 251             return isReuseAddress? 1 : -1;
 252 
 253         int value = getIntOption(nativefd, opt);
 254 
 255         switch (opt) {
 256             case TCP_NODELAY :
 257             case SO_OOBINLINE :
 258             case SO_KEEPALIVE :
 259             case SO_REUSEADDR :
 260                 return (value == 0) ? -1 : 1;
 261         }
 262         return value;
 263     }
 264 
 265     @Override
 266     void socketSendUrgentData(int data) throws IOException {
 267         int nativefd = checkAndReturnNativeFD();
 268         sendOOB(nativefd, data);
 269     }
 270 
 271     private int checkAndReturnNativeFD() throws SocketException {
 272         if (fd == null || !fd.valid())
 273             throw new SocketException("Socket closed");
 274 
 275         return fdAccess.get(fd);
 276     }
 277 
 278     static final int WOULDBLOCK = -2;       // Nothing available (non-blocking)
 279 
 280     static {
 281         initIDs();
 282     }
 283 
 284     /* Native methods */
 285 
 286     static native void initIDs();
 287 
 288     static native int socket0(boolean stream, boolean v6Only) throws IOException;
 289 
 290     static native void bind0(int fd, InetAddress localAddress, int localport,
 291                              boolean exclBind)
 292         throws IOException;
 293 
 294     static native int connect0(int fd, InetAddress remote, int remotePort)
 295         throws IOException;
 296 
 297     static native void waitForConnect(int fd, int timeout) throws IOException;
 298 
 299     static native int localPort0(int fd) throws IOException;
 300 
 301     static native void localAddress(int fd, InetAddressContainer in) throws SocketException;
 302 
 303     static native void listen0(int fd, int backlog) throws IOException;
 304 
 305     static native int accept0(int fd, InetSocketAddress[] isaa) throws IOException;
 306 
 307     static native void waitForNewConnection(int fd, int timeout) throws IOException;
 308 
 309     static native int available0(int fd) throws IOException;
 310 
 311     static native void close0(int fd) throws IOException;
 312 
 313     static native void shutdown0(int fd, int howto) throws IOException;
 314 
 315     static native void setIntOption(int fd, int cmd, int optionValue) throws SocketException;
 316 
 317     static native void setSoTimeout0(int fd, int timeout) throws SocketException;
 318 
 319     static native int getIntOption(int fd, int cmd) throws SocketException;
 320 
 321     static native void sendOOB(int fd, int data) throws IOException;
 322 
 323     static native void configureBlocking(int fd, boolean blocking) throws IOException;
 324 }