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 jdk.internal.misc.SharedSecrets;
  30 import jdk.internal.misc.JavaIOFileDescriptorAccess;
  31 
  32 /**
  33  * This class defines the plain SocketImpl that is used on Windows platforms
  34  * greater or equal to Windows Vista. These platforms have a dual
  35  * layer TCP/IP stack and can handle both IPv4 and IPV6 through a
  36  * single file descriptor.
  37  *
  38  * @author Chris Hegarty
  39  */
  40 
  41 class DualStackPlainSocketImpl extends AbstractPlainSocketImpl {
  42 
  43     private static final JavaIOFileDescriptorAccess fdAccess =
  44         SharedSecrets.getJavaIOFileDescriptorAccess();
  45 
  46     // true if this socket is exclusively bound
  47     private final boolean exclusiveBind;
  48 
  49     // emulates SO_REUSEADDR when exclusiveBind is true
  50     private boolean isReuseAddress;
  51 
  52     public DualStackPlainSocketImpl(boolean exclBind) {
  53         exclusiveBind = exclBind;
  54     }
  55 
  56     public DualStackPlainSocketImpl(FileDescriptor fd, boolean exclBind) {
  57         this.fd = fd;
  58         exclusiveBind = exclBind;
  59     }
  60 
  61     void socketCreate(boolean stream) throws IOException {
  62         if (fd == null)
  63             throw new SocketException("Socket closed");
  64 
  65         int newfd = socket0(stream, false /*v6 Only*/);
  66 
  67         fdAccess.set(fd, newfd);
  68     }
  69 
  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     void socketBind(InetAddress address, int port) throws IOException {
 101         int nativefd = checkAndReturnNativeFD();
 102 
 103         if (address == null)
 104             throw new NullPointerException("inet address argument is null.");
 105 
 106         bind0(nativefd, address, port, exclusiveBind);
 107         if (port == 0) {
 108             localport = localPort0(nativefd);
 109         } else {
 110             localport = port;
 111         }
 112 
 113         this.address = address;
 114     }
 115 
 116     void socketListen(int backlog) throws IOException {
 117         int nativefd = checkAndReturnNativeFD();
 118 
 119         listen0(nativefd, backlog);
 120     }
 121 
 122     void socketAccept(SocketImpl s) throws IOException {
 123         int nativefd = checkAndReturnNativeFD();
 124 
 125         if (s == null)
 126             throw new NullPointerException("socket is null");
 127 
 128         int newfd = -1;
 129         InetSocketAddress[] isaa = new InetSocketAddress[1];
 130         if (timeout <= 0) {
 131             newfd = accept0(nativefd, isaa);
 132         } else {
 133             configureBlocking(nativefd, false);
 134             try {
 135                 waitForNewConnection(nativefd, timeout);
 136                 newfd = accept0(nativefd, isaa);
 137                 if (newfd != -1) {
 138                     configureBlocking(newfd, true);
 139                 }
 140             } finally {
 141                 configureBlocking(nativefd, true);
 142             }
 143         }
 144         /* Update (SocketImpl)s' fd */
 145         fdAccess.set(s.fd, newfd);
 146         /* Update socketImpls remote port, address and localport */
 147         InetSocketAddress isa = isaa[0];
 148         s.port = isa.getPort();
 149         s.address = isa.getAddress();
 150         s.localport = localport;
 151     }
 152 
 153     int socketAvailable() throws IOException {
 154         int nativefd = checkAndReturnNativeFD();
 155         return available0(nativefd);
 156     }
 157 
 158     void socketClose0(boolean useDeferredClose/*unused*/) throws IOException {
 159         if (fd == null)
 160             throw new SocketException("Socket closed");
 161 
 162         if (!fd.valid())
 163             return;
 164 
 165         final int nativefd = fdAccess.get(fd);
 166         fdAccess.set(fd, -1);
 167         close0(nativefd);
 168     }
 169 
 170     void socketShutdown(int howto) throws IOException {
 171         int nativefd = checkAndReturnNativeFD();
 172         shutdown0(nativefd, howto);
 173     }
 174 
 175     // Intentional fallthrough after SO_REUSEADDR
 176     @SuppressWarnings("fallthrough")
 177     void socketSetOption(int opt, boolean on, Object value)
 178         throws SocketException {
 179         int nativefd = checkAndReturnNativeFD();
 180 
 181         if (opt == SO_TIMEOUT) {  // timeout implemented through select.
 182             return;
 183         }
 184         // SO_REUSEPORT is not supported on Windows.
 185         if (opt == SO_REUSEPORT) {
 186             throw new UnsupportedOperationException("unsupported option");
 187         }
 188 
 189         int optionValue = 0;
 190 
 191         switch(opt) {
 192             case SO_REUSEADDR :
 193                 if (exclusiveBind) {
 194                     // SO_REUSEADDR emulated when using exclusive bind
 195                     isReuseAddress = on;
 196                     return;
 197                 }
 198                 // intentional fallthrough
 199             case TCP_NODELAY :
 200             case SO_OOBINLINE :
 201             case SO_KEEPALIVE :
 202                 optionValue = on ? 1 : 0;
 203                 break;
 204             case SO_SNDBUF :
 205             case SO_RCVBUF :
 206             case IP_TOS :
 207                 optionValue = ((Integer)value).intValue();
 208                 break;
 209             case SO_LINGER :
 210                 if (on) {
 211                     optionValue =  ((Integer)value).intValue();
 212                 } else {
 213                     optionValue = -1;
 214                 }
 215                 break;
 216             default :/* shouldn't get here */
 217                 throw new SocketException("Option not supported");
 218         }
 219 
 220         setIntOption(nativefd, opt, optionValue);
 221     }
 222 
 223     int socketGetOption(int opt, Object iaContainerObj) throws SocketException {
 224         int nativefd = checkAndReturnNativeFD();
 225 
 226         // SO_BINDADDR is not a socket option.
 227         if (opt == SO_BINDADDR) {
 228             localAddress(nativefd, (InetAddressContainer)iaContainerObj);
 229             return 0;  // return value doesn't matter.
 230         }
 231         // SO_REUSEPORT is not supported on Windows.
 232         if (opt == SO_REUSEPORT) {
 233             throw new UnsupportedOperationException("unsupported option");
 234         }
 235 
 236         // SO_REUSEADDR emulated when using exclusive bind
 237         if (opt == SO_REUSEADDR && exclusiveBind)
 238             return isReuseAddress? 1 : -1;
 239 
 240         int value = getIntOption(nativefd, opt);
 241 
 242         switch (opt) {
 243             case TCP_NODELAY :
 244             case SO_OOBINLINE :
 245             case SO_KEEPALIVE :
 246             case SO_REUSEADDR :
 247                 return (value == 0) ? -1 : 1;
 248         }
 249         return value;
 250     }
 251 
 252     void socketSendUrgentData(int data) throws IOException {
 253         int nativefd = checkAndReturnNativeFD();
 254         sendOOB(nativefd, data);
 255     }
 256 
 257     private int checkAndReturnNativeFD() throws SocketException {
 258         if (fd == null || !fd.valid())
 259             throw new SocketException("Socket closed");
 260 
 261         return fdAccess.get(fd);
 262     }
 263 
 264     static final int WOULDBLOCK = -2;       // Nothing available (non-blocking)
 265 
 266     static {
 267         initIDs();
 268     }
 269 
 270     /* Native methods */
 271 
 272     static native void initIDs();
 273 
 274     static native int socket0(boolean stream, boolean v6Only) throws IOException;
 275 
 276     static native void bind0(int fd, InetAddress localAddress, int localport,
 277                              boolean exclBind)
 278         throws IOException;
 279 
 280     static native int connect0(int fd, InetAddress remote, int remotePort)
 281         throws IOException;
 282 
 283     static native void waitForConnect(int fd, int timeout) throws IOException;
 284 
 285     static native int localPort0(int fd) throws IOException;
 286 
 287     static native void localAddress(int fd, InetAddressContainer in) throws SocketException;
 288 
 289     static native void listen0(int fd, int backlog) throws IOException;
 290 
 291     static native int accept0(int fd, InetSocketAddress[] isaa) throws IOException;
 292 
 293     static native void waitForNewConnection(int fd, int timeout) throws IOException;
 294 
 295     static native int available0(int fd) throws IOException;
 296 
 297     static native void close0(int fd) throws IOException;
 298 
 299     static native void shutdown0(int fd, int howto) throws IOException;
 300 
 301     static native void setIntOption(int fd, int cmd, int optionValue) throws SocketException;
 302 
 303     static native int getIntOption(int fd, int cmd) throws SocketException;
 304 
 305     static native void sendOOB(int fd, int data) throws IOException;
 306 
 307     static native void configureBlocking(int fd, boolean blocking) throws IOException;
 308 }