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  * When a security manager is installed, some non-standard socket options
  53  * may require a security permission before being set or get.
  54  * The details are specified in {@link ExtendedSocketOptions}. No permission
  55  * is required for {@link java.net.StandardSocketOptions}.
  56  *
  57  * @see java.nio.channels.NetworkChannel
  58  */
  59 public class Sockets {
  60 
  61     private static final Map<Class<?>,Set<SocketOption<?>>>
  62             options = optionSets();
  63 
  64     private Sockets() {}
  65 
  66     /**
  67      * Sets the value of a socket option on a {@link java.net.Socket}
  68      *
  69      * @param s the socket
  70      * @param name The socket option
  71      * @param value The value of the socket option. May be null for some
  72      *              options.
  73      *
  74      * @throws UnsupportedOperationException if the socket does not support
  75      *         the option.
  76      *
  77      * @throws IllegalArgumentException if the value is not valid for
  78      *         the option.
  79      *
  80      * @throws IOException if an I/O error occurs, or socket is closed.
  81      *
  82      * @throws SecurityException if a security manager is set and the
  83      *         caller does not have any required permission.
  84      *
  85      * @throws NullPointerException if name is null
  86      *
  87      * @see java.net.StandardSocketOptions
  88      */
  89     public static <T> void setOption(Socket s, SocketOption<T> name, T value) throws IOException
  90     {
  91         s.setOption(name, value);
  92     }
  93 
  94     /**
  95      * Returns the value of a socket option from a {@link java.net.Socket}
  96      *
  97      * @param s the socket
  98      * @param name The socket option
  99      *
 100      * @return The value of the socket option.
 101      *
 102      * @throws UnsupportedOperationException if the socket does not support
 103      *         the option.
 104      *
 105      * @throws IOException if an I/O error occurs
 106      *
 107      * @throws SecurityException if a security manager is set and the
 108      *         caller does not have any required permission.
 109      *
 110      * @throws NullPointerException if name is null
 111      *
 112      * @see java.net.StandardSocketOptions
 113      */
 114     public static <T> T getOption(Socket s, SocketOption<T> name) throws IOException
 115     {
 116         return s.getOption(name);
 117     }
 118 
 119     /**
 120      * Sets the value of a socket option on a {@link java.net.ServerSocket}
 121      *
 122      * @param s the socket
 123      * @param name The socket option
 124      * @param value The value of the socket option.
 125      *
 126      * @throws UnsupportedOperationException if the socket does not support
 127      *         the option.
 128      *
 129      * @throws IllegalArgumentException if the value is not valid for
 130      *         the option.
 131      *
 132      * @throws IOException if an I/O error occurs
 133      *
 134      * @throws NullPointerException if name is null
 135      *
 136      * @throws SecurityException if a security manager is set and the
 137      *         caller does not have any required permission.
 138      *
 139      * @see java.net.StandardSocketOptions
 140      */
 141     public static <T> void setOption(ServerSocket s, SocketOption<T> name, T value) throws IOException
 142     {
 143         s.setOption(name, value);
 144     }
 145 
 146     /**
 147      * Returns the value of a socket option from a {@link java.net.ServerSocket}
 148      *
 149      * @param s the socket
 150      * @param name The socket option
 151      *
 152      * @return The value of the socket option.
 153      *
 154      * @throws UnsupportedOperationException if the socket does not support
 155      *         the option.
 156      *
 157      * @throws IOException if an I/O error occurs
 158      *
 159      * @throws NullPointerException if name is null
 160      *
 161      * @throws SecurityException if a security manager is set and the
 162      *         caller does not have any required permission.
 163      *
 164      * @see java.net.StandardSocketOptions
 165      */
 166     public static <T> T getOption(ServerSocket s, SocketOption<T> name) throws IOException
 167     {
 168         return s.getOption(name);
 169     }
 170 
 171     /**
 172      * Sets the value of a socket option on a {@link java.net.DatagramSocket}
 173      * or {@link java.net.MulticastSocket}
 174      *
 175      * @param s the socket
 176      * @param name The socket option
 177      * @param value The value of the socket option.
 178      *
 179      * @throws UnsupportedOperationException if the socket does not support
 180      *         the option.
 181      *
 182      * @throws IllegalArgumentException if the value is not valid for
 183      *         the option.
 184      *
 185      * @throws IOException if an I/O error occurs
 186      *
 187      * @throws NullPointerException if name is null
 188      *
 189      * @throws SecurityException if a security manager is set and the
 190      *         caller does not have any required permission.
 191      *
 192      * @see java.net.StandardSocketOptions
 193      */
 194     public static <T> void setOption(DatagramSocket s, SocketOption<T> name, T value) throws IOException
 195     {
 196         s.setOption(name, value);
 197     }
 198 
 199     /**
 200      * Returns the value of a socket option from a
 201      * {@link java.net.DatagramSocket} or {@link java.net.MulticastSocket}
 202      *
 203      * @param s the socket
 204      * @param name The socket option
 205      *
 206      * @return The value of the socket option.
 207      *
 208      * @throws UnsupportedOperationException if the socket does not support
 209      *         the option.
 210      *
 211      * @throws IOException if an I/O error occurs
 212      *
 213      * @throws NullPointerException if name is null
 214      *
 215      * @throws SecurityException if a security manager is set and the
 216      *         caller does not have any required permission.
 217      *
 218      * @see java.net.StandardSocketOptions
 219      */
 220     public static <T> T getOption(DatagramSocket s, SocketOption<T> name) throws IOException
 221     {
 222         return s.getOption(name);
 223     }
 224 
 225     /**
 226      * Returns a set of {@link java.net.SocketOption}s supported by the
 227      * given socket type. This set may include standard options and also
 228      * non standard extended options.
 229      *
 230      * @param socketType the type of java.net socket
 231      *
 232      * @throws IllegalArgumentException if socketType is not a valid
 233      *         socket type from the java.net package.
 234      */
 235     public static Set<SocketOption<?>> supportedOptions(Class<?> socketType) {
 236         Set<SocketOption<?>> set = options.get(socketType);
 237         if (set == null) {
 238             throw new IllegalArgumentException("unknown socket type");
 239         }
 240         return set;
 241     }
 242 
 243     private static void checkValueType(Object value, Class<?> type) {
 244         if (!type.isAssignableFrom(value.getClass())) {
 245             String s = "Found: " + value.getClass().toString() + " Expected: "
 246                         + type.toString();
 247             throw new IllegalArgumentException(s);
 248         }
 249     }
 250 
 251     private static volatile boolean checkedReusePort;
 252     private static volatile boolean isReusePortAvailable;
 253 
 254     /**
 255      * Tells whether SO_REUSEPORT is supported.
 256      */
 257     static boolean isReusePortAvailable() {
 258         if (!checkedReusePort) {
 259             Set<SocketOption<?>> s = new Socket().supportedOptions();
 260             isReusePortAvailable = s.contains(StandardSocketOptions.SO_REUSEPORT);
 261             checkedReusePort = true;
 262         }
 263         return isReusePortAvailable;
 264     }
 265 
 266     private static Map<Class<?>,Set<SocketOption<?>>> optionSets() {
 267         Map<Class<?>,Set<SocketOption<?>>> options = new HashMap<>();
 268         boolean flowsupported = PlatformSocketOptions.get().flowSupported();
 269         boolean reuseportsupported = isReusePortAvailable();
 270         // Socket
 271 
 272         Set<SocketOption<?>> set = new HashSet<>();
 273         set.add(StandardSocketOptions.SO_KEEPALIVE);
 274         set.add(StandardSocketOptions.SO_SNDBUF);
 275         set.add(StandardSocketOptions.SO_RCVBUF);
 276         set.add(StandardSocketOptions.SO_REUSEADDR);
 277         if (reuseportsupported) {
 278             set.add(StandardSocketOptions.SO_REUSEPORT);
 279         }
 280         set.add(StandardSocketOptions.SO_LINGER);
 281         set.add(StandardSocketOptions.IP_TOS);
 282         set.add(StandardSocketOptions.TCP_NODELAY);
 283         if (flowsupported) {
 284             set.add(ExtendedSocketOptions.SO_FLOW_SLA);
 285         }
 286         if (QuickAck.available) {
 287             set.add(ExtendedSocketOptions.TCP_QUICKACK);
 288         }
 289         if (KeepAliveOptions.AVAILABLE) {
 290             set.addAll(Set.of(ExtendedSocketOptions.TCP_KEEPCOUNT,
 291                     ExtendedSocketOptions.TCP_KEEPIDLE,
 292                     ExtendedSocketOptions.TCP_KEEPINTERVAL));
 293         }
 294         set = Collections.unmodifiableSet(set);
 295         options.put(Socket.class, set);
 296 
 297         // ServerSocket
 298 
 299         set = new HashSet<>();
 300         set.add(StandardSocketOptions.SO_RCVBUF);
 301         set.add(StandardSocketOptions.SO_REUSEADDR);
 302         if (reuseportsupported) {
 303             set.add(StandardSocketOptions.SO_REUSEPORT);
 304         }
 305         if (QuickAck.available) {
 306             set.add(ExtendedSocketOptions.TCP_QUICKACK);
 307         }
 308         if (KeepAliveOptions.AVAILABLE) {
 309             set.addAll(Set.of(ExtendedSocketOptions.TCP_KEEPCOUNT,
 310                     ExtendedSocketOptions.TCP_KEEPIDLE,
 311                     ExtendedSocketOptions.TCP_KEEPINTERVAL));
 312         }
 313         set.add(StandardSocketOptions.IP_TOS);
 314         set = Collections.unmodifiableSet(set);
 315         options.put(ServerSocket.class, set);
 316 
 317         // DatagramSocket
 318 
 319         set = new HashSet<>();
 320         set.add(StandardSocketOptions.SO_SNDBUF);
 321         set.add(StandardSocketOptions.SO_RCVBUF);
 322         set.add(StandardSocketOptions.SO_REUSEADDR);
 323         if (reuseportsupported) {
 324             set.add(StandardSocketOptions.SO_REUSEPORT);
 325         }
 326         set.add(StandardSocketOptions.IP_TOS);
 327         if (flowsupported) {
 328             set.add(ExtendedSocketOptions.SO_FLOW_SLA);
 329         }
 330         set = Collections.unmodifiableSet(set);
 331         options.put(DatagramSocket.class, set);
 332 
 333         // MulticastSocket
 334 
 335         set = new HashSet<>();
 336         set.add(StandardSocketOptions.SO_SNDBUF);
 337         set.add(StandardSocketOptions.SO_RCVBUF);
 338         set.add(StandardSocketOptions.SO_REUSEADDR);
 339         if (reuseportsupported) {
 340             set.add(StandardSocketOptions.SO_REUSEPORT);
 341         }
 342         set.add(StandardSocketOptions.IP_TOS);
 343         set.add(StandardSocketOptions.IP_MULTICAST_IF);
 344         set.add(StandardSocketOptions.IP_MULTICAST_TTL);
 345         set.add(StandardSocketOptions.IP_MULTICAST_LOOP);
 346         if (flowsupported) {
 347             set.add(ExtendedSocketOptions.SO_FLOW_SLA);
 348         }
 349         set = Collections.unmodifiableSet(set);
 350         options.put(MulticastSocket.class, set);
 351 
 352         return Collections.unmodifiableMap(options);
 353     }
 354 
 355     /**
 356      * Tells whether TCP_QUICKACK is supported.
 357      */
 358     static class QuickAck {
 359 
 360         static final boolean available;
 361 
 362         static {
 363             Set<SocketOption<?>> s = new Socket().supportedOptions();
 364             available = s.contains(ExtendedSocketOptions.TCP_QUICKACK);
 365         }
 366     }
 367 
 368     /**
 369      * Tells whether TCP_KEEPALIVE options are supported.
 370      */
 371     static class KeepAliveOptions {
 372 
 373         static final boolean AVAILABLE;
 374 
 375         static {
 376             Set<SocketOption<?>> s = new Socket().supportedOptions();
 377             AVAILABLE = s.containsAll(Set.of(ExtendedSocketOptions.TCP_KEEPCOUNT,
 378                                             ExtendedSocketOptions.TCP_KEEPIDLE,
 379                                             ExtendedSocketOptions.TCP_KEEPINTERVAL));
 380         }
 381     }
 382 
 383     /**
 384      * Creates an unconnected RDMA socket
 385      */
 386     public static Socket openRdmaSocket() throws IOException {
 387         RdmaSocketImpl impl = new RdmaSocketImpl();
 388         Socket s = new Socket(impl) {};
 389         return s;
 390     }
 391 
 392     /**
 393      * Creates an unbound RDMA server socket
 394      */
 395     public static ServerSocket openRdmaServerSocket() throws IOException {
 396         SocketImpl impl = new RdmaSocketImpl();
 397         ServerSocket ss = new ServerSocket(impl) {
 398             public Socket accept() throws IOException {
 399                 if (isClosed())
 400                 throw new SocketException("Socket is closed");
 401                 if (!isBound())
 402                     throw new SocketException("Socket is not bound yet");
 403 
 404                 Socket s = openRdmaSocket();
 405                 implAccept(s);
 406                 return s;
 407             }    
 408         };
 409         return ss;
 410     }
 411 
 412     /**
 413      * Opens a RDMA socket channel.
 414      *
 415      * @return  A new RDMA socket channel
 416      *
 417      * @throws  IOException
 418      *          If an I/O error occurs
 419      */
 420     public static SocketChannel openRdmaSocketChannel() throws IOException {
 421         return RdmaPollSelectorProvider.provider().openSocketChannel();
 422     }
 423 
 424     /**
 425      * Opens a RDMA server-socket channel.
 426      *
 427      * <p> The new channel is created by invoking the {@link
 428      * java.nio.channels.spi.SelectorProvider#openServerSocketChannel
 429      * openServerSocketChannel} method of the system-wide default {@link
 430      * java.nio.channels.spi.SelectorProvider} object.
 431      *
 432      * <p> The new channel's socket is initially unbound; it must be bound to a
 433      * specific address via one of its socket's {@link
 434      * java.net.ServerSocket#bind(SocketAddress) bind} methods before
 435      * connections can be accepted.  </p>
 436      *
 437      * @return  A new RDMA server socket channel
 438      *
 439      * @throws  IOException
 440      *          If an I/O error occurs
 441      */
 442     public static ServerSocketChannel openRdmaServerSocketChannel()
 443         throws IOException {
 444         return RdmaPollSelectorProvider.provider().openServerSocketChannel();
 445     }
 446 
 447     /**
 448      * Opens a RDMA selector.
 449      *
 450      * @return  A new RDMA selector
 451      *
 452      * @throws  IOException
 453      *          If an I/O error occurs
 454      */
 455     public static Selector openRdmaSelector() throws IOException {
 456         return RdmaPollSelectorProvider.provider().openSelector();
 457     }
 458 }