1 /*
   2  * Copyright (c) 2014, 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.io.FileDescriptor;
  31 import java.security.PrivilegedAction;
  32 import java.security.AccessController;
  33 import java.lang.reflect.*;
  34 import java.util.Set;
  35 import java.util.HashSet;
  36 import java.util.HashMap;
  37 import java.util.Collections;
  38 import sun.net.ExtendedOptionsImpl;
  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 @jdk.Exported
  59 public class Sockets {
  60 
  61     private final static HashMap<Class<?>,Set<SocketOption<?>>>
  62         options = new HashMap<>();
  63 
  64     static {
  65         initOptionSets();
  66         AccessController.doPrivileged(
  67             new java.security.PrivilegedAction<Void>() {
  68                 public Void run() {
  69                     initMethods();
  70                     return null;
  71                 }
  72             }
  73         );
  74     }
  75 
  76     private static Method siSetOption;
  77     private static Method siGetOption;
  78     private static Method dsiSetOption;
  79     private static Method dsiGetOption;
  80 
  81     private static void initMethods() {
  82         try {
  83             Class<?> clazz = Class.forName("java.net.SocketSecrets");
  84 
  85             siSetOption = clazz.getDeclaredMethod(
  86                 "setOption", Object.class,
  87                 SocketOption.class, Object.class
  88             );
  89             siSetOption.setAccessible(true);
  90 
  91             siGetOption = clazz.getDeclaredMethod(
  92                 "getOption", Object.class, SocketOption.class
  93             );
  94             siGetOption.setAccessible(true);
  95 
  96             dsiSetOption = clazz.getDeclaredMethod(
  97                 "setOption", DatagramSocket.class,
  98                 SocketOption.class, Object.class
  99             );
 100             dsiSetOption.setAccessible(true);
 101 
 102             dsiGetOption = clazz.getDeclaredMethod(
 103                 "getOption", DatagramSocket.class, SocketOption.class
 104             );
 105             dsiGetOption.setAccessible(true);
 106         } catch (ReflectiveOperationException e) {
 107             throw new InternalError(e);
 108         }
 109     }
 110 
 111     private static <T> void invokeSet(
 112         Method method, Object socket,
 113         SocketOption<T> option, T value) throws IOException
 114     {
 115         try {
 116             method.invoke(null, socket, option, value);
 117         } catch (Exception e) {
 118             if (e instanceof InvocationTargetException) {
 119                 Throwable t = ((InvocationTargetException)e).getTargetException();
 120                 if (t instanceof IOException) {
 121                     throw (IOException)t;
 122                 } else if (t instanceof RuntimeException) {
 123                     throw (RuntimeException)t;
 124                 }
 125             }
 126             throw new RuntimeException(e);
 127         }
 128     }
 129 
 130     private static <T> T invokeGet(
 131         Method method, Object socket, SocketOption<T> option) throws IOException
 132     {
 133         try {
 134             return (T)method.invoke(null, socket, option);
 135         } catch (Exception e) {
 136             if (e instanceof InvocationTargetException) {
 137                 Throwable t = ((InvocationTargetException)e).getTargetException();
 138                 if (t instanceof IOException) {
 139                     throw (IOException)t;
 140                 } else if (t instanceof RuntimeException) {
 141                     throw (RuntimeException)t;
 142                 }
 143             }
 144             throw new RuntimeException(e);
 145         }
 146     }
 147 
 148     private Sockets() {}
 149 
 150     /**
 151      * Sets the value of a socket option on a {@link java.net.Socket}
 152      *
 153      * @param s the socket
 154      * @param name The socket option
 155      * @param value The value of the socket option. May be null for some
 156      *              options.
 157      *
 158      * @throws UnsupportedOperationException if the socket does not support
 159      *         the option.
 160      *
 161      * @throws IllegalArgumentException if the value is not valid for
 162      *         the option.
 163      *
 164      * @throws IOException if an I/O error occurs, or socket is closed.
 165      *
 166      * @throws SecurityException if a security manager is set and the
 167      *         caller does not have any required permission.
 168      *
 169      * @throws NullPointerException if name is null
 170      *
 171      * @see java.net.StandardSocketOptions
 172      */
 173     public static <T> void setOption(Socket s, SocketOption<T> name, T value) throws IOException
 174     {
 175         if (!isSupported(Socket.class, name)) {
 176             throw new UnsupportedOperationException(name.name());
 177         }
 178         invokeSet(siSetOption, s, name, value);
 179     }
 180 
 181     /**
 182      * Returns the value of a socket option from a {@link java.net.Socket}
 183      *
 184      * @param s the socket
 185      * @param name The socket option
 186      *
 187      * @return The value of the socket option.
 188      *
 189      * @throws UnsupportedOperationException if the socket does not support
 190      *         the option.
 191      *
 192      * @throws IOException if an I/O error occurs
 193      *
 194      * @throws SecurityException if a security manager is set and the
 195      *         caller does not have any required permission.
 196      *
 197      * @throws NullPointerException if name is null
 198      *
 199      * @see java.net.StandardSocketOptions
 200      */
 201     public static <T> T getOption(Socket s, SocketOption<T> name) throws IOException
 202     {
 203         if (!isSupported(Socket.class, name)) {
 204             throw new UnsupportedOperationException(name.name());
 205         }
 206         return invokeGet(siGetOption, s, name);
 207     }
 208 
 209     /**
 210      * Sets the value of a socket option on a {@link java.net.ServerSocket}
 211      *
 212      * @param s the socket
 213      * @param name The socket option
 214      * @param value The value of the socket option.
 215      *
 216      * @throws UnsupportedOperationException if the socket does not support
 217      *         the option.
 218      *
 219      * @throws IllegalArgumentException if the value is not valid for
 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> void setOption(ServerSocket s, SocketOption<T> name, T value) throws IOException
 232     {
 233         if (!isSupported(ServerSocket.class, name)) {
 234             throw new UnsupportedOperationException(name.name());
 235         }
 236         invokeSet(siSetOption, s, name, value);
 237     }
 238 
 239     /**
 240      * Returns the value of a socket option from a {@link java.net.ServerSocket}
 241      *
 242      * @param s the socket
 243      * @param name The socket option
 244      *
 245      * @return The value of the socket option.
 246      *
 247      * @throws UnsupportedOperationException if the socket does not support
 248      *         the option.
 249      *
 250      * @throws IOException if an I/O error occurs
 251      *
 252      * @throws NullPointerException if name is null
 253      *
 254      * @throws SecurityException if a security manager is set and the
 255      *         caller does not have any required permission.
 256      *
 257      * @see java.net.StandardSocketOptions
 258      */
 259     public static <T> T getOption(ServerSocket s, SocketOption<T> name) throws IOException
 260     {
 261         if (!isSupported(ServerSocket.class, name)) {
 262             throw new UnsupportedOperationException(name.name());
 263         }
 264         return invokeGet(siGetOption, s, name);
 265     }
 266 
 267     /**
 268      * Sets the value of a socket option on a {@link java.net.DatagramSocket}
 269      * or {@link java.net.MulticastSocket}
 270      *
 271      * @param s the socket
 272      * @param name The socket option
 273      * @param value The value of the socket option.
 274      *
 275      * @throws UnsupportedOperationException if the socket does not support
 276      *         the option.
 277      *
 278      * @throws IllegalArgumentException if the value is not valid for
 279      *         the option.
 280      *
 281      * @throws IOException if an I/O error occurs
 282      *
 283      * @throws NullPointerException if name is null
 284      *
 285      * @throws SecurityException if a security manager is set and the
 286      *         caller does not have any required permission.
 287      *
 288      * @see java.net.StandardSocketOptions
 289      */
 290     public static <T> void setOption(DatagramSocket s, SocketOption<T> name, T value) throws IOException
 291     {
 292         if (!isSupported(s.getClass(), name)) {
 293             throw new UnsupportedOperationException(name.name());
 294         }
 295         invokeSet(dsiSetOption, s, name, value);
 296     }
 297 
 298     /**
 299      * Returns the value of a socket option from a
 300      * {@link java.net.DatagramSocket} or {@link java.net.MulticastSocket}
 301      *
 302      * @param s the socket
 303      * @param name The socket option
 304      *
 305      * @return The value of the socket option.
 306      *
 307      * @throws UnsupportedOperationException if the socket does not support
 308      *         the option.
 309      *
 310      * @throws IOException if an I/O error occurs
 311      *
 312      * @throws NullPointerException if name is null
 313      *
 314      * @throws SecurityException if a security manager is set and the
 315      *         caller does not have any required permission.
 316      *
 317      * @see java.net.StandardSocketOptions
 318      */
 319     public static <T> T getOption(DatagramSocket s, SocketOption<T> name) throws IOException
 320     {
 321         if (!isSupported(s.getClass(), name)) {
 322             throw new UnsupportedOperationException(name.name());
 323         }
 324         return invokeGet(dsiGetOption, s, name);
 325     }
 326 
 327     /**
 328      * Returns a set of {@link java.net.SocketOption}s supported by the
 329      * given socket type. This set may include standard options and also
 330      * non standard extended options.
 331      *
 332      * @param socketType the type of java.net socket
 333      *
 334      * @throws IllegalArgumentException if socketType is not a valid
 335      *         socket type from the java.net package.
 336      */
 337     public static Set<SocketOption<?>> supportedOptions(Class<?> socketType) {
 338         Set<SocketOption<?>> set = options.get(socketType);
 339         if (set == null) {
 340             throw new IllegalArgumentException("unknown socket type");
 341         }
 342         return set;
 343     }
 344 
 345     private static boolean isSupported(Class<?> type, SocketOption<?> option) {
 346         Set<SocketOption<?>> options = supportedOptions(type);
 347         return options.contains(option);
 348     }
 349 
 350     private static void initOptionSets() {
 351         boolean flowsupported = ExtendedOptionsImpl.flowSupported();
 352 
 353         // Socket
 354 
 355         Set<SocketOption<?>> set = new HashSet<>();
 356         set.add(StandardSocketOptions.SO_KEEPALIVE);
 357         set.add(StandardSocketOptions.SO_SNDBUF);
 358         set.add(StandardSocketOptions.SO_RCVBUF);
 359         set.add(StandardSocketOptions.SO_REUSEADDR);
 360         set.add(StandardSocketOptions.SO_LINGER);
 361         set.add(StandardSocketOptions.IP_TOS);
 362         set.add(StandardSocketOptions.TCP_NODELAY);
 363         if (flowsupported) {
 364             set.add(ExtendedSocketOptions.SO_FLOW_SLA);
 365         }
 366         set = Collections.unmodifiableSet(set);
 367         options.put(Socket.class, set);
 368 
 369         // ServerSocket
 370 
 371         set = new HashSet<>();
 372         set.add(StandardSocketOptions.SO_RCVBUF);
 373         set.add(StandardSocketOptions.SO_REUSEADDR);
 374         set = Collections.unmodifiableSet(set);
 375         options.put(ServerSocket.class, set);
 376 
 377         // DatagramSocket
 378 
 379         set = new HashSet<>();
 380         set.add(StandardSocketOptions.SO_SNDBUF);
 381         set.add(StandardSocketOptions.SO_RCVBUF);
 382         set.add(StandardSocketOptions.SO_REUSEADDR);
 383         set.add(StandardSocketOptions.IP_TOS);
 384         if (flowsupported) {
 385             set.add(ExtendedSocketOptions.SO_FLOW_SLA);
 386         }
 387         set = Collections.unmodifiableSet(set);
 388         options.put(DatagramSocket.class, set);
 389 
 390         // MulticastSocket
 391 
 392         set = new HashSet<>();
 393         set.add(StandardSocketOptions.SO_SNDBUF);
 394         set.add(StandardSocketOptions.SO_RCVBUF);
 395         set.add(StandardSocketOptions.SO_REUSEADDR);
 396         set.add(StandardSocketOptions.IP_TOS);
 397         set.add(StandardSocketOptions.IP_MULTICAST_IF);
 398         set.add(StandardSocketOptions.IP_MULTICAST_TTL);
 399         set.add(StandardSocketOptions.IP_MULTICAST_LOOP);
 400         if (flowsupported) {
 401             set.add(ExtendedSocketOptions.SO_FLOW_SLA);
 402         }
 403         set = Collections.unmodifiableSet(set);
 404         options.put(MulticastSocket.class, set);
 405     }
 406 }