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