Home | History | Annotate | Download | only in net
      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.add(StandardSocketOptions.IP_TOS);
    375         set = Collections.unmodifiableSet(set);
    376         options.put(ServerSocket.class, set);
    377 
    378         // DatagramSocket
    379 
    380         set = new HashSet<>();
    381         set.add(StandardSocketOptions.SO_SNDBUF);
    382         set.add(StandardSocketOptions.SO_RCVBUF);
    383         set.add(StandardSocketOptions.SO_REUSEADDR);
    384         set.add(StandardSocketOptions.IP_TOS);
    385         if (flowsupported) {
    386             set.add(ExtendedSocketOptions.SO_FLOW_SLA);
    387         }
    388         set = Collections.unmodifiableSet(set);
    389         options.put(DatagramSocket.class, set);
    390 
    391         // MulticastSocket
    392 
    393         set = new HashSet<>();
    394         set.add(StandardSocketOptions.SO_SNDBUF);
    395         set.add(StandardSocketOptions.SO_RCVBUF);
    396         set.add(StandardSocketOptions.SO_REUSEADDR);
    397         set.add(StandardSocketOptions.IP_TOS);
    398         set.add(StandardSocketOptions.IP_MULTICAST_IF);
    399         set.add(StandardSocketOptions.IP_MULTICAST_TTL);
    400         set.add(StandardSocketOptions.IP_MULTICAST_LOOP);
    401         if (flowsupported) {
    402             set.add(ExtendedSocketOptions.SO_FLOW_SLA);
    403         }
    404         set = Collections.unmodifiableSet(set);
    405         options.put(MulticastSocket.class, set);
    406     }
    407 }
    408