/* * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /* * @test * @bug 8036979 8072384 8044773 8225214 8233296 8234083 * @library /test/lib * @requires !vm.graal.enabled * @run main/othervm -Xcheck:jni OptionsTest * @run main/othervm -Xcheck:jni -Djava.net.preferIPv4Stack=true OptionsTest * @run main/othervm --limit-modules=java.base OptionsTest */ import java.lang.reflect.Method; import java.net.*; import java.util.*; import jdk.test.lib.NetworkConfiguration; import jdk.test.lib.net.IPSupport; public class OptionsTest { static class Test { final SocketOption option; final T value; Test(SocketOption option, T value) { this.option = option; this.value = value; } static Test create(SocketOption option, T value) { return new Test(option, value); } } // The tests set the option using the new API, read back the set value // which could be different, and then use the legacy get API to check // these values are the same static Test[] socketTests = new Test[] { Test.create(StandardSocketOptions.SO_KEEPALIVE, Boolean.TRUE), Test.create(StandardSocketOptions.SO_SNDBUF, Integer.valueOf(10 * 100)), Test.create(StandardSocketOptions.SO_RCVBUF, Integer.valueOf(8 * 100)), Test.create(StandardSocketOptions.SO_REUSEADDR, Boolean.FALSE), Test.create(StandardSocketOptions.SO_REUSEPORT, Boolean.FALSE), Test.create(StandardSocketOptions.SO_LINGER, Integer.valueOf(-1)), Test.create(StandardSocketOptions.SO_LINGER, Integer.valueOf(0)), Test.create(StandardSocketOptions.SO_LINGER, Integer.valueOf(80)), Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(0)), // lower-bound Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(100)), Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(255)) //upper-bound }; static Test[] serverSocketTests = new Test[] { Test.create(StandardSocketOptions.SO_RCVBUF, Integer.valueOf(8 * 100)), Test.create(StandardSocketOptions.SO_REUSEADDR, Boolean.FALSE), Test.create(StandardSocketOptions.SO_REUSEPORT, Boolean.FALSE), Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(0)), // lower-bound Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(100)), Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(255)) //upper-bound }; static Test[] datagramSocketTests = new Test[] { Test.create(StandardSocketOptions.SO_SNDBUF, Integer.valueOf(10 * 100)), Test.create(StandardSocketOptions.SO_RCVBUF, Integer.valueOf(8 * 100)), Test.create(StandardSocketOptions.SO_REUSEADDR, Boolean.FALSE), Test.create(StandardSocketOptions.SO_REUSEPORT, Boolean.FALSE), Test.create(StandardSocketOptions.SO_BROADCAST, Boolean.FALSE), Test.create(StandardSocketOptions.SO_BROADCAST, Boolean.TRUE), Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(0)), // lower-bound Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(100)), Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(255)) //upper-bound }; static Test[] multicastSocketTests = new Test[] { Test.create(StandardSocketOptions.SO_BROADCAST, Boolean.FALSE), Test.create(StandardSocketOptions.SO_BROADCAST, Boolean.TRUE), Test.create(StandardSocketOptions.IP_MULTICAST_IF, getNetworkInterface()), Test.create(StandardSocketOptions.IP_MULTICAST_TTL, Integer.valueOf(0)), // lower-bound Test.create(StandardSocketOptions.IP_MULTICAST_TTL, Integer.valueOf(10)), Test.create(StandardSocketOptions.IP_MULTICAST_TTL, Integer.valueOf(255)), //upper-bound Test.create(StandardSocketOptions.IP_MULTICAST_LOOP, Boolean.TRUE) }; static NetworkInterface getNetworkInterface() { try { NetworkConfiguration nc = NetworkConfiguration.probe(); return nc.multicastInterfaces(true).findAny().orElse(null); } catch (Exception e) { } return null; } static boolean okayToTest(Socket s, SocketOption option) { if (option == StandardSocketOptions.SO_REUSEPORT) { // skip SO_REUSEPORT if option is not supported return s.supportedOptions().contains(StandardSocketOptions.SO_REUSEPORT); } if (option == StandardSocketOptions.IP_TOS && s.isConnected()) { // skip IP_TOS if connected return false; } return true; } static void testEqual(SocketOption option, T value1, T value2) { if (!value1.equals(value2)) { throw new RuntimeException("Test of " + option.name() + " failed: " + value1 + " != " + value2); } } static void test(Socket s, Test test) throws Exception { SocketOption option = test.option; s.setOption(option, test.value); T value1 = s.getOption(test.option); T value2 = (T) legacyGetOption(Socket.class, s, test.option); testEqual(option, value1, value2); } static void test(ServerSocket ss, Test test) throws Exception { SocketOption option = test.option; ss.setOption(option, test.value); T value1 = ss.getOption(test.option); T value2 = (T) legacyGetOption(ServerSocket.class, ss, test.option); testEqual(option, value1, value2); } static void test(DatagramSocket ds, Test test) throws Exception { SocketOption option = test.option; ds.setOption(option, test.value); T value1 = ds.getOption(test.option); T value2 = (T) legacyGetOption(ds.getClass(), ds, test.option); testEqual(option, value1, value2); } // Tests default and negative values of SO_LINGER. All negative values should // retrieve as -1. static void testSoLingerValues() throws Exception { try (Socket s = new Socket()) { // retrieve without set int defaultValue = s.getOption(StandardSocketOptions.SO_LINGER); testEqual(StandardSocketOptions.SO_LINGER, -1, defaultValue); for (int v : List.of(-1, -2, -100, -65534, -65535, -65536, -100000)) { System.out.println("Testing SO_LINGER with:" + v); s.setOption(StandardSocketOptions.SO_LINGER, v); int value = s.getOption(StandardSocketOptions.SO_LINGER); testEqual(StandardSocketOptions.SO_LINGER, -1, value); } } } @SuppressWarnings("try") static void doSocketTests() throws Exception { // unconnected socket try (Socket s = new Socket()) { for (Test test : socketTests) { if (okayToTest(s, test.option)) { test(s, test); } } } // connected socket try (ServerSocket ss = new ServerSocket()) { var loopback = InetAddress.getLoopbackAddress(); ss.bind(new InetSocketAddress(loopback, 0)); try (Socket s1 = new Socket()) { s1.connect(ss.getLocalSocketAddress()); try (Socket s2 = ss.accept()) { for (Test test : socketTests) { if (okayToTest(s1, test.option)) { test(s1, test); } } } } } testSoLingerValues(); } static void doServerSocketTests() throws Exception { try (ServerSocket ss = new ServerSocket(0)) { Set> options = ss.supportedOptions(); boolean reuseport = options.contains(StandardSocketOptions.SO_REUSEPORT); for (Test test : serverSocketTests) { if (!(test.option == StandardSocketOptions.SO_REUSEPORT && !reuseport)) { test(ss, test); } } } } static void doDatagramSocketTests() throws Exception { try (DatagramSocket ds = new DatagramSocket(0)) { Set> options = ds.supportedOptions(); boolean reuseport = options.contains(StandardSocketOptions.SO_REUSEPORT); for (Test test : datagramSocketTests) { if (!(test.option == StandardSocketOptions.SO_REUSEPORT && !reuseport)) { test(ds, test); } } } } static void doMulticastSocketTests() throws Exception { try (MulticastSocket ms = new MulticastSocket(0)) { for (Test test : multicastSocketTests) { test(ms, test); } } } static Object legacyGetOption(Class type, Object s, Object option) throws Exception { if (type.equals(Socket.class)) { Socket socket = (Socket)s; Set> options = socket.supportedOptions(); boolean reuseport = options.contains(StandardSocketOptions.SO_REUSEPORT); if (option.equals(StandardSocketOptions.SO_KEEPALIVE)) { return Boolean.valueOf(socket.getKeepAlive()); } else if (option.equals(StandardSocketOptions.SO_SNDBUF)) { return Integer.valueOf(socket.getSendBufferSize()); } else if (option.equals(StandardSocketOptions.SO_RCVBUF)) { return Integer.valueOf(socket.getReceiveBufferSize()); } else if (option.equals(StandardSocketOptions.SO_REUSEADDR)) { return Boolean.valueOf(socket.getReuseAddress()); } else if (option.equals(StandardSocketOptions.SO_REUSEPORT) && reuseport) { return Boolean.valueOf(socket.getOption(StandardSocketOptions.SO_REUSEPORT)); } else if (option.equals(StandardSocketOptions.SO_LINGER)) { return Integer.valueOf(socket.getSoLinger()); } else if (option.equals(StandardSocketOptions.IP_TOS)) { return Integer.valueOf(socket.getTrafficClass()); } else if (option.equals(StandardSocketOptions.TCP_NODELAY)) { return Boolean.valueOf(socket.getTcpNoDelay()); } else { throw new RuntimeException("unexpected socket option"); } } else if (type.equals(ServerSocket.class)) { ServerSocket socket = (ServerSocket)s; Set> options = socket.supportedOptions(); boolean reuseport = options.contains(StandardSocketOptions.SO_REUSEPORT); if (option.equals(StandardSocketOptions.SO_RCVBUF)) { return Integer.valueOf(socket.getReceiveBufferSize()); } else if (option.equals(StandardSocketOptions.SO_REUSEADDR)) { return Boolean.valueOf(socket.getReuseAddress()); } else if (option.equals(StandardSocketOptions.SO_REUSEPORT) && reuseport) { return Boolean.valueOf(socket.getOption(StandardSocketOptions.SO_REUSEPORT)); } else if (option.equals(StandardSocketOptions.IP_TOS)) { return getServerSocketTrafficClass(socket); } else { throw new RuntimeException("unexpected socket option"); } } else if (type.equals(DatagramSocket.class)) { DatagramSocket socket = (DatagramSocket)s; Set> options = socket.supportedOptions(); boolean reuseport = options.contains(StandardSocketOptions.SO_REUSEPORT); if (option.equals(StandardSocketOptions.SO_SNDBUF)) { return Integer.valueOf(socket.getSendBufferSize()); } else if (option.equals(StandardSocketOptions.SO_RCVBUF)) { return Integer.valueOf(socket.getReceiveBufferSize()); } else if (option.equals(StandardSocketOptions.SO_REUSEADDR)) { return Boolean.valueOf(socket.getReuseAddress()); } else if (option.equals(StandardSocketOptions.SO_BROADCAST)) { return Boolean.valueOf(socket.getBroadcast()); } else if (option.equals(StandardSocketOptions.SO_REUSEPORT) && reuseport) { return Boolean.valueOf(socket.getOption(StandardSocketOptions.SO_REUSEPORT)); } else if (option.equals(StandardSocketOptions.IP_TOS)) { return Integer.valueOf(socket.getTrafficClass()); } else { throw new RuntimeException("unexpected socket option"); } } else if (type.equals(MulticastSocket.class)) { MulticastSocket socket = (MulticastSocket)s; Set> options = socket.supportedOptions(); boolean reuseport = options.contains(StandardSocketOptions.SO_REUSEPORT); if (option.equals(StandardSocketOptions.SO_SNDBUF)) { return Integer.valueOf(socket.getSendBufferSize()); } else if (option.equals(StandardSocketOptions.SO_RCVBUF)) { return Integer.valueOf(socket.getReceiveBufferSize()); } else if (option.equals(StandardSocketOptions.SO_REUSEADDR)) { return Boolean.valueOf(socket.getReuseAddress()); } else if (option.equals(StandardSocketOptions.SO_BROADCAST)) { return Boolean.valueOf(socket.getBroadcast()); } else if (option.equals(StandardSocketOptions.SO_REUSEPORT) && reuseport) { return Boolean.valueOf(socket.getOption(StandardSocketOptions.SO_REUSEPORT)); } else if (option.equals(StandardSocketOptions.IP_TOS)) { return Integer.valueOf(socket.getTrafficClass()); } else if (option.equals(StandardSocketOptions.IP_MULTICAST_IF)) { return socket.getNetworkInterface(); } else if (option.equals(StandardSocketOptions.IP_MULTICAST_TTL)) { return Integer.valueOf(socket.getTimeToLive()); } else if (option.equals(StandardSocketOptions.IP_MULTICAST_LOOP)) { return !Boolean.valueOf(socket.getLoopbackMode()); } else { throw new RuntimeException("unexpected socket option"); } } throw new RuntimeException("unexpected socket type"); } public static void main(String args[]) throws Exception { IPSupport.throwSkippedExceptionIfNonOperational(); doSocketTests(); doServerSocketTests(); doDatagramSocketTests(); doMulticastSocketTests(); } // Reflectively access jdk.net.Sockets.getOption so that the test can run // without the jdk.net module. static Object getServerSocketTrafficClass(ServerSocket ss) throws Exception { try { Class c = Class.forName("jdk.net.Sockets"); Method m = c.getMethod("getOption", ServerSocket.class, SocketOption.class); return m.invoke(null, ss, StandardSocketOptions.IP_TOS); } catch (ClassNotFoundException e) { // Ok, jdk.net module not present, just fall back System.out.println("jdk.net module not present, falling back."); return Integer.valueOf(ss.getOption(StandardSocketOptions.IP_TOS)); } catch (ReflectiveOperationException e) { throw new AssertionError(e); } } }