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 8225214
  27  * @library /test/lib
  28  * @requires !vm.graal.enabled
  29  * @run main/othervm -Xcheck:jni OptionsTest
  30  * @run main/othervm -Djdk.net.usePlainSocketImpl OptionsTest
  31  * @run main/othervm -Xcheck:jni -Djava.net.preferIPv4Stack=true OptionsTest
  32  * @run main/othervm --limit-modules=java.base OptionsTest
  33  */
  34 
  35 import java.lang.reflect.Method;
  36 import java.net.*;
  37 import java.util.*;
  38 import jdk.test.lib.net.IPSupport;
  39 
  40 public class OptionsTest {
  41 
  42     static class Test<T> {
  43         final SocketOption<T> option;
  44         final T value;
  45         Test(SocketOption<T> option, T value) {
  46             this.option = option;
  47             this.value = value;
  48         }
  49         static <T> Test<T> create(SocketOption<T> option, T value) {
  50             return new Test<T>(option, value);
  51         }
  52 
  53     }
  54 
  55     // The tests set the option using the new API, read back the set value
  56     // which could be different, and then use the legacy get API to check
  57     // these values are the same
  58 
  59     static Test<?>[] socketTests = new Test<?>[] {
  60         Test.create(StandardSocketOptions.SO_KEEPALIVE, Boolean.TRUE),
  61         Test.create(StandardSocketOptions.SO_SNDBUF, Integer.valueOf(10 * 100)),
  62         Test.create(StandardSocketOptions.SO_RCVBUF, Integer.valueOf(8 * 100)),
  63         Test.create(StandardSocketOptions.SO_REUSEADDR, Boolean.FALSE),
  64         Test.create(StandardSocketOptions.SO_REUSEPORT, Boolean.FALSE),
  65         Test.create(StandardSocketOptions.SO_LINGER, Integer.valueOf(-1)),
  66         Test.create(StandardSocketOptions.SO_LINGER, Integer.valueOf(0)),
  67         Test.create(StandardSocketOptions.SO_LINGER, Integer.valueOf(80)),
  68         Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(0)),  // lower-bound
  69         Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(100)),
  70         Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(255))  //upper-bound
  71     };
  72 
  73     static Test<?>[] serverSocketTests = new Test<?>[] {
  74         Test.create(StandardSocketOptions.SO_RCVBUF, Integer.valueOf(8 * 100)),
  75         Test.create(StandardSocketOptions.SO_REUSEADDR, Boolean.FALSE),
  76         Test.create(StandardSocketOptions.SO_REUSEPORT, Boolean.FALSE),
  77         Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(0)),  // lower-bound
  78         Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(100)),
  79         Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(255))  //upper-bound
  80     };
  81 
  82     static Test<?>[] datagramSocketTests = new Test<?>[] {
  83         Test.create(StandardSocketOptions.SO_SNDBUF, Integer.valueOf(10 * 100)),
  84         Test.create(StandardSocketOptions.SO_RCVBUF, Integer.valueOf(8 * 100)),
  85         Test.create(StandardSocketOptions.SO_REUSEADDR, Boolean.FALSE),
  86         Test.create(StandardSocketOptions.SO_REUSEPORT, Boolean.FALSE),
  87         Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(0)),  // lower-bound
  88         Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(100)),
  89         Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(255))  //upper-bound
  90     };
  91 
  92     static Test<?>[] multicastSocketTests = new Test<?>[] {
  93         Test.create(StandardSocketOptions.IP_MULTICAST_IF, getNetworkInterface()),
  94         Test.create(StandardSocketOptions.IP_MULTICAST_TTL, Integer.valueOf(0)),   // lower-bound
  95         Test.create(StandardSocketOptions.IP_MULTICAST_TTL, Integer.valueOf(10)),
  96         Test.create(StandardSocketOptions.IP_MULTICAST_TTL, Integer.valueOf(255)), //upper-bound
  97         Test.create(StandardSocketOptions.IP_MULTICAST_LOOP, Boolean.TRUE)
  98     };
  99 
 100     static NetworkInterface getNetworkInterface() {
 101         try {
 102             Enumeration<NetworkInterface> nifs = NetworkInterface.getNetworkInterfaces();
 103             while (nifs.hasMoreElements()) {
 104                 NetworkInterface ni = nifs.nextElement();
 105                 if (ni.supportsMulticast()) {
 106                     return ni;
 107                 }
 108             }
 109         } catch (Exception e) {
 110         }
 111         return null;
 112     }
 113 
 114     static boolean okayToTest(Socket s, SocketOption<?> option) {
 115         if (option == StandardSocketOptions.SO_REUSEPORT) {
 116             // skip SO_REUSEPORT if option is not supported
 117             return s.supportedOptions().contains(StandardSocketOptions.SO_REUSEPORT);
 118         }
 119         if (option == StandardSocketOptions.IP_TOS && s.isConnected()) {
 120             // skip IP_TOS if connected
 121             return false;
 122         }
 123         return true;
 124     }
 125 
 126     static <T> void testEqual(SocketOption<T> option, T value1, T value2) {
 127         if (!value1.equals(value2)) {
 128             throw new RuntimeException("Test of " + option.name() + " failed: "
 129                     + value1 + " != " + value2);
 130         }
 131     }
 132 
 133     static <T> void test(Socket s, Test<T> test) throws Exception {
 134         SocketOption<T> option = test.option;
 135         s.setOption(option, test.value);
 136         T value1 = s.getOption(test.option);
 137         T value2 = (T) legacyGetOption(Socket.class, s, test.option);
 138         testEqual(option, value1, value2);
 139     }
 140 
 141     static <T> void test(ServerSocket ss, Test<T> test) throws Exception {
 142         SocketOption<T> option = test.option;
 143         ss.setOption(option, test.value);
 144         T value1 = ss.getOption(test.option);
 145         T value2 = (T) legacyGetOption(ServerSocket.class, ss, test.option);
 146         testEqual(option, value1, value2);
 147     }
 148 
 149     static <T> void test(DatagramSocket ds, Test<T> test) throws Exception {
 150         SocketOption<T> option = test.option;
 151         ds.setOption(option, test.value);
 152         T value1 = ds.getOption(test.option);
 153         T value2 = (T) legacyGetOption(ds.getClass(), ds, test.option);
 154         testEqual(option, value1, value2);
 155     }
 156 
 157     @SuppressWarnings("try")
 158     static void doSocketTests() throws Exception {
 159         // unconnected socket
 160         try (Socket s = new Socket()) {
 161             for (Test<?> test : socketTests) {
 162                 if (okayToTest(s, test.option)) {
 163                     test(s, test);
 164                 }
 165             }
 166         }
 167 
 168         // connected socket
 169         try (ServerSocket ss = new ServerSocket()) {
 170             var loopback = InetAddress.getLoopbackAddress();
 171             ss.bind(new InetSocketAddress(loopback, 0));
 172             try (Socket s1 = new Socket()) {
 173                 s1.connect(ss.getLocalSocketAddress());
 174                 try (Socket s2 = ss.accept()) {
 175                     for (Test<?> test : socketTests) {
 176                         if (okayToTest(s1, test.option)) {
 177                             test(s1, test);
 178                         }
 179                     }
 180                 }
 181             }
 182         }
 183     }
 184 
 185     static void doServerSocketTests() throws Exception {
 186         try (ServerSocket ss = new ServerSocket(0)) {
 187             Set<SocketOption<?>> options = ss.supportedOptions();
 188             boolean reuseport = options.contains(StandardSocketOptions.SO_REUSEPORT);
 189             for (Test<?> test : serverSocketTests) {
 190                 if (!(test.option == StandardSocketOptions.SO_REUSEPORT && !reuseport)) {
 191                     test(ss, test);
 192                 }
 193             }
 194         }
 195     }
 196 
 197     static void doDatagramSocketTests() throws Exception {
 198         try (DatagramSocket ds = new DatagramSocket(0)) {
 199             Set<SocketOption<?>> options = ds.supportedOptions();
 200             boolean reuseport = options.contains(StandardSocketOptions.SO_REUSEPORT);
 201             for (Test<?> test : datagramSocketTests) {
 202                 if (!(test.option == StandardSocketOptions.SO_REUSEPORT && !reuseport)) {
 203                     test(ds, test);
 204                 }
 205             }
 206         }
 207     }
 208 
 209     static void doMulticastSocketTests() throws Exception {
 210         try (MulticastSocket ms = new MulticastSocket(0)) {
 211             for (Test<?> test : multicastSocketTests) {
 212                 test(ms, test);
 213             }
 214         }
 215     }
 216 
 217     static Object legacyGetOption(Class<?> type, Object s, Object option) throws Exception {
 218         if (type.equals(Socket.class)) {
 219             Socket socket = (Socket)s;
 220             Set<SocketOption<?>> options = socket.supportedOptions();
 221             boolean reuseport = options.contains(StandardSocketOptions.SO_REUSEPORT);
 222 
 223             if (option.equals(StandardSocketOptions.SO_KEEPALIVE)) {
 224                 return Boolean.valueOf(socket.getKeepAlive());
 225             } else if (option.equals(StandardSocketOptions.SO_SNDBUF)) {
 226                 return Integer.valueOf(socket.getSendBufferSize());
 227             } else if (option.equals(StandardSocketOptions.SO_RCVBUF)) {
 228                 return Integer.valueOf(socket.getReceiveBufferSize());
 229             } else if (option.equals(StandardSocketOptions.SO_REUSEADDR)) {
 230                 return Boolean.valueOf(socket.getReuseAddress());
 231             } else if (option.equals(StandardSocketOptions.SO_REUSEPORT) && reuseport) {
 232                 return Boolean.valueOf(socket.getOption(StandardSocketOptions.SO_REUSEPORT));
 233             } else if (option.equals(StandardSocketOptions.SO_LINGER)) {
 234                 return Integer.valueOf(socket.getSoLinger());
 235             } else if (option.equals(StandardSocketOptions.IP_TOS)) {
 236                 return Integer.valueOf(socket.getTrafficClass());
 237             } else if (option.equals(StandardSocketOptions.TCP_NODELAY)) {
 238                 return Boolean.valueOf(socket.getTcpNoDelay());
 239             } else {
 240                 throw new RuntimeException("unexpected socket option");
 241             }
 242         } else if  (type.equals(ServerSocket.class)) {
 243             ServerSocket socket = (ServerSocket)s;
 244             Set<SocketOption<?>> options = socket.supportedOptions();
 245             boolean reuseport = options.contains(StandardSocketOptions.SO_REUSEPORT);
 246 
 247             if (option.equals(StandardSocketOptions.SO_RCVBUF)) {
 248                 return Integer.valueOf(socket.getReceiveBufferSize());
 249             } else if (option.equals(StandardSocketOptions.SO_REUSEADDR)) {
 250                 return Boolean.valueOf(socket.getReuseAddress());
 251             } else if (option.equals(StandardSocketOptions.SO_REUSEPORT) && reuseport) {
 252                 return Boolean.valueOf(socket.getOption(StandardSocketOptions.SO_REUSEPORT));
 253             } else if (option.equals(StandardSocketOptions.IP_TOS)) {
 254                 return getServerSocketTrafficClass(socket);
 255             } else {
 256                 throw new RuntimeException("unexpected socket option");
 257             }
 258         } else if  (type.equals(DatagramSocket.class)) {
 259             DatagramSocket socket = (DatagramSocket)s;
 260             Set<SocketOption<?>> options = socket.supportedOptions();
 261             boolean reuseport = options.contains(StandardSocketOptions.SO_REUSEPORT);
 262 
 263             if (option.equals(StandardSocketOptions.SO_SNDBUF)) {
 264                 return Integer.valueOf(socket.getSendBufferSize());
 265             } else if (option.equals(StandardSocketOptions.SO_RCVBUF)) {
 266                 return Integer.valueOf(socket.getReceiveBufferSize());
 267             } else if (option.equals(StandardSocketOptions.SO_REUSEADDR)) {
 268                 return Boolean.valueOf(socket.getReuseAddress());
 269             } else if (option.equals(StandardSocketOptions.SO_REUSEPORT) && reuseport) {
 270                 return Boolean.valueOf(socket.getOption(StandardSocketOptions.SO_REUSEPORT));
 271             } else if (option.equals(StandardSocketOptions.IP_TOS)) {
 272                 return Integer.valueOf(socket.getTrafficClass());
 273             } else {
 274                 throw new RuntimeException("unexpected socket option");
 275             }
 276 
 277         } else if  (type.equals(MulticastSocket.class)) {
 278             MulticastSocket socket = (MulticastSocket)s;
 279             Set<SocketOption<?>> options = socket.supportedOptions();
 280             boolean reuseport = options.contains(StandardSocketOptions.SO_REUSEPORT);
 281 
 282             if (option.equals(StandardSocketOptions.SO_SNDBUF)) {
 283                 return Integer.valueOf(socket.getSendBufferSize());
 284             } else if (option.equals(StandardSocketOptions.SO_RCVBUF)) {
 285                 return Integer.valueOf(socket.getReceiveBufferSize());
 286             } else if (option.equals(StandardSocketOptions.SO_REUSEADDR)) {
 287                 return Boolean.valueOf(socket.getReuseAddress());
 288             } else if (option.equals(StandardSocketOptions.SO_REUSEPORT) && reuseport) {
 289                 return Boolean.valueOf(socket.getOption(StandardSocketOptions.SO_REUSEPORT));
 290             } else if (option.equals(StandardSocketOptions.IP_TOS)) {
 291                 return Integer.valueOf(socket.getTrafficClass());
 292             } else if (option.equals(StandardSocketOptions.IP_MULTICAST_IF)) {
 293                 return socket.getNetworkInterface();
 294             } else if (option.equals(StandardSocketOptions.IP_MULTICAST_TTL)) {
 295                 return Integer.valueOf(socket.getTimeToLive());
 296             } else if (option.equals(StandardSocketOptions.IP_MULTICAST_LOOP)) {
 297                 return Boolean.valueOf(socket.getLoopbackMode());
 298             } else {
 299                 throw new RuntimeException("unexpected socket option");
 300             }
 301         }
 302         throw new RuntimeException("unexpected socket type");
 303     }
 304 
 305     // Tests default and negative values of SO_LINGER. All negative values should
 306     // retrieve as -1.
 307     public static void soLinger() throws Exception {
 308         try (Socket s = new Socket()) {
 309             // retrieve without set
 310             int ignoreValue = s.getOption(StandardSocketOptions.SO_LINGER);
 311 
 312             for (int v : List.of(-1, -2, -100, -65534, -65535, -65536, -100000)) {
 313                 System.out.println("Testing SO_LINGER with:" + v);
 314                 s.setOption(StandardSocketOptions.SO_LINGER, v);
 315                 int value = s.getOption(StandardSocketOptions.SO_LINGER);
 316                 testEqual(StandardSocketOptions.SO_LINGER, -1, value);
 317             }
 318         }
 319     }
 320 
 321     public static void main(String args[]) throws Exception {
 322         IPSupport.throwSkippedExceptionIfNonOperational();
 323         doSocketTests();
 324         doServerSocketTests();
 325         doDatagramSocketTests();
 326         doMulticastSocketTests();
 327         soLinger();
 328     }
 329 
 330     // Reflectively access jdk.net.Sockets.getOption so that the test can run
 331     // without the jdk.net module.
 332     static Object getServerSocketTrafficClass(ServerSocket ss) throws Exception {
 333         try {
 334             Class<?> c = Class.forName("jdk.net.Sockets");
 335             Method m = c.getDeclaredMethod("getOption", ServerSocket.class, SocketOption.class);
 336             return m.invoke(null, ss, StandardSocketOptions.IP_TOS);
 337         } catch (ClassNotFoundException e) {
 338             // Ok, jdk.net module not present, just fall back
 339             System.out.println("jdk.net module not present, falling back.");
 340             return Integer.valueOf(ss.getOption(StandardSocketOptions.IP_TOS));
 341         } catch (ReflectiveOperationException e) {
 342             throw new AssertionError(e);
 343         }
 344     }
 345 }