1 /* 2 * Copyright (c) 2014, 2019, 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test 26 * @bug 8036979 8072384 8044773 27 * @library /test/lib 28 * @requires !vm.graal.enabled 29 * @run main/othervm -Xcheck:jni OptionsTest 30 * @run main/othervm -Xcheck:jni -Djava.net.preferIPv4Stack=true OptionsTest 31 * @run main/othervm --limit-modules=java.base OptionsTest 32 */ 33 34 import java.lang.reflect.Method; 35 import java.net.*; 36 import java.util.*; 37 import jdk.test.lib.net.IPSupport; 38 39 public class OptionsTest { 40 41 static class Test<T> { 42 final SocketOption<T> option; 43 final T value; 44 Test(SocketOption<T> option, T value) { 45 this.option = option; 46 this.value = value; 47 } 48 static <T> Test<T> create(SocketOption<T> option, T value) { 49 return new Test<T>(option, value); 50 } 51 52 } 53 54 // The tests set the option using the new API, read back the set value 55 // which could be diferent, and then use the legacy get API to check 56 // these values are the same 57 58 static Test<?>[] socketTests = new Test<?>[] { 59 Test.create(StandardSocketOptions.SO_KEEPALIVE, Boolean.TRUE), 60 Test.create(StandardSocketOptions.SO_SNDBUF, Integer.valueOf(10 * 100)), 61 Test.create(StandardSocketOptions.SO_RCVBUF, Integer.valueOf(8 * 100)), 62 Test.create(StandardSocketOptions.SO_REUSEADDR, Boolean.FALSE), 63 Test.create(StandardSocketOptions.SO_REUSEPORT, Boolean.FALSE), 64 Test.create(StandardSocketOptions.SO_LINGER, Integer.valueOf(80)), 65 Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(0)), // lower-bound 66 Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(100)), 67 Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(255)) //upper-bound 68 }; 69 70 static Test<?>[] serverSocketTests = new Test<?>[] { 71 Test.create(StandardSocketOptions.SO_RCVBUF, Integer.valueOf(8 * 100)), 72 Test.create(StandardSocketOptions.SO_REUSEADDR, Boolean.FALSE), 73 Test.create(StandardSocketOptions.SO_REUSEPORT, Boolean.FALSE), 74 Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(0)), // lower-bound 75 Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(100)), 76 Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(255)) //upper-bound 77 }; 78 79 static Test<?>[] datagramSocketTests = new Test<?>[] { 80 Test.create(StandardSocketOptions.SO_SNDBUF, Integer.valueOf(10 * 100)), 81 Test.create(StandardSocketOptions.SO_RCVBUF, Integer.valueOf(8 * 100)), 82 Test.create(StandardSocketOptions.SO_REUSEADDR, Boolean.FALSE), 83 Test.create(StandardSocketOptions.SO_REUSEPORT, Boolean.FALSE), 84 Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(0)), // lower-bound 85 Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(100)), 86 Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(255)) //upper-bound 87 }; 88 89 static Test<?>[] multicastSocketTests = new Test<?>[] { 90 Test.create(StandardSocketOptions.IP_MULTICAST_IF, getNetworkInterface()), 91 Test.create(StandardSocketOptions.IP_MULTICAST_TTL, Integer.valueOf(0)), // lower-bound 92 Test.create(StandardSocketOptions.IP_MULTICAST_TTL, Integer.valueOf(10)), 93 Test.create(StandardSocketOptions.IP_MULTICAST_TTL, Integer.valueOf(255)), //upper-bound 94 Test.create(StandardSocketOptions.IP_MULTICAST_LOOP, Boolean.TRUE) 95 }; 96 97 static NetworkInterface getNetworkInterface() { 98 try { 99 Enumeration<NetworkInterface> nifs = NetworkInterface.getNetworkInterfaces(); 100 while (nifs.hasMoreElements()) { 101 NetworkInterface ni = nifs.nextElement(); 102 if (ni.supportsMulticast()) { 103 return ni; 104 } 105 } 106 } catch (Exception e) { 107 } 108 return null; 109 } 110 111 static boolean okayToTest(Socket s, SocketOption<?> option) { 112 if (option == StandardSocketOptions.SO_REUSEPORT) { 113 // skip SO_REUSEPORT if option is not supported 114 return s.supportedOptions().contains(StandardSocketOptions.SO_REUSEPORT); 115 } 116 if (option == StandardSocketOptions.IP_TOS && s.isConnected()) { 117 // skip IP_TOS if connected 118 return false; 119 } 120 return true; 121 } 122 123 static <T> void testEqual(SocketOption<T> option, T value1, T value2) { 124 if (!value1.equals(value2)) { 125 throw new RuntimeException("Test of " + option.name() + " failed: " 126 + value1 + " != " + value2); 127 } 128 } 129 130 static <T> void test(Socket s, Test<T> test) throws Exception { 131 SocketOption<T> option = test.option; 132 s.setOption(option, test.value); 133 T value1 = s.getOption(test.option); 134 T value2 = (T) legacyGetOption(Socket.class, s, test.option); 135 testEqual(option, value1, value2); 136 } 137 138 static <T> void test(ServerSocket ss, Test<T> test) throws Exception { 139 SocketOption<T> option = test.option; 140 ss.setOption(option, test.value); 141 T value1 = ss.getOption(test.option); 142 T value2 = (T) legacyGetOption(ServerSocket.class, ss, test.option); 143 testEqual(option, value1, value2); 144 } 145 146 static <T> void test(DatagramSocket ds, Test<T> test) throws Exception { 147 SocketOption<T> option = test.option; 148 ds.setOption(option, test.value); 149 T value1 = ds.getOption(test.option); 150 T value2 = (T) legacyGetOption(ds.getClass(), ds, test.option); 151 testEqual(option, value1, value2); 152 } 153 154 @SuppressWarnings("try") 155 static void doSocketTests() throws Exception { 156 // unconnected socket 157 try (Socket s = new Socket()) { 158 for (Test<?> test : socketTests) { 159 if (okayToTest(s, test.option)) { 160 test(s, test); 161 } 162 } 163 } 164 165 // connected socket 166 try (ServerSocket ss = new ServerSocket()) { 167 var loopback = InetAddress.getLoopbackAddress(); 168 ss.bind(new InetSocketAddress(loopback, 0)); 169 try (Socket s1 = new Socket()) { 170 s1.connect(ss.getLocalSocketAddress()); 171 try (Socket s2 = ss.accept()) { 172 for (Test<?> test : socketTests) { 173 if (okayToTest(s1, test.option)) { 174 test(s1, test); 175 } 176 } 177 } 178 } 179 } 180 } 181 182 static void doServerSocketTests() throws Exception { 183 try (ServerSocket ss = new ServerSocket(0)) { 184 Set<SocketOption<?>> options = ss.supportedOptions(); 185 boolean reuseport = options.contains(StandardSocketOptions.SO_REUSEPORT); 186 for (Test<?> test : serverSocketTests) { 187 if (!(test.option == StandardSocketOptions.SO_REUSEPORT && !reuseport)) { 188 test(ss, test); 189 } 190 } 191 } 192 } 193 194 static void doDatagramSocketTests() throws Exception { 195 try (DatagramSocket ds = new DatagramSocket(0)) { 196 Set<SocketOption<?>> options = ds.supportedOptions(); 197 boolean reuseport = options.contains(StandardSocketOptions.SO_REUSEPORT); 198 for (Test<?> test : datagramSocketTests) { 199 if (!(test.option == StandardSocketOptions.SO_REUSEPORT && !reuseport)) { 200 test(ds, test); 201 } 202 } 203 } 204 } 205 206 static void doMulticastSocketTests() throws Exception { 207 try (MulticastSocket ms = new MulticastSocket(0)) { 208 for (Test<?> test : multicastSocketTests) { 209 test(ms, test); 210 } 211 } 212 } 213 214 static Object legacyGetOption(Class<?> type, Object s, Object option) throws Exception { 215 if (type.equals(Socket.class)) { 216 Socket socket = (Socket)s; 217 Set<SocketOption<?>> options = socket.supportedOptions(); 218 boolean reuseport = options.contains(StandardSocketOptions.SO_REUSEPORT); 219 220 if (option.equals(StandardSocketOptions.SO_KEEPALIVE)) { 221 return Boolean.valueOf(socket.getKeepAlive()); 222 } else if (option.equals(StandardSocketOptions.SO_SNDBUF)) { 223 return Integer.valueOf(socket.getSendBufferSize()); 224 } else if (option.equals(StandardSocketOptions.SO_RCVBUF)) { 225 return Integer.valueOf(socket.getReceiveBufferSize()); 226 } else if (option.equals(StandardSocketOptions.SO_REUSEADDR)) { 227 return Boolean.valueOf(socket.getReuseAddress()); 228 } else if (option.equals(StandardSocketOptions.SO_REUSEPORT) && reuseport) { 229 return Boolean.valueOf(socket.getOption(StandardSocketOptions.SO_REUSEPORT)); 230 } else if (option.equals(StandardSocketOptions.SO_LINGER)) { 231 return Integer.valueOf(socket.getSoLinger()); 232 } else if (option.equals(StandardSocketOptions.IP_TOS)) { 233 return Integer.valueOf(socket.getTrafficClass()); 234 } else if (option.equals(StandardSocketOptions.TCP_NODELAY)) { 235 return Boolean.valueOf(socket.getTcpNoDelay()); 236 } else { 237 throw new RuntimeException("unexecpted socket option"); 238 } 239 } else if (type.equals(ServerSocket.class)) { 240 ServerSocket socket = (ServerSocket)s; 241 Set<SocketOption<?>> options = socket.supportedOptions(); 242 boolean reuseport = options.contains(StandardSocketOptions.SO_REUSEPORT); 243 244 if (option.equals(StandardSocketOptions.SO_RCVBUF)) { 245 return Integer.valueOf(socket.getReceiveBufferSize()); 246 } else if (option.equals(StandardSocketOptions.SO_REUSEADDR)) { 247 return Boolean.valueOf(socket.getReuseAddress()); 248 } else if (option.equals(StandardSocketOptions.SO_REUSEPORT) && reuseport) { 249 return Boolean.valueOf(socket.getOption(StandardSocketOptions.SO_REUSEPORT)); 250 } else if (option.equals(StandardSocketOptions.IP_TOS)) { 251 return getServerSocketTrafficClass(socket); 252 } else { 253 throw new RuntimeException("unexecpted socket option"); 254 } 255 } else if (type.equals(DatagramSocket.class)) { 256 DatagramSocket socket = (DatagramSocket)s; 257 Set<SocketOption<?>> options = socket.supportedOptions(); 258 boolean reuseport = options.contains(StandardSocketOptions.SO_REUSEPORT); 259 260 if (option.equals(StandardSocketOptions.SO_SNDBUF)) { 261 return Integer.valueOf(socket.getSendBufferSize()); 262 } else if (option.equals(StandardSocketOptions.SO_RCVBUF)) { 263 return Integer.valueOf(socket.getReceiveBufferSize()); 264 } else if (option.equals(StandardSocketOptions.SO_REUSEADDR)) { 265 return Boolean.valueOf(socket.getReuseAddress()); 266 } else if (option.equals(StandardSocketOptions.SO_REUSEPORT) && reuseport) { 267 return Boolean.valueOf(socket.getOption(StandardSocketOptions.SO_REUSEPORT)); 268 } else if (option.equals(StandardSocketOptions.IP_TOS)) { 269 return Integer.valueOf(socket.getTrafficClass()); 270 } else { 271 throw new RuntimeException("unexecpted socket option"); 272 } 273 274 } else if (type.equals(MulticastSocket.class)) { 275 MulticastSocket socket = (MulticastSocket)s; 276 Set<SocketOption<?>> options = socket.supportedOptions(); 277 boolean reuseport = options.contains(StandardSocketOptions.SO_REUSEPORT); 278 279 if (option.equals(StandardSocketOptions.SO_SNDBUF)) { 280 return Integer.valueOf(socket.getSendBufferSize()); 281 } else if (option.equals(StandardSocketOptions.SO_RCVBUF)) { 282 return Integer.valueOf(socket.getReceiveBufferSize()); 283 } else if (option.equals(StandardSocketOptions.SO_REUSEADDR)) { 284 return Boolean.valueOf(socket.getReuseAddress()); 285 } else if (option.equals(StandardSocketOptions.SO_REUSEPORT) && reuseport) { 286 return Boolean.valueOf(socket.getOption(StandardSocketOptions.SO_REUSEPORT)); 287 } else if (option.equals(StandardSocketOptions.IP_TOS)) { 288 return Integer.valueOf(socket.getTrafficClass()); 289 } else if (option.equals(StandardSocketOptions.IP_MULTICAST_IF)) { 290 return socket.getNetworkInterface(); 291 } else if (option.equals(StandardSocketOptions.IP_MULTICAST_TTL)) { 292 return Integer.valueOf(socket.getTimeToLive()); 293 } else if (option.equals(StandardSocketOptions.IP_MULTICAST_LOOP)) { 294 return Boolean.valueOf(socket.getLoopbackMode()); 295 } else { 296 throw new RuntimeException("unexecpted socket option"); 297 } 298 } 299 throw new RuntimeException("unexecpted socket type"); 300 } 301 302 public static void main(String args[]) throws Exception { 303 IPSupport.throwSkippedExceptionIfNonOperational(); 304 doSocketTests(); 305 doServerSocketTests(); 306 doDatagramSocketTests(); 307 doMulticastSocketTests(); 308 } 309 310 // Reflectively access jdk.net.Sockets.getOption so that the test can run 311 // without the jdk.net module. 312 static Object getServerSocketTrafficClass(ServerSocket ss) throws Exception { 313 try { 314 Class<?> c = Class.forName("jdk.net.Sockets"); 315 Method m = c.getDeclaredMethod("getOption", ServerSocket.class, SocketOption.class); 316 return m.invoke(null, ss, StandardSocketOptions.IP_TOS); 317 } catch (ClassNotFoundException e) { 318 // Ok, jdk.net module not present, just fall back 319 System.out.println("jdk.net module not present, falling back."); 320 return Integer.valueOf(ss.getOption(StandardSocketOptions.IP_TOS)); 321 } catch (ReflectiveOperationException e) { 322 throw new AssertionError(e); 323 } 324 } 325 }