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