| /* |
| * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package jdk.net; |
| |
| import java.net.*; |
| import java.io.IOException; |
| import java.io.FileDescriptor; |
| import java.security.PrivilegedAction; |
| import java.security.AccessController; |
| import java.lang.reflect.*; |
| import java.util.Set; |
| import java.util.HashSet; |
| import java.util.HashMap; |
| import java.util.Collections; |
| import sun.net.ExtendedOptionsImpl; |
| |
| /** |
| * Defines static methods to set and get socket options defined by the |
| * {@link java.net.SocketOption} interface. All of the standard options defined |
| * by {@link java.net.Socket}, {@link java.net.ServerSocket}, and |
| * {@link java.net.DatagramSocket} can be set this way, as well as additional |
| * or platform specific options supported by each socket type. |
| * <p> |
| * The {@link #supportedOptions(Class)} method can be called to determine |
| * the complete set of options available (per socket type) on the |
| * current system. |
| * <p> |
| * When a security manager is installed, some non-standard socket options |
| * may require a security permission before being set or get. |
| * The details are specified in {@link ExtendedSocketOptions}. No permission |
| * is required for {@link java.net.StandardSocketOptions}. |
| * |
| * @see java.nio.channels.NetworkChannel |
| */ |
| // Android-removed: @jdk.Exported, not present on Android. |
| // @jdk.Exported |
| public class Sockets { |
| |
| private final static HashMap<Class<?>,Set<SocketOption<?>>> |
| options = new HashMap<>(); |
| |
| static { |
| initOptionSets(); |
| AccessController.doPrivileged( |
| new java.security.PrivilegedAction<Void>() { |
| public Void run() { |
| initMethods(); |
| return null; |
| } |
| } |
| ); |
| } |
| |
| private static Method siSetOption; |
| private static Method siGetOption; |
| private static Method dsiSetOption; |
| private static Method dsiGetOption; |
| |
| private static void initMethods() { |
| try { |
| Class<?> clazz = Class.forName("java.net.SocketSecrets"); |
| |
| siSetOption = clazz.getDeclaredMethod( |
| "setOption", Object.class, |
| SocketOption.class, Object.class |
| ); |
| siSetOption.setAccessible(true); |
| |
| siGetOption = clazz.getDeclaredMethod( |
| "getOption", Object.class, SocketOption.class |
| ); |
| siGetOption.setAccessible(true); |
| |
| dsiSetOption = clazz.getDeclaredMethod( |
| "setOption", DatagramSocket.class, |
| SocketOption.class, Object.class |
| ); |
| dsiSetOption.setAccessible(true); |
| |
| dsiGetOption = clazz.getDeclaredMethod( |
| "getOption", DatagramSocket.class, SocketOption.class |
| ); |
| dsiGetOption.setAccessible(true); |
| } catch (ReflectiveOperationException e) { |
| throw new InternalError(e); |
| } |
| } |
| |
| private static <T> void invokeSet( |
| Method method, Object socket, |
| SocketOption<T> option, T value) throws IOException |
| { |
| try { |
| method.invoke(null, socket, option, value); |
| } catch (Exception e) { |
| if (e instanceof InvocationTargetException) { |
| Throwable t = ((InvocationTargetException)e).getTargetException(); |
| if (t instanceof IOException) { |
| throw (IOException)t; |
| } else if (t instanceof RuntimeException) { |
| throw (RuntimeException)t; |
| } |
| } |
| throw new RuntimeException(e); |
| } |
| } |
| |
| private static <T> T invokeGet( |
| Method method, Object socket, SocketOption<T> option) throws IOException |
| { |
| try { |
| return (T)method.invoke(null, socket, option); |
| } catch (Exception e) { |
| if (e instanceof InvocationTargetException) { |
| Throwable t = ((InvocationTargetException)e).getTargetException(); |
| if (t instanceof IOException) { |
| throw (IOException)t; |
| } else if (t instanceof RuntimeException) { |
| throw (RuntimeException)t; |
| } |
| } |
| throw new RuntimeException(e); |
| } |
| } |
| |
| private Sockets() {} |
| |
| /** |
| * Sets the value of a socket option on a {@link java.net.Socket} |
| * |
| * @param s the socket |
| * @param name The socket option |
| * @param value The value of the socket option. May be null for some |
| * options. |
| * |
| * @throws UnsupportedOperationException if the socket does not support |
| * the option. |
| * |
| * @throws IllegalArgumentException if the value is not valid for |
| * the option. |
| * |
| * @throws IOException if an I/O error occurs, or socket is closed. |
| * |
| * @throws SecurityException if a security manager is set and the |
| * caller does not have any required permission. |
| * |
| * @throws NullPointerException if name is null |
| * |
| * @see java.net.StandardSocketOptions |
| */ |
| public static <T> void setOption(Socket s, SocketOption<T> name, T value) throws IOException |
| { |
| if (!isSupported(Socket.class, name)) { |
| throw new UnsupportedOperationException(name.name()); |
| } |
| invokeSet(siSetOption, s, name, value); |
| } |
| |
| /** |
| * Returns the value of a socket option from a {@link java.net.Socket} |
| * |
| * @param s the socket |
| * @param name The socket option |
| * |
| * @return The value of the socket option. |
| * |
| * @throws UnsupportedOperationException if the socket does not support |
| * the option. |
| * |
| * @throws IOException if an I/O error occurs |
| * |
| * @throws SecurityException if a security manager is set and the |
| * caller does not have any required permission. |
| * |
| * @throws NullPointerException if name is null |
| * |
| * @see java.net.StandardSocketOptions |
| */ |
| public static <T> T getOption(Socket s, SocketOption<T> name) throws IOException |
| { |
| if (!isSupported(Socket.class, name)) { |
| throw new UnsupportedOperationException(name.name()); |
| } |
| return invokeGet(siGetOption, s, name); |
| } |
| |
| /** |
| * Sets the value of a socket option on a {@link java.net.ServerSocket} |
| * |
| * @param s the socket |
| * @param name The socket option |
| * @param value The value of the socket option. |
| * |
| * @throws UnsupportedOperationException if the socket does not support |
| * the option. |
| * |
| * @throws IllegalArgumentException if the value is not valid for |
| * the option. |
| * |
| * @throws IOException if an I/O error occurs |
| * |
| * @throws NullPointerException if name is null |
| * |
| * @throws SecurityException if a security manager is set and the |
| * caller does not have any required permission. |
| * |
| * @see java.net.StandardSocketOptions |
| */ |
| public static <T> void setOption(ServerSocket s, SocketOption<T> name, T value) throws IOException |
| { |
| if (!isSupported(ServerSocket.class, name)) { |
| throw new UnsupportedOperationException(name.name()); |
| } |
| invokeSet(siSetOption, s, name, value); |
| } |
| |
| /** |
| * Returns the value of a socket option from a {@link java.net.ServerSocket} |
| * |
| * @param s the socket |
| * @param name The socket option |
| * |
| * @return The value of the socket option. |
| * |
| * @throws UnsupportedOperationException if the socket does not support |
| * the option. |
| * |
| * @throws IOException if an I/O error occurs |
| * |
| * @throws NullPointerException if name is null |
| * |
| * @throws SecurityException if a security manager is set and the |
| * caller does not have any required permission. |
| * |
| * @see java.net.StandardSocketOptions |
| */ |
| public static <T> T getOption(ServerSocket s, SocketOption<T> name) throws IOException |
| { |
| if (!isSupported(ServerSocket.class, name)) { |
| throw new UnsupportedOperationException(name.name()); |
| } |
| return invokeGet(siGetOption, s, name); |
| } |
| |
| /** |
| * Sets the value of a socket option on a {@link java.net.DatagramSocket} |
| * or {@link java.net.MulticastSocket} |
| * |
| * @param s the socket |
| * @param name The socket option |
| * @param value The value of the socket option. |
| * |
| * @throws UnsupportedOperationException if the socket does not support |
| * the option. |
| * |
| * @throws IllegalArgumentException if the value is not valid for |
| * the option. |
| * |
| * @throws IOException if an I/O error occurs |
| * |
| * @throws NullPointerException if name is null |
| * |
| * @throws SecurityException if a security manager is set and the |
| * caller does not have any required permission. |
| * |
| * @see java.net.StandardSocketOptions |
| */ |
| public static <T> void setOption(DatagramSocket s, SocketOption<T> name, T value) throws IOException |
| { |
| if (!isSupported(s.getClass(), name)) { |
| throw new UnsupportedOperationException(name.name()); |
| } |
| invokeSet(dsiSetOption, s, name, value); |
| } |
| |
| /** |
| * Returns the value of a socket option from a |
| * {@link java.net.DatagramSocket} or {@link java.net.MulticastSocket} |
| * |
| * @param s the socket |
| * @param name The socket option |
| * |
| * @return The value of the socket option. |
| * |
| * @throws UnsupportedOperationException if the socket does not support |
| * the option. |
| * |
| * @throws IOException if an I/O error occurs |
| * |
| * @throws NullPointerException if name is null |
| * |
| * @throws SecurityException if a security manager is set and the |
| * caller does not have any required permission. |
| * |
| * @see java.net.StandardSocketOptions |
| */ |
| public static <T> T getOption(DatagramSocket s, SocketOption<T> name) throws IOException |
| { |
| if (!isSupported(s.getClass(), name)) { |
| throw new UnsupportedOperationException(name.name()); |
| } |
| return invokeGet(dsiGetOption, s, name); |
| } |
| |
| /** |
| * Returns a set of {@link java.net.SocketOption}s supported by the |
| * given socket type. This set may include standard options and also |
| * non standard extended options. |
| * |
| * @param socketType the type of java.net socket |
| * |
| * @throws IllegalArgumentException if socketType is not a valid |
| * socket type from the java.net package. |
| */ |
| public static Set<SocketOption<?>> supportedOptions(Class<?> socketType) { |
| Set<SocketOption<?>> set = options.get(socketType); |
| if (set == null) { |
| throw new IllegalArgumentException("unknown socket type"); |
| } |
| return set; |
| } |
| |
| private static boolean isSupported(Class<?> type, SocketOption<?> option) { |
| Set<SocketOption<?>> options = supportedOptions(type); |
| return options.contains(option); |
| } |
| |
| private static void initOptionSets() { |
| boolean flowsupported = ExtendedOptionsImpl.flowSupported(); |
| |
| // Socket |
| |
| Set<SocketOption<?>> set = new HashSet<>(); |
| set.add(StandardSocketOptions.SO_KEEPALIVE); |
| set.add(StandardSocketOptions.SO_SNDBUF); |
| set.add(StandardSocketOptions.SO_RCVBUF); |
| set.add(StandardSocketOptions.SO_REUSEADDR); |
| set.add(StandardSocketOptions.SO_LINGER); |
| set.add(StandardSocketOptions.IP_TOS); |
| set.add(StandardSocketOptions.TCP_NODELAY); |
| if (flowsupported) { |
| set.add(ExtendedSocketOptions.SO_FLOW_SLA); |
| } |
| set = Collections.unmodifiableSet(set); |
| options.put(Socket.class, set); |
| |
| // ServerSocket |
| |
| set = new HashSet<>(); |
| set.add(StandardSocketOptions.SO_RCVBUF); |
| set.add(StandardSocketOptions.SO_REUSEADDR); |
| set.add(StandardSocketOptions.IP_TOS); |
| set = Collections.unmodifiableSet(set); |
| options.put(ServerSocket.class, set); |
| |
| // DatagramSocket |
| |
| set = new HashSet<>(); |
| set.add(StandardSocketOptions.SO_SNDBUF); |
| set.add(StandardSocketOptions.SO_RCVBUF); |
| set.add(StandardSocketOptions.SO_REUSEADDR); |
| set.add(StandardSocketOptions.IP_TOS); |
| if (flowsupported) { |
| set.add(ExtendedSocketOptions.SO_FLOW_SLA); |
| } |
| set = Collections.unmodifiableSet(set); |
| options.put(DatagramSocket.class, set); |
| |
| // MulticastSocket |
| |
| set = new HashSet<>(); |
| set.add(StandardSocketOptions.SO_SNDBUF); |
| set.add(StandardSocketOptions.SO_RCVBUF); |
| set.add(StandardSocketOptions.SO_REUSEADDR); |
| set.add(StandardSocketOptions.IP_TOS); |
| set.add(StandardSocketOptions.IP_MULTICAST_IF); |
| set.add(StandardSocketOptions.IP_MULTICAST_TTL); |
| set.add(StandardSocketOptions.IP_MULTICAST_LOOP); |
| if (flowsupported) { |
| set.add(ExtendedSocketOptions.SO_FLOW_SLA); |
| } |
| set = Collections.unmodifiableSet(set); |
| options.put(MulticastSocket.class, set); |
| } |
| } |