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 }