1 /* 2 * Copyright (c) 2016, 2018, 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.util.Collections; 31 import java.util.HashMap; 32 import java.util.HashSet; 33 import java.util.Map; 34 import java.util.Set; 35 import jdk.net.ExtendedSocketOptions.PlatformSocketOptions; 36 37 /** 38 * Defines static methods to set and get socket options defined by the 39 * {@link java.net.SocketOption} interface. All of the standard options defined 40 * by {@link java.net.Socket}, {@link java.net.ServerSocket}, and 41 * {@link java.net.DatagramSocket} can be set this way, as well as additional 42 * or platform specific options supported by each socket type. 43 * <p> 44 * The {@link #supportedOptions(Class)} method can be called to determine 45 * the complete set of options available (per socket type) on the 46 * current system. 47 * <p> 48 * When a security manager is installed, some non-standard socket options 49 * may require a security permission before being set or get. 50 * The details are specified in {@link ExtendedSocketOptions}. No permission 51 * is required for {@link java.net.StandardSocketOptions}. 52 * 53 * @see java.nio.channels.NetworkChannel 54 */ 55 public class Sockets { 56 57 private static final Map<Class<?>,Set<SocketOption<?>>> 58 options = optionSets(); 59 60 private Sockets() {} 61 62 /** 63 * Sets the value of a socket option on a {@link java.net.Socket} 64 * 65 * @param s the socket 66 * @param name The socket option 67 * @param value The value of the socket option. May be null for some 68 * options. 69 * 70 * @throws UnsupportedOperationException if the socket does not support 71 * the option. 72 * 73 * @throws IllegalArgumentException if the value is not valid for 74 * the option. 75 * 76 * @throws IOException if an I/O error occurs, or socket is closed. 77 * 78 * @throws SecurityException if a security manager is set and the 79 * caller does not have any required permission. 80 * 81 * @throws NullPointerException if name is null 82 * 83 * @see java.net.StandardSocketOptions 84 */ 85 public static <T> void setOption(Socket s, SocketOption<T> name, T value) throws IOException 86 { 87 s.setOption(name, value); 88 } 89 90 /** 91 * Returns the value of a socket option from a {@link java.net.Socket} 92 * 93 * @param s the socket 94 * @param name The socket option 95 * 96 * @return The value of the socket option. 97 * 98 * @throws UnsupportedOperationException if the socket does not support 99 * the option. 100 * 101 * @throws IOException if an I/O error occurs 102 * 103 * @throws SecurityException if a security manager is set and the 104 * caller does not have any required permission. 105 * 106 * @throws NullPointerException if name is null 107 * 108 * @see java.net.StandardSocketOptions 109 */ 110 public static <T> T getOption(Socket s, SocketOption<T> name) throws IOException 111 { 112 return s.getOption(name); 113 } 114 115 /** 116 * Sets the value of a socket option on a {@link java.net.ServerSocket} 117 * 118 * @param s the socket 119 * @param name The socket option 120 * @param value The value of the socket option. 121 * 122 * @throws UnsupportedOperationException if the socket does not support 123 * the option. 124 * 125 * @throws IllegalArgumentException if the value is not valid for 126 * the option. 127 * 128 * @throws IOException if an I/O error occurs 129 * 130 * @throws NullPointerException if name is null 131 * 132 * @throws SecurityException if a security manager is set and the 133 * caller does not have any required permission. 134 * 135 * @see java.net.StandardSocketOptions 136 */ 137 public static <T> void setOption(ServerSocket s, SocketOption<T> name, T value) throws IOException 138 { 139 s.setOption(name, value); 140 } 141 142 /** 143 * Returns the value of a socket option from a {@link java.net.ServerSocket} 144 * 145 * @param s the socket 146 * @param name The socket option 147 * 148 * @return The value of the socket option. 149 * 150 * @throws UnsupportedOperationException if the socket does not support 151 * the option. 152 * 153 * @throws IOException if an I/O error occurs 154 * 155 * @throws NullPointerException if name is null 156 * 157 * @throws SecurityException if a security manager is set and the 158 * caller does not have any required permission. 159 * 160 * @see java.net.StandardSocketOptions 161 */ 162 public static <T> T getOption(ServerSocket s, SocketOption<T> name) throws IOException 163 { 164 return s.getOption(name); 165 } 166 167 /** 168 * Sets the value of a socket option on a {@link java.net.DatagramSocket} 169 * or {@link java.net.MulticastSocket} 170 * 171 * @param s the socket 172 * @param name The socket option 173 * @param value The value of the socket option. 174 * 175 * @throws UnsupportedOperationException if the socket does not support 176 * the option. 177 * 178 * @throws IllegalArgumentException if the value is not valid for 179 * the option. 180 * 181 * @throws IOException if an I/O error occurs 182 * 183 * @throws NullPointerException if name is null 184 * 185 * @throws SecurityException if a security manager is set and the 186 * caller does not have any required permission. 187 * 188 * @see java.net.StandardSocketOptions 189 */ 190 public static <T> void setOption(DatagramSocket s, SocketOption<T> name, T value) throws IOException 191 { 192 s.setOption(name, value); 193 } 194 195 /** 196 * Returns the value of a socket option from a 197 * {@link java.net.DatagramSocket} or {@link java.net.MulticastSocket} 198 * 199 * @param s the socket 200 * @param name The socket option 201 * 202 * @return The value of the socket option. 203 * 204 * @throws UnsupportedOperationException if the socket does not support 205 * the option. 206 * 207 * @throws IOException if an I/O error occurs 208 * 209 * @throws NullPointerException if name is null 210 * 211 * @throws SecurityException if a security manager is set and the 212 * caller does not have any required permission. 213 * 214 * @see java.net.StandardSocketOptions 215 */ 216 public static <T> T getOption(DatagramSocket s, SocketOption<T> name) throws IOException 217 { 218 return s.getOption(name); 219 } 220 221 /** 222 * Returns a set of {@link java.net.SocketOption}s supported by the 223 * given socket type. This set may include standard options and also 224 * non standard extended options. 225 * 226 * @param socketType the type of java.net socket 227 * 228 * @throws IllegalArgumentException if socketType is not a valid 229 * socket type from the java.net package. 230 */ 231 public static Set<SocketOption<?>> supportedOptions(Class<?> socketType) { 232 Set<SocketOption<?>> set = options.get(socketType); 233 if (set == null) { 234 throw new IllegalArgumentException("unknown socket type"); 235 } 236 return set; 237 } 238 239 private static void checkValueType(Object value, Class<?> type) { 240 if (!type.isAssignableFrom(value.getClass())) { 241 String s = "Found: " + value.getClass().toString() + " Expected: " 242 + type.toString(); 243 throw new IllegalArgumentException(s); 244 } 245 } 246 247 private static volatile boolean checkedReusePort; 248 private static volatile boolean isReusePortAvailable; 249 250 /** 251 * Tells whether SO_REUSEPORT is supported. 252 */ 253 static boolean isReusePortAvailable() { 254 if (!checkedReusePort) { 255 Set<SocketOption<?>> s = new Socket().supportedOptions(); 256 isReusePortAvailable = s.contains(StandardSocketOptions.SO_REUSEPORT); 257 checkedReusePort = true; 258 } 259 return isReusePortAvailable; 260 } 261 262 private static Map<Class<?>,Set<SocketOption<?>>> optionSets() { 263 Map<Class<?>,Set<SocketOption<?>>> options = new HashMap<>(); 264 boolean flowsupported = PlatformSocketOptions.get().flowSupported(); 265 boolean reuseportsupported = isReusePortAvailable(); 266 // Socket 267 268 Set<SocketOption<?>> set = new HashSet<>(); 269 set.add(StandardSocketOptions.SO_KEEPALIVE); 270 set.add(StandardSocketOptions.SO_SNDBUF); 271 set.add(StandardSocketOptions.SO_RCVBUF); 272 set.add(StandardSocketOptions.SO_REUSEADDR); 273 if (reuseportsupported) { 274 set.add(StandardSocketOptions.SO_REUSEPORT); 275 } 276 set.add(StandardSocketOptions.SO_LINGER); 277 set.add(StandardSocketOptions.IP_TOS); 278 set.add(StandardSocketOptions.TCP_NODELAY); 279 if (flowsupported) { 280 set.add(ExtendedSocketOptions.SO_FLOW_SLA); 281 } 282 if (QuickAck.available) { 283 set.add(ExtendedSocketOptions.TCP_QUICKACK); 284 } 285 if (KeepAliveOptions.AVAILABLE) { 286 set.addAll(Set.of(ExtendedSocketOptions.TCP_KEEPCOUNT, 287 ExtendedSocketOptions.TCP_KEEPIDLE, 288 ExtendedSocketOptions.TCP_KEEPINTERVAL)); 289 } 290 set = Collections.unmodifiableSet(set); 291 options.put(Socket.class, set); 292 293 // ServerSocket 294 295 set = new HashSet<>(); 296 set.add(StandardSocketOptions.SO_RCVBUF); 297 set.add(StandardSocketOptions.SO_REUSEADDR); 298 if (reuseportsupported) { 299 set.add(StandardSocketOptions.SO_REUSEPORT); 300 } 301 if (QuickAck.available) { 302 set.add(ExtendedSocketOptions.TCP_QUICKACK); 303 } 304 if (KeepAliveOptions.AVAILABLE) { 305 set.addAll(Set.of(ExtendedSocketOptions.TCP_KEEPCOUNT, 306 ExtendedSocketOptions.TCP_KEEPIDLE, 307 ExtendedSocketOptions.TCP_KEEPINTERVAL)); 308 } 309 set.add(StandardSocketOptions.IP_TOS); 310 set = Collections.unmodifiableSet(set); 311 options.put(ServerSocket.class, set); 312 313 // DatagramSocket 314 315 set = new HashSet<>(); 316 set.add(StandardSocketOptions.SO_SNDBUF); 317 set.add(StandardSocketOptions.SO_RCVBUF); 318 set.add(StandardSocketOptions.SO_REUSEADDR); 319 if (reuseportsupported) { 320 set.add(StandardSocketOptions.SO_REUSEPORT); 321 } 322 set.add(StandardSocketOptions.IP_TOS); 323 if (flowsupported) { 324 set.add(ExtendedSocketOptions.SO_FLOW_SLA); 325 } 326 set = Collections.unmodifiableSet(set); 327 options.put(DatagramSocket.class, set); 328 329 // MulticastSocket 330 331 set = new HashSet<>(); 332 set.add(StandardSocketOptions.SO_SNDBUF); 333 set.add(StandardSocketOptions.SO_RCVBUF); 334 set.add(StandardSocketOptions.SO_REUSEADDR); 335 if (reuseportsupported) { 336 set.add(StandardSocketOptions.SO_REUSEPORT); 337 } 338 set.add(StandardSocketOptions.IP_TOS); 339 set.add(StandardSocketOptions.IP_MULTICAST_IF); 340 set.add(StandardSocketOptions.IP_MULTICAST_TTL); 341 set.add(StandardSocketOptions.IP_MULTICAST_LOOP); 342 if (flowsupported) { 343 set.add(ExtendedSocketOptions.SO_FLOW_SLA); 344 } 345 set = Collections.unmodifiableSet(set); 346 options.put(MulticastSocket.class, set); 347 348 return Collections.unmodifiableMap(options); 349 } 350 351 /** 352 * Tells whether TCP_QUICKACK is supported. 353 */ 354 static class QuickAck { 355 356 static final boolean available; 357 358 static { 359 Set<SocketOption<?>> s = new Socket().supportedOptions(); 360 available = s.contains(ExtendedSocketOptions.TCP_QUICKACK); 361 } 362 } 363 364 /** 365 * Tells whether TCP_KEEPALIVE options are supported. 366 */ 367 static class KeepAliveOptions { 368 369 static final boolean AVAILABLE; 370 371 static { 372 Set<SocketOption<?>> s = new Socket().supportedOptions(); 373 AVAILABLE = s.containsAll(Set.of(ExtendedSocketOptions.TCP_KEEPCOUNT, 374 ExtendedSocketOptions.TCP_KEEPIDLE, 375 ExtendedSocketOptions.TCP_KEEPINTERVAL)); 376 } 377 } 378 }