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