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