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