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