1 /* 2 * Copyright (c) 2014, 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.io.FileDescriptor; 29 import java.net.SocketException; 30 import java.net.SocketOption; 31 import java.security.AccessController; 32 import java.security.PrivilegedAction; 33 import java.util.Collections; 34 import java.util.HashSet; 35 import java.util.Set; 36 import jdk.internal.misc.JavaIOFileDescriptorAccess; 37 import jdk.internal.misc.SharedSecrets; 38 39 /** 40 * Defines extended socket options, beyond those defined in 41 * {@link java.net.StandardSocketOptions}. These options may be platform 42 * specific. 43 * 44 * @since 1.8 45 */ 46 public final class ExtendedSocketOptions { 47 48 private static class ExtSocketOption<T> implements SocketOption<T> { 49 private final String name; 50 private final Class<T> type; 51 ExtSocketOption(String name, Class<T> type) { 52 this.name = name; 53 this.type = type; 54 } 55 @Override public String name() { return name; } 56 @Override public Class<T> type() { return type; } 57 @Override public String toString() { return name; } 58 } 59 60 private ExtendedSocketOptions() { } 61 62 /** 63 * Service level properties. When a security manager is installed, 64 * setting or getting this option requires a {@link NetworkPermission} 65 * {@code ("setOption.SO_FLOW_SLA")} or {@code "getOption.SO_FLOW_SLA"} 66 * respectively. 67 */ 68 public static final SocketOption<SocketFlow> SO_FLOW_SLA = new 69 ExtSocketOption<SocketFlow>("SO_FLOW_SLA", SocketFlow.class); 70 71 /** 72 * Disable Delayed Acknowledgements. 73 * 74 * <p> 75 * This socket option can be used to reduce or disable delayed 76 * acknowledgments (ACKs). When {@code TCP_QUICKACK} is enabled, ACKs are 77 * sent immediately, rather than delayed if needed in accordance to normal 78 * TCP operation. This option is not permanent, it only enables a switch to 79 * or from {@code TCP_QUICKACK} mode. Subsequent operations of the TCP 80 * protocol will once again disable/enable {@code TCP_QUICKACK} mode 81 * depending on internal protocol processing and factors such as delayed ACK 82 * timeouts occurring and data transfer, therefore this option needs to be 83 * set with {@code setOption} after each operation of TCP on a given socket. 84 * 85 * <p> 86 * The value of this socket option is a {@code Boolean} that represents 87 * whether the option is enabled or disabled. The socket option is specific 88 * to stream-oriented sockets using the TCP/IP protocol. The exact semantics 89 * of this socket option are socket type and system dependent. 90 * 91 * @since 10 92 */ 93 public static final SocketOption<Boolean> TCP_QUICKACK = 94 new ExtSocketOption<Boolean>("TCP_QUICKACK", Boolean.class); 95 96 /** 97 * Keep-Alive idle time. 98 * 99 * <p> 100 * The value of this socket option is an {@code Integer} that is the number 101 * of seconds of idle time before keep-alive initiates a probe. The socket 102 * option is specific to stream-oriented sockets using the TCP/IP protocol. 103 * The exact semantics of this socket option are system dependent. 104 * 105 * <p> 106 * When the {@link java.net.StandardSocketOptions#SO_KEEPALIVE 107 * SO_KEEPALIVE} option is enabled, TCP probes a connection that has been 108 * idle for some amount of time. The default value for this idle period is 109 * system dependent, but is typically 2 hours. The {@code TCP_KEEPIDLE} 110 * option can be used to affect this value for a given socket. 111 * 112 * @since 11 113 */ 114 public static final SocketOption<Integer> TCP_KEEPIDLE 115 = new ExtSocketOption<Integer>("TCP_KEEPIDLE", Integer.class); 116 117 /** 118 * Keep-Alive retransmission interval time. 119 * 120 * <p> 121 * The value of this socket option is an {@code Integer} that is the number 122 * of seconds to wait before retransmitting a keep-alive probe. The socket 123 * option is specific to stream-oriented sockets using the TCP/IP protocol. 124 * The exact semantics of this socket option are system dependent. 125 * 126 * <p> 127 * When the {@link java.net.StandardSocketOptions#SO_KEEPALIVE 128 * SO_KEEPALIVE} option is enabled, TCP probes a connection that has been 129 * idle for some amount of time. If the remote system does not respond to a 130 * keep-alive probe, TCP retransmits the probe after some amount of time. 131 * The default value for this retransmission interval is system dependent, 132 * but is typically 75 seconds. The {@code TCP_KEEPINTERVAL} option can be 133 * used to affect this value for a given socket. 134 * 135 * @since 11 136 */ 137 public static final SocketOption<Integer> TCP_KEEPINTERVAL 138 = new ExtSocketOption<Integer>("TCP_KEEPINTERVAL", Integer.class); 139 140 /** 141 * Keep-Alive retransmission maximum limit. 142 * 143 * <p> 144 * The value of this socket option is an {@code Integer} that is the maximum 145 * number of keep-alive probes to be sent. The socket option is specific to 146 * stream-oriented sockets using the TCP/IP protocol. The exact semantics of 147 * this socket option are system dependent. 148 * 149 * <p> 150 * When the {@link java.net.StandardSocketOptions#SO_KEEPALIVE 151 * SO_KEEPALIVE} option is enabled, TCP probes a connection that has been 152 * idle for some amount of time. If the remote system does not respond to a 153 * keep-alive probe, TCP retransmits the probe a certain number of times 154 * before a connection is considered to be broken. The default value for 155 * this keep-alive probe retransmit limit is system dependent, but is 156 * typically 8. The {@code TCP_KEEPCOUNT} option can be used to affect this 157 * value for a given socket. 158 * 159 * @since 11 160 */ 161 public static final SocketOption<Integer> TCP_KEEPCOUNT 162 = new ExtSocketOption<Integer>("TCP_KEEPCOUNT", Integer.class); 163 164 private static final PlatformSocketOptions platformSocketOptions = 165 PlatformSocketOptions.get(); 166 167 private static final boolean flowSupported = 168 platformSocketOptions.flowSupported(); 169 private static final boolean quickAckSupported = 170 platformSocketOptions.quickAckSupported(); 171 private static final boolean keepAliveOptSupported = 172 platformSocketOptions.keepAliveOptionsSupported(); 173 private static final Set<SocketOption<?>> extendedOptions = options(); 174 175 static Set<SocketOption<?>> options() { 176 Set<SocketOption<?>> options = new HashSet<>(); 177 if (flowSupported) { 178 options.add(SO_FLOW_SLA); 179 } 180 if (quickAckSupported) { 181 options.add(TCP_QUICKACK); 182 } 183 if (keepAliveOptSupported) { 184 options.addAll(Set.of(TCP_KEEPCOUNT, TCP_KEEPIDLE, TCP_KEEPINTERVAL)); 185 } 186 return Collections.unmodifiableSet(options); 187 } 188 189 static { 190 // Registers the extended socket options with the base module. 191 sun.net.ext.ExtendedSocketOptions.register( 192 new sun.net.ext.ExtendedSocketOptions(extendedOptions) { 193 194 @Override 195 public void setOption(FileDescriptor fd, 196 SocketOption<?> option, 197 Object value) 198 throws SocketException 199 { 200 SecurityManager sm = System.getSecurityManager(); 201 if (sm != null) 202 sm.checkPermission(new NetworkPermission("setOption." + option.name())); 203 204 if (fd == null || !fd.valid()) 205 throw new SocketException("socket closed"); 206 207 if (option == SO_FLOW_SLA) { 208 assert flowSupported; 209 SocketFlow flow = checkValueType(value, option.type()); 210 setFlowOption(fd, flow); 211 } else if (option == TCP_QUICKACK) { 212 setQuickAckOption(fd, (boolean) value); 213 } else if (option == TCP_KEEPCOUNT) { 214 setTcpkeepAliveProbes(fd, (Integer) value); 215 } else if (option == TCP_KEEPIDLE) { 216 setTcpKeepAliveTime(fd, (Integer) value); 217 } else if (option == TCP_KEEPINTERVAL) { 218 setTcpKeepAliveIntvl(fd, (Integer) value); 219 } else { 220 throw new InternalError("Unexpected option " + option); 221 } 222 } 223 224 @Override 225 public Object getOption(FileDescriptor fd, 226 SocketOption<?> option) 227 throws SocketException 228 { 229 SecurityManager sm = System.getSecurityManager(); 230 if (sm != null) 231 sm.checkPermission(new NetworkPermission("getOption." + option.name())); 232 233 if (fd == null || !fd.valid()) 234 throw new SocketException("socket closed"); 235 236 if (option == SO_FLOW_SLA) { 237 assert flowSupported; 238 SocketFlow flow = SocketFlow.create(); 239 getFlowOption(fd, flow); 240 return flow; 241 } else if (option == TCP_QUICKACK) { 242 return getQuickAckOption(fd); 243 } else if (option == TCP_KEEPCOUNT) { 244 return getTcpkeepAliveProbes(fd); 245 } else if (option == TCP_KEEPIDLE) { 246 return getTcpKeepAliveTime(fd); 247 } else if (option == TCP_KEEPINTERVAL) { 248 return getTcpKeepAliveIntvl(fd); 249 } else { 250 throw new InternalError("Unexpected option " + option); 251 } 252 } 253 }); 254 } 255 256 @SuppressWarnings("unchecked") 257 private static <T> T checkValueType(Object value, Class<?> type) { 258 if (!type.isAssignableFrom(value.getClass())) { 259 String s = "Found: " + value.getClass() + ", Expected: " + type; 260 throw new IllegalArgumentException(s); 261 } 262 return (T) value; 263 } 264 265 private static final JavaIOFileDescriptorAccess fdAccess = 266 SharedSecrets.getJavaIOFileDescriptorAccess(); 267 268 private static void setFlowOption(FileDescriptor fd, SocketFlow f) 269 throws SocketException 270 { 271 int status = platformSocketOptions.setFlowOption(fdAccess.get(fd), 272 f.priority(), 273 f.bandwidth()); 274 f.status(status); // augment the given flow with the status 275 } 276 277 private static void getFlowOption(FileDescriptor fd, SocketFlow f) 278 throws SocketException { 279 int status = platformSocketOptions.getFlowOption(fdAccess.get(fd), f); 280 f.status(status); // augment the given flow with the status 281 } 282 283 private static void setQuickAckOption(FileDescriptor fd, boolean enable) 284 throws SocketException { 285 platformSocketOptions.setQuickAck(fdAccess.get(fd), enable); 286 } 287 288 private static Object getQuickAckOption(FileDescriptor fd) 289 throws SocketException { 290 return platformSocketOptions.getQuickAck(fdAccess.get(fd)); 291 } 292 293 private static void setTcpkeepAliveProbes(FileDescriptor fd, int value) 294 throws SocketException { 295 platformSocketOptions.setTcpkeepAliveProbes(fdAccess.get(fd), value); 296 } 297 298 private static void setTcpKeepAliveTime(FileDescriptor fd, int value) 299 throws SocketException { 300 platformSocketOptions.setTcpKeepAliveTime(fdAccess.get(fd), value); 301 } 302 303 private static void setTcpKeepAliveIntvl(FileDescriptor fd, int value) 304 throws SocketException { 305 platformSocketOptions.setTcpKeepAliveIntvl(fdAccess.get(fd), value); 306 } 307 308 private static int getTcpkeepAliveProbes(FileDescriptor fd) throws SocketException { 309 return platformSocketOptions.getTcpkeepAliveProbes(fdAccess.get(fd)); 310 } 311 312 private static int getTcpKeepAliveTime(FileDescriptor fd) throws SocketException { 313 return platformSocketOptions.getTcpKeepAliveTime(fdAccess.get(fd)); 314 } 315 316 private static int getTcpKeepAliveIntvl(FileDescriptor fd) throws SocketException { 317 return platformSocketOptions.getTcpKeepAliveIntvl(fdAccess.get(fd)); 318 } 319 320 static class PlatformSocketOptions { 321 322 protected PlatformSocketOptions() {} 323 324 @SuppressWarnings("unchecked") 325 private static PlatformSocketOptions newInstance(String cn) { 326 Class<PlatformSocketOptions> c; 327 try { 328 c = (Class<PlatformSocketOptions>)Class.forName(cn); 329 return c.getConstructor(new Class<?>[] { }).newInstance(); 330 } catch (ReflectiveOperationException x) { 331 throw new AssertionError(x); 332 } 333 } 334 335 private static PlatformSocketOptions create() { 336 String osname = AccessController.doPrivileged( 337 new PrivilegedAction<String>() { 338 public String run() { 339 return System.getProperty("os.name"); 340 } 341 }); 342 if ("SunOS".equals(osname)) { 343 return newInstance("jdk.net.SolarisSocketOptions"); 344 } else if ("Linux".equals(osname)) { 345 return newInstance("jdk.net.LinuxSocketOptions"); 346 } else if (osname.startsWith("Mac")) { 347 return newInstance("jdk.net.MacOSXSocketOptions"); 348 } else { 349 return new PlatformSocketOptions(); 350 } 351 } 352 353 private static final PlatformSocketOptions instance = create(); 354 355 static PlatformSocketOptions get() { 356 return instance; 357 } 358 359 int setFlowOption(int fd, int priority, long bandwidth) 360 throws SocketException 361 { 362 throw new UnsupportedOperationException("unsupported socket option"); 363 } 364 365 int getFlowOption(int fd, SocketFlow f) throws SocketException { 366 throw new UnsupportedOperationException("unsupported socket option"); 367 } 368 369 boolean flowSupported() { 370 return false; 371 } 372 373 void setQuickAck(int fd, boolean on) throws SocketException { 374 throw new UnsupportedOperationException("unsupported TCP_QUICKACK option"); 375 } 376 377 boolean getQuickAck(int fd) throws SocketException { 378 throw new UnsupportedOperationException("unsupported TCP_QUICKACK option"); 379 } 380 381 boolean quickAckSupported() { 382 return false; 383 } 384 385 boolean keepAliveOptionsSupported() { 386 return false; 387 } 388 389 void setTcpkeepAliveProbes(int fd, final int value) throws SocketException { 390 throw new UnsupportedOperationException("unsupported TCP_KEEPCNT option"); 391 } 392 393 void setTcpKeepAliveTime(int fd, final int value) throws SocketException { 394 throw new UnsupportedOperationException("unsupported TCP_KEEPIDLE option"); 395 } 396 397 void setTcpKeepAliveIntvl(int fd, final int value) throws SocketException { 398 throw new UnsupportedOperationException("unsupported TCP_KEEPINTVL option"); 399 } 400 401 int getTcpkeepAliveProbes(int fd) throws SocketException { 402 throw new UnsupportedOperationException("unsupported TCP_KEEPCNT option"); 403 } 404 405 int getTcpKeepAliveTime(int fd) throws SocketException { 406 throw new UnsupportedOperationException("unsupported TCP_KEEPIDLE option"); 407 } 408 409 int getTcpKeepAliveIntvl(int fd) throws SocketException { 410 throw new UnsupportedOperationException("unsupported TCP_KEEPINTVL option"); 411 } 412 } 413 }