1 /*
   2  * Copyright (c) 2016, 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 
  26 package jdk.net;
  27 
  28 import java.net.*;
  29 import java.nio.channels.*;
  30 import java.io.IOException;
  31 import java.lang.reflect.Field;
  32 import java.util.Collections;
  33 import java.util.HashMap;
  34 import java.util.HashSet;
  35 import java.util.Map;
  36 import java.util.Set;
  37 import rdma.ch.RdmaPollSelectorProvider;
  38 import rdma.ch.RdmaSocketImpl;
  39 import jdk.net.ExtendedSocketOptions.PlatformSocketOptions;
  40 
  41 /**
  42  * Defines static methods to set and get socket options defined by the
  43  * {@link java.net.SocketOption} interface. All of the standard options defined
  44  * by {@link java.net.Socket}, {@link java.net.ServerSocket}, and
  45  * {@link java.net.DatagramSocket} can be set this way, as well as additional
  46  * or platform specific options supported by each socket type.
  47  * <p>
  48  * The {@link #supportedOptions(Class)} method can be called to determine
  49  * the complete set of options available (per socket type) on the
  50  * current system.
  51  * <p>
  52  * The {@link #openRdmaSelector() openRdmaSelector}, {@link
  53  * #openRdmaSocketChannel() openRdmaSocketChannel}, and {@link
  54  * #openRdmaServerSocketChannel() openRdmaServerSocketChannel} methods
  55  * create selectors and selectable channels for use with RDMA sockets.
  56  * These objects are created a {@link java.nio.channels.spi.SelectorProvider
  57  * SelectorProvider} that is not the default {@code SelectorProvider}.
  58  * Consequently, selectable channels to RDMA sockets may not be multiplexed
  59  * with selectable channel created by the default selector provider. The
  60  * selector provider does not support datagram channels, its {@code
  61  * openDatagramChannel} methods throw {@link UnsupportedOperationException}.
  62  * When a security manager is installed, some non-standard socket options
  63  * may require a security permission before being set or get.
  64  * The details are specified in {@link ExtendedSocketOptions}. No permission
  65  * is required for {@link java.net.StandardSocketOptions}.
  66  * <p>
  67  *
  68  * @see java.nio.channels.NetworkChannel
  69  */
  70 public class Sockets {
  71 
  72     private static final Map<Class<?>,Set<SocketOption<?>>>
  73             options = optionSets();
  74 
  75     private Sockets() {}
  76 
  77     /**
  78      * Sets the value of a socket option on a {@link java.net.Socket}
  79      *
  80      * @param s the socket
  81      * @param name The socket option
  82      * @param value The value of the socket option. May be null for some
  83      *              options.
  84      *
  85      * @throws UnsupportedOperationException if the socket does not support
  86      *         the option.
  87      *
  88      * @throws IllegalArgumentException if the value is not valid for
  89      *         the option.
  90      *
  91      * @throws IOException if an I/O error occurs, or socket is closed.
  92      *
  93      * @throws SecurityException if a security manager is set and the
  94      *         caller does not have any required permission.
  95      *
  96      * @throws NullPointerException if name is null
  97      *
  98      * @see java.net.StandardSocketOptions
  99      */
 100     public static <T> void setOption(Socket s, SocketOption<T> name, T value) throws IOException
 101     {
 102         s.setOption(name, value);
 103     }
 104 
 105     /**
 106      * Returns the value of a socket option from a {@link java.net.Socket}
 107      *
 108      * @param s the socket
 109      * @param name The socket option
 110      *
 111      * @return The value of the socket option.
 112      *
 113      * @throws UnsupportedOperationException if the socket does not support
 114      *         the option.
 115      *
 116      * @throws IOException if an I/O error occurs
 117      *
 118      * @throws SecurityException if a security manager is set and the
 119      *         caller does not have any required permission.
 120      *
 121      * @throws NullPointerException if name is null
 122      *
 123      * @see java.net.StandardSocketOptions
 124      */
 125     public static <T> T getOption(Socket s, SocketOption<T> name) throws IOException
 126     {
 127         return s.getOption(name);
 128     }
 129 
 130     /**
 131      * Sets the value of a socket option on a {@link java.net.ServerSocket}
 132      *
 133      * @param s the socket
 134      * @param name The socket option
 135      * @param value The value of the socket option.
 136      *
 137      * @throws UnsupportedOperationException if the socket does not support
 138      *         the option.
 139      *
 140      * @throws IllegalArgumentException if the value is not valid for
 141      *         the option.
 142      *
 143      * @throws IOException if an I/O error occurs
 144      *
 145      * @throws NullPointerException if name is null
 146      *
 147      * @throws SecurityException if a security manager is set and the
 148      *         caller does not have any required permission.
 149      *
 150      * @see java.net.StandardSocketOptions
 151      */
 152     public static <T> void setOption(ServerSocket s, SocketOption<T> name, T value) throws IOException
 153     {
 154         s.setOption(name, value);
 155     }
 156 
 157     /**
 158      * Returns the value of a socket option from a {@link java.net.ServerSocket}
 159      *
 160      * @param s the socket
 161      * @param name The socket option
 162      *
 163      * @return The value of the socket option.
 164      *
 165      * @throws UnsupportedOperationException if the socket does not support
 166      *         the option.
 167      *
 168      * @throws IOException if an I/O error occurs
 169      *
 170      * @throws NullPointerException if name is null
 171      *
 172      * @throws SecurityException if a security manager is set and the
 173      *         caller does not have any required permission.
 174      *
 175      * @see java.net.StandardSocketOptions
 176      */
 177     public static <T> T getOption(ServerSocket s, SocketOption<T> name) throws IOException
 178     {
 179         return s.getOption(name);
 180     }
 181 
 182     /**
 183      * Sets the value of a socket option on a {@link java.net.DatagramSocket}
 184      * or {@link java.net.MulticastSocket}
 185      *
 186      * @param s the socket
 187      * @param name The socket option
 188      * @param value The value of the socket option.
 189      *
 190      * @throws UnsupportedOperationException if the socket does not support
 191      *         the option.
 192      *
 193      * @throws IllegalArgumentException if the value is not valid for
 194      *         the option.
 195      *
 196      * @throws IOException if an I/O error occurs
 197      *
 198      * @throws NullPointerException if name is null
 199      *
 200      * @throws SecurityException if a security manager is set and the
 201      *         caller does not have any required permission.
 202      *
 203      * @see java.net.StandardSocketOptions
 204      */
 205     public static <T> void setOption(DatagramSocket s, SocketOption<T> name, T value) throws IOException
 206     {
 207         s.setOption(name, value);
 208     }
 209 
 210     /**
 211      * Returns the value of a socket option from a
 212      * {@link java.net.DatagramSocket} or {@link java.net.MulticastSocket}
 213      *
 214      * @param s the socket
 215      * @param name The socket option
 216      *
 217      * @return The value of the socket option.
 218      *
 219      * @throws UnsupportedOperationException if the socket does not support
 220      *         the option.
 221      *
 222      * @throws IOException if an I/O error occurs
 223      *
 224      * @throws NullPointerException if name is null
 225      *
 226      * @throws SecurityException if a security manager is set and the
 227      *         caller does not have any required permission.
 228      *
 229      * @see java.net.StandardSocketOptions
 230      */
 231     public static <T> T getOption(DatagramSocket s, SocketOption<T> name) throws IOException
 232     {
 233         return s.getOption(name);
 234     }
 235 
 236     /**
 237      * Returns a set of {@link java.net.SocketOption}s supported by the
 238      * given socket type. This set may include standard options and also
 239      * non standard extended options.
 240      *
 241      * @param socketType the type of java.net socket
 242      *
 243      * @throws IllegalArgumentException if socketType is not a valid
 244      *         socket type from the java.net package.
 245      */
 246     public static Set<SocketOption<?>> supportedOptions(Class<?> socketType) {
 247         Set<SocketOption<?>> set = options.get(socketType);
 248         if (set == null) {
 249             throw new IllegalArgumentException("unknown socket type");
 250         }
 251         return set;
 252     }
 253 
 254     private static void checkValueType(Object value, Class<?> type) {
 255         if (!type.isAssignableFrom(value.getClass())) {
 256             String s = "Found: " + value.getClass().toString() + " Expected: "
 257                         + type.toString();
 258             throw new IllegalArgumentException(s);
 259         }
 260     }
 261 
 262     private static volatile boolean checkedReusePort;
 263     private static volatile boolean isReusePortAvailable;
 264 
 265     /**
 266      * Tells whether SO_REUSEPORT is supported.
 267      */
 268     static boolean isReusePortAvailable() {
 269         if (!checkedReusePort) {
 270             Set<SocketOption<?>> s = new Socket().supportedOptions();
 271             isReusePortAvailable = s.contains(StandardSocketOptions.SO_REUSEPORT);
 272             checkedReusePort = true;
 273         }
 274         return isReusePortAvailable;
 275     }
 276 
 277     private static Map<Class<?>,Set<SocketOption<?>>> optionSets() {
 278         Map<Class<?>,Set<SocketOption<?>>> options = new HashMap<>();
 279         boolean flowsupported = PlatformSocketOptions.get().flowSupported();
 280         boolean reuseportsupported = isReusePortAvailable();
 281         // Socket
 282 
 283         Set<SocketOption<?>> set = new HashSet<>();
 284         set.add(StandardSocketOptions.SO_KEEPALIVE);
 285         set.add(StandardSocketOptions.SO_SNDBUF);
 286         set.add(StandardSocketOptions.SO_RCVBUF);
 287         set.add(StandardSocketOptions.SO_REUSEADDR);
 288         if (reuseportsupported) {
 289             set.add(StandardSocketOptions.SO_REUSEPORT);
 290         }
 291         set.add(StandardSocketOptions.SO_LINGER);
 292         set.add(StandardSocketOptions.IP_TOS);
 293         set.add(StandardSocketOptions.TCP_NODELAY);
 294         if (flowsupported) {
 295             set.add(ExtendedSocketOptions.SO_FLOW_SLA);
 296         }
 297         if (QuickAck.available) {
 298             set.add(ExtendedSocketOptions.TCP_QUICKACK);
 299         }
 300         if (KeepAliveOptions.AVAILABLE) {
 301             set.addAll(Set.of(ExtendedSocketOptions.TCP_KEEPCOUNT,
 302                     ExtendedSocketOptions.TCP_KEEPIDLE,
 303                     ExtendedSocketOptions.TCP_KEEPINTERVAL));
 304         }
 305         set = Collections.unmodifiableSet(set);
 306         options.put(Socket.class, set);
 307 
 308         // ServerSocket
 309 
 310         set = new HashSet<>();
 311         set.add(StandardSocketOptions.SO_RCVBUF);
 312         set.add(StandardSocketOptions.SO_REUSEADDR);
 313         if (reuseportsupported) {
 314             set.add(StandardSocketOptions.SO_REUSEPORT);
 315         }
 316         if (QuickAck.available) {
 317             set.add(ExtendedSocketOptions.TCP_QUICKACK);
 318         }
 319         if (KeepAliveOptions.AVAILABLE) {
 320             set.addAll(Set.of(ExtendedSocketOptions.TCP_KEEPCOUNT,
 321                     ExtendedSocketOptions.TCP_KEEPIDLE,
 322                     ExtendedSocketOptions.TCP_KEEPINTERVAL));
 323         }
 324         set.add(StandardSocketOptions.IP_TOS);
 325         set = Collections.unmodifiableSet(set);
 326         options.put(ServerSocket.class, set);
 327 
 328         // DatagramSocket
 329 
 330         set = new HashSet<>();
 331         set.add(StandardSocketOptions.SO_SNDBUF);
 332         set.add(StandardSocketOptions.SO_RCVBUF);
 333         set.add(StandardSocketOptions.SO_REUSEADDR);
 334         if (reuseportsupported) {
 335             set.add(StandardSocketOptions.SO_REUSEPORT);
 336         }
 337         set.add(StandardSocketOptions.IP_TOS);
 338         if (flowsupported) {
 339             set.add(ExtendedSocketOptions.SO_FLOW_SLA);
 340         }
 341         set = Collections.unmodifiableSet(set);
 342         options.put(DatagramSocket.class, set);
 343 
 344         // MulticastSocket
 345 
 346         set = new HashSet<>();
 347         set.add(StandardSocketOptions.SO_SNDBUF);
 348         set.add(StandardSocketOptions.SO_RCVBUF);
 349         set.add(StandardSocketOptions.SO_REUSEADDR);
 350         if (reuseportsupported) {
 351             set.add(StandardSocketOptions.SO_REUSEPORT);
 352         }
 353         set.add(StandardSocketOptions.IP_TOS);
 354         set.add(StandardSocketOptions.IP_MULTICAST_IF);
 355         set.add(StandardSocketOptions.IP_MULTICAST_TTL);
 356         set.add(StandardSocketOptions.IP_MULTICAST_LOOP);
 357         if (flowsupported) {
 358             set.add(ExtendedSocketOptions.SO_FLOW_SLA);
 359         }
 360         set = Collections.unmodifiableSet(set);
 361         options.put(MulticastSocket.class, set);
 362 
 363         return Collections.unmodifiableMap(options);
 364     }
 365 
 366     /**
 367      * Tells whether TCP_QUICKACK is supported.
 368      */
 369     static class QuickAck {
 370 
 371         static final boolean available;
 372 
 373         static {
 374             Set<SocketOption<?>> s = new Socket().supportedOptions();
 375             available = s.contains(ExtendedSocketOptions.TCP_QUICKACK);
 376         }
 377     }
 378 
 379     /**
 380      * Tells whether TCP_KEEPALIVE options are supported.
 381      */
 382     static class KeepAliveOptions {
 383 
 384         static final boolean AVAILABLE;
 385 
 386         static {
 387             Set<SocketOption<?>> s = new Socket().supportedOptions();
 388             AVAILABLE = s.containsAll(Set.of(ExtendedSocketOptions.TCP_KEEPCOUNT,
 389                                             ExtendedSocketOptions.TCP_KEEPIDLE,
 390                                             ExtendedSocketOptions.TCP_KEEPINTERVAL));
 391         }
 392     }
 393 
 394 
 395     /**
 396      * Creates an unconnected RDMA socket.
 397      *
 398      * <p> A RDMA socket supports the same socket options that that {@code
 399      * java.net.Socket} defines. In addition, it also supports the socket options
 400      * specified by {@link RdmaSocketOptions}.
 401      *
 402      * @apiNote The rsocket implementation on Linux only supports IPv4 addresses.
 403      * Consequently, attempting to bind or connect to an IPv6 address will fail
 404      * with {@code IllegalArgumentException}.
 405      *
 406      * @throws IOException
 407      *         If an I/O error occurs
 408      * @throws UnsupportedOperationException
 409      *         If RDMA sockets are not supported on this platform
 410      *
 411      * @since 12
 412      */
 413     public static Socket openRdmaSocket() throws IOException {
 414         RdmaSocketImpl impl = new RdmaSocketImpl();
 415         Socket s = new Socket(impl) {};
 416         return s;
 417     }
 418 
 419     /**
 420      * Creates an unbound RDMA server socket.
 421      *
 422      * <p> A RDMA socket supports the same socket options that that {@code
 423      * java.net.ServerSocket} defines.
 424      *
 425      * @apiNote The rsocket implementation on Linux only supports IPv4 addresses.
 426      * Consequently, attempting to bind to an IPv6 address will fail with
 427      * {@code IllegalArgumentException}.
 428      *
 429      * @throws IOException
 430      *         If an I/O error occurs
 431      * @throws UnsupportedOperationException
 432      *         If RDMA sockets are not supported on this platform
 433      *
 434      * @since N
 435      */
 436     public static ServerSocket openRdmaServerSocket() throws IOException {
 437         SocketImpl impl = new RdmaSocketImpl();
 438         ServerSocket ss = new ServerSocket(impl) {
 439             public Socket accept() throws IOException {
 440                 if (isClosed())
 441                 throw new SocketException("Socket is closed");
 442                 if (!isBound())
 443                     throw new SocketException("Socket is not bound yet");
 444 
 445                 Socket s = openRdmaSocket();
 446                 implAccept(s);
 447                 return s;
 448             }    
 449         };
 450         return ss;
 451     }
 452 
 453     /**
 454      * Opens a socket channel to a RDMA socket. A newly-created socket channel
 455      * is {@link SocketChannel#isOpen() open}, not yet bound to a {@link
 456      * SocketChannel#getLocalAddress() local address}, and not yet
 457      * {@link SocketChannel#isConnected() connected}.
 458      *
 459      * <p> A socket channel to a RDMA socket supports all of the socket options
 460      * specified by {@code SocketChannel}. In addition, it also supports the
 461      * socket options specified by {@link RdmaSocketOptions}.
 462      *
 463      * @apiNote The rsocket implementation on Linux only supports IPv4 addresses.
 464      * Consequently, attempting to bind or connect to an IPv6 address will fail
 465      * with {@code UnsupportedAddressTypeException}.
 466      *
 467      * @throws IOException
 468      *         If an I/O error occurs
 469      * @throws UnsupportedOperationException
 470      *         If RDMA sockets are not supported on this platform
 471      *
 472      * @since 12
 473      */
 474     public static SocketChannel openRdmaSocketChannel() throws IOException {
 475         return RdmaPollSelectorProvider.provider().openSocketChannel();
 476     }
 477 
 478     /**
 479      * Opens a server-socket channel to a RDMA socket. A newly-created socket
 480      * channel is {@link SocketChannel#isOpen() open} but not yet bound to a
 481      * {@link SocketChannel#getLocalAddress() local address}.
 482      *
 483      * @apiNote The rsocket implementation on Linux only supports IPv4 addresses.
 484      * Consequently, attempting to bind to an IPv6 address will fail with
 485      * {@code UnsupportedAddressTypeException}.
 486      *
 487      * @throws IOException
 488      *         If an I/O error occurs
 489      * @throws UnsupportedOperationException
 490      *         If RDMA sockets are not supported on this platform
 491      *
 492      * @since 12
 493      */
 494     public static ServerSocketChannel openRdmaServerSocketChannel()
 495         throws IOException {
 496         return RdmaPollSelectorProvider.provider().openServerSocketChannel();
 497     }
 498  
 499     /**
 500      * Opens a selector to multiplex selectable channels to RDMA sockets.
 501      *
 502      * @throws IOException
 503      *         If an I/O error occurs
 504      * @throws UnsupportedOperationException
 505      *         If RDMA sockets are not supported on this platform
 506      *
 507      * @since 12
 508      */
 509     public static Selector openRdmaSelector() throws IOException {
 510         return RdmaPollSelectorProvider.provider().openSelector();
 511     }
 512 }