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 {
initOptionSets()65         initOptionSets();
AccessController.doPrivileged( new java.security.PrivilegedAction<Void>() { public Void run() { initMethods(); return null; } } )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 
initMethods()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 
invokeSet( Method method, Object socket, SocketOption<T> option, T value)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 
invokeGet( Method method, Object socket, SocketOption<T> option)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 
Sockets()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      */
setOption(Socket s, SocketOption<T> name, T value)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      */
getOption(Socket s, SocketOption<T> name)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      */
setOption(ServerSocket s, SocketOption<T> name, T value)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      */
getOption(ServerSocket s, SocketOption<T> name)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      */
setOption(DatagramSocket s, SocketOption<T> name, T value)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      */
getOption(DatagramSocket s, SocketOption<T> name)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      */
supportedOptions(Class<?> socketType)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 
isSupported(Class<?> type, SocketOption<?> option)345     private static boolean isSupported(Class<?> type, SocketOption<?> option) {
346         Set<SocketOption<?>> options = supportedOptions(type);
347         return options.contains(option);
348     }
349 
initOptionSets()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