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