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.nio.channels.*; 30 import java.io.IOException; 31 import java.util.Collections; 32 import java.util.HashMap; 33 import java.util.HashSet; 34 import java.util.Map; 35 import java.util.Set; 36 import rdma.ch.RdmaPollSelectorProvider; 37 import rdma.ch.RdmaSocketImpl; 38 import jdk.net.ExtendedSocketOptions.PlatformSocketOptions; 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 public class Sockets { 59 60 private static final Map<Class<?>,Set<SocketOption<?>>> 61 options = optionSets(); 62 63 private Sockets() {} 64 65 /** 66 * Sets the value of a socket option on a {@link java.net.Socket} 67 * 68 * @param s the socket 69 * @param name The socket option 70 * @param value The value of the socket option. May be null for some 71 * options. 72 * 73 * @throws UnsupportedOperationException if the socket does not support 74 * the option. 75 * 76 * @throws IllegalArgumentException if the value is not valid for 77 * the option. 78 * 79 * @throws IOException if an I/O error occurs, or socket is closed. 80 * 81 * @throws SecurityException if a security manager is set and the 82 * caller does not have any required permission. 83 * 84 * @throws NullPointerException if name is null 85 * 86 * @see java.net.StandardSocketOptions 87 */ 88 public static <T> void setOption(Socket s, SocketOption<T> name, T value) throws IOException 89 { 90 s.setOption(name, value); 91 } 92 93 /** 94 * Returns the value of a socket option from a {@link java.net.Socket} 95 * 96 * @param s the socket 97 * @param name The socket option 98 * 99 * @return The value of the socket option. 100 * 101 * @throws UnsupportedOperationException if the socket does not support 102 * the option. 103 * 104 * @throws IOException if an I/O error occurs 105 * 106 * @throws SecurityException if a security manager is set and the 107 * caller does not have any required permission. 108 * 109 * @throws NullPointerException if name is null 110 * 111 * @see java.net.StandardSocketOptions 112 */ 113 public static <T> T getOption(Socket s, SocketOption<T> name) throws IOException 114 { 115 return s.getOption(name); 116 } 117 118 /** 119 * Sets the value of a socket option on a {@link java.net.ServerSocket} 120 * 121 * @param s the socket 122 * @param name The socket option 123 * @param value The value of the socket option. 124 * 125 * @throws UnsupportedOperationException if the socket does not support 126 * the option. 127 * 128 * @throws IllegalArgumentException if the value is not valid for 129 * the option. 130 * 131 * @throws IOException if an I/O error occurs 132 * 133 * @throws NullPointerException if name is null 134 * 135 * @throws SecurityException if a security manager is set and the 136 * caller does not have any required permission. 137 * 138 * @see java.net.StandardSocketOptions 139 */ 140 public static <T> void setOption(ServerSocket s, SocketOption<T> name, T value) throws IOException 141 { 142 s.setOption(name, value); 143 } 144 145 /** 146 * Returns the value of a socket option from a {@link java.net.ServerSocket} 147 * 148 * @param s the socket 149 * @param name The socket option 150 * 151 * @return The value of the socket option. 152 * 153 * @throws UnsupportedOperationException if the socket does not support 154 * the option. 155 * 156 * @throws IOException if an I/O error occurs 157 * 158 * @throws NullPointerException if name is null 159 * 160 * @throws SecurityException if a security manager is set and the 161 * caller does not have any required permission. 162 * 163 * @see java.net.StandardSocketOptions 164 */ 165 public static <T> T getOption(ServerSocket s, SocketOption<T> name) throws IOException 166 { 167 return s.getOption(name); 168 } 169 170 /** 171 * Sets the value of a socket option on a {@link java.net.DatagramSocket} 172 * or {@link java.net.MulticastSocket} 173 * 174 * @param s the socket 175 * @param name The socket option 176 * @param value The value of the socket option. 177 * 178 * @throws UnsupportedOperationException if the socket does not support 179 * the option. 180 * 181 * @throws IllegalArgumentException if the value is not valid for 182 * the option. 183 * 184 * @throws IOException if an I/O error occurs 185 * 186 * @throws NullPointerException if name is null 187 * 188 * @throws SecurityException if a security manager is set and the 189 * caller does not have any required permission. 190 * 191 * @see java.net.StandardSocketOptions 192 */ 193 public static <T> void setOption(DatagramSocket s, SocketOption<T> name, T value) throws IOException 194 { 195 s.setOption(name, value); 196 } 197 198 /** 199 * Returns the value of a socket option from a 200 * {@link java.net.DatagramSocket} or {@link java.net.MulticastSocket} 201 * 202 * @param s the socket 203 * @param name The socket option 204 * 205 * @return The value of the socket option. 206 * 207 * @throws UnsupportedOperationException if the socket does not support 208 * the option. 209 * 210 * @throws IOException if an I/O error occurs 211 * 212 * @throws NullPointerException if name is null 213 * 214 * @throws SecurityException if a security manager is set and the 215 * caller does not have any required permission. 216 * 217 * @see java.net.StandardSocketOptions 218 */ 219 public static <T> T getOption(DatagramSocket s, SocketOption<T> name) throws IOException 220 { 221 return s.getOption(name); 222 } 223 224 /** 225 * Returns a set of {@link java.net.SocketOption}s supported by the 226 * given socket type. This set may include standard options and also 227 * non standard extended options. 228 * 229 * @param socketType the type of java.net socket 230 * 231 * @throws IllegalArgumentException if socketType is not a valid 232 * socket type from the java.net package. 233 */ 234 public static Set<SocketOption<?>> supportedOptions(Class<?> socketType) { 235 Set<SocketOption<?>> set = options.get(socketType); 236 if (set == null) { 237 throw new IllegalArgumentException("unknown socket type"); 238 } 239 return set; 240 } 241 242 private static void checkValueType(Object value, Class<?> type) { 243 if (!type.isAssignableFrom(value.getClass())) { 244 String s = "Found: " + value.getClass().toString() + " Expected: " 245 + type.toString(); 246 throw new IllegalArgumentException(s); 247 } 248 } 249 250 private static volatile boolean checkedReusePort; 251 private static volatile boolean isReusePortAvailable; 252 253 /** 254 * Tells whether SO_REUSEPORT is supported. 255 */ 256 static boolean isReusePortAvailable() { 257 if (!checkedReusePort) { 258 Set<SocketOption<?>> s = new Socket().supportedOptions(); 259 isReusePortAvailable = s.contains(StandardSocketOptions.SO_REUSEPORT); 260 checkedReusePort = true; 261 } 262 return isReusePortAvailable; 263 } 264 265 private static Map<Class<?>,Set<SocketOption<?>>> optionSets() { 266 Map<Class<?>,Set<SocketOption<?>>> options = new HashMap<>(); 267 boolean flowsupported = PlatformSocketOptions.get().flowSupported(); 268 boolean reuseportsupported = isReusePortAvailable(); 269 // Socket 270 271 Set<SocketOption<?>> set = new HashSet<>(); 272 set.add(StandardSocketOptions.SO_KEEPALIVE); 273 set.add(StandardSocketOptions.SO_SNDBUF); 274 set.add(StandardSocketOptions.SO_RCVBUF); 275 set.add(StandardSocketOptions.SO_REUSEADDR); 276 if (reuseportsupported) { 277 set.add(StandardSocketOptions.SO_REUSEPORT); 278 } 279 set.add(StandardSocketOptions.SO_LINGER); 280 set.add(StandardSocketOptions.IP_TOS); 281 set.add(StandardSocketOptions.TCP_NODELAY); 282 if (flowsupported) { 283 set.add(ExtendedSocketOptions.SO_FLOW_SLA); 284 } 285 if (QuickAck.available) { 286 set.add(ExtendedSocketOptions.TCP_QUICKACK); 287 } 288 if (KeepAliveOptions.AVAILABLE) { 289 set.addAll(Set.of(ExtendedSocketOptions.TCP_KEEPCOUNT, 290 ExtendedSocketOptions.TCP_KEEPIDLE, 291 ExtendedSocketOptions.TCP_KEEPINTERVAL)); 292 } 293 set = Collections.unmodifiableSet(set); 294 options.put(Socket.class, set); 295 296 // ServerSocket 297 298 set = new HashSet<>(); 299 set.add(StandardSocketOptions.SO_RCVBUF); 300 set.add(StandardSocketOptions.SO_REUSEADDR); 301 if (reuseportsupported) { 302 set.add(StandardSocketOptions.SO_REUSEPORT); 303 } 304 if (QuickAck.available) { 305 set.add(ExtendedSocketOptions.TCP_QUICKACK); 306 } 307 if (KeepAliveOptions.AVAILABLE) { 308 set.addAll(Set.of(ExtendedSocketOptions.TCP_KEEPCOUNT, 309 ExtendedSocketOptions.TCP_KEEPIDLE, 310 ExtendedSocketOptions.TCP_KEEPINTERVAL)); 311 } 312 set.add(StandardSocketOptions.IP_TOS); 313 set = Collections.unmodifiableSet(set); 314 options.put(ServerSocket.class, set); 315 316 // DatagramSocket 317 318 set = new HashSet<>(); 319 set.add(StandardSocketOptions.SO_SNDBUF); 320 set.add(StandardSocketOptions.SO_RCVBUF); 321 set.add(StandardSocketOptions.SO_REUSEADDR); 322 if (reuseportsupported) { 323 set.add(StandardSocketOptions.SO_REUSEPORT); 324 } 325 set.add(StandardSocketOptions.IP_TOS); 326 if (flowsupported) { 327 set.add(ExtendedSocketOptions.SO_FLOW_SLA); 328 } 329 set = Collections.unmodifiableSet(set); 330 options.put(DatagramSocket.class, set); 331 332 // MulticastSocket 333 334 set = new HashSet<>(); 335 set.add(StandardSocketOptions.SO_SNDBUF); 336 set.add(StandardSocketOptions.SO_RCVBUF); 337 set.add(StandardSocketOptions.SO_REUSEADDR); 338 if (reuseportsupported) { 339 set.add(StandardSocketOptions.SO_REUSEPORT); 340 } 341 set.add(StandardSocketOptions.IP_TOS); 342 set.add(StandardSocketOptions.IP_MULTICAST_IF); 343 set.add(StandardSocketOptions.IP_MULTICAST_TTL); 344 set.add(StandardSocketOptions.IP_MULTICAST_LOOP); 345 if (flowsupported) { 346 set.add(ExtendedSocketOptions.SO_FLOW_SLA); 347 } 348 set = Collections.unmodifiableSet(set); 349 options.put(MulticastSocket.class, set); 350 351 return Collections.unmodifiableMap(options); 352 } 353 354 /** 355 * Tells whether TCP_QUICKACK is supported. 356 */ 357 static class QuickAck { 358 359 static final boolean available; 360 361 static { 362 Set<SocketOption<?>> s = new Socket().supportedOptions(); 363 available = s.contains(ExtendedSocketOptions.TCP_QUICKACK); 364 } 365 } 366 367 /** 368 * Tells whether TCP_KEEPALIVE options are supported. 369 */ 370 static class KeepAliveOptions { 371 372 static final boolean AVAILABLE; 373 374 static { 375 Set<SocketOption<?>> s = new Socket().supportedOptions(); 376 AVAILABLE = s.containsAll(Set.of(ExtendedSocketOptions.TCP_KEEPCOUNT, 377 ExtendedSocketOptions.TCP_KEEPIDLE, 378 ExtendedSocketOptions.TCP_KEEPINTERVAL)); 379 } 380 } 381 382 /** 383 * Creates an unconnected RDMA socket 384 */ 385 public static Socket openRdmaSocket() throws IOException { 386 SocketImpl impl = new RdmaSocketImpl(); 387 return new Socket(impl) { }; 388 } 389 390 /** 391 * Creates an unbound RDMA server socket 392 */ 393 public static ServerSocket openRdmaServerSocket() throws IOException { 394 SocketImpl impl = new RdmaSocketImpl(); 395 ServerSocket ss = new ServerSocket(impl) { 396 public Socket accept() throws IOException { 397 if (isClosed()) 398 throw new SocketException("Socket is closed"); 399 if (!isBound()) 400 throw new SocketException("Socket is not bound yet"); 401 402 Socket s = openRdmaSocket(); 403 implAccept(s); 404 return s; 405 } 406 }; 407 return ss; 408 } 409 410 /** 411 * Opens a RDMA socket channel. 412 * 413 * @return A new RDMA socket channel 414 * 415 * @throws IOException 416 * If an I/O error occurs 417 */ 418 public static SocketChannel openRdmaSocketChannel() throws IOException { 419 return RdmaPollSelectorProvider.provider().openSocketChannel(); 420 } 421 422 /** 423 * Opens a RDMA server-socket channel. 424 * 425 * <p> The new channel is created by invoking the {@link 426 * java.nio.channels.spi.SelectorProvider#openServerSocketChannel 427 * openServerSocketChannel} method of the system-wide default {@link 428 * java.nio.channels.spi.SelectorProvider} object. 429 * 430 * <p> The new channel's socket is initially unbound; it must be bound to a 431 * specific address via one of its socket's {@link 432 * java.net.ServerSocket#bind(SocketAddress) bind} methods before 433 * connections can be accepted. </p> 434 * 435 * @return A new RDMA server socket channel 436 * 437 * @throws IOException 438 * If an I/O error occurs 439 */ 440 public static ServerSocketChannel openRdmaServerSocketChannel() 441 throws IOException { 442 return RdmaPollSelectorProvider.provider().openServerSocketChannel(); 443 } 444 445 /** 446 * Opens a RDMA selector. 447 * 448 * @return A new RDMA selector 449 * 450 * @throws IOException 451 * If an I/O error occurs 452 */ 453 public static Selector openRdmaSelector() throws IOException { 454 return RdmaPollSelectorProvider.provider().openSelector(); 455 } 456 }