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