/* * 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. *

* The {@link #supportedOptions(Class)} method can be called to determine * the complete set of options available (per socket type) on the * current system. *

* 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 */ //@jdk.Exported public class Sockets { private final static HashMap,Set>> options = new HashMap<>(); static { initOptionSets(); AccessController.doPrivileged( new java.security.PrivilegedAction() { 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 void invokeSet( Method method, Object socket, SocketOption 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 invokeGet( Method method, Object socket, SocketOption 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 void setOption(Socket s, SocketOption 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 getOption(Socket s, SocketOption 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 void setOption(ServerSocket s, SocketOption 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 getOption(ServerSocket s, SocketOption 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 void setOption(DatagramSocket s, SocketOption 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 getOption(DatagramSocket s, SocketOption 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> supportedOptions(Class socketType) { Set> set = options.get(socketType); if (set == null) { throw new IllegalArgumentException("unknown socket type"); } return set; } private static boolean isSupported(Class type, SocketOption option) { Set> options = supportedOptions(type); return options.contains(option); } private static void initOptionSets() { boolean flowsupported = ExtendedOptionsImpl.flowSupported(); // Socket Set> 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); } }