1 /*
2  * Copyright (c) 2014, 2017, 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  * @requires !vm.graal.enabled
28  * @run main/othervm -Xcheck:jni OptionsTest
29  * @run main/othervm -Xcheck:jni -Djava.net.preferIPv4Stack=true OptionsTest
30  * @run main/othervm --limit-modules=java.base OptionsTest
31  */
32 package test.java.net.SocketOptions;
33 
34 import java.lang.reflect.Method;
35 import java.net.*;
36 import java.util.*;
37 
38 import org.testng.annotations.Test;
39 
40 public class OptionsTest {
41 
42     static class TestClass {
TestClass(SocketOption<?> option, Object testValue)43         TestClass(SocketOption<?> option, Object testValue) {
44             this.option = option;
45             this.testValue = testValue;
46         }
create(SocketOption<?> option, Object testValue)47         static TestClass create(SocketOption<?> option, Object testValue) {
48             return new TestClass(option, testValue);
49         }
50         Object option;
51         Object testValue;
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 TestClass[] socketTests = new TestClass[] {
59             TestClass.create(StandardSocketOptions.SO_KEEPALIVE, Boolean.TRUE),
60             TestClass.create(StandardSocketOptions.SO_SNDBUF, Integer.valueOf(10 * 100)),
61             TestClass.create(StandardSocketOptions.SO_RCVBUF, Integer.valueOf(8 * 100)),
62             TestClass.create(StandardSocketOptions.SO_REUSEADDR, Boolean.FALSE),
63             TestClass.create(StandardSocketOptions.SO_REUSEPORT, Boolean.FALSE),
64             TestClass.create(StandardSocketOptions.SO_LINGER, Integer.valueOf(80)),
65             TestClass.create(StandardSocketOptions.IP_TOS, Integer.valueOf(100))
66     };
67 
68     static TestClass[] serverSocketTests = new TestClass[] {
69             TestClass.create(StandardSocketOptions.SO_RCVBUF, Integer.valueOf(8 * 100)),
70             TestClass.create(StandardSocketOptions.SO_REUSEADDR, Boolean.FALSE),
71             TestClass.create(StandardSocketOptions.SO_REUSEPORT, Boolean.FALSE),
72             TestClass.create(StandardSocketOptions.IP_TOS, Integer.valueOf(100))
73     };
74 
75     static TestClass[] dgSocketTests = new TestClass[] {
76             TestClass.create(StandardSocketOptions.SO_SNDBUF, Integer.valueOf(10 * 100)),
77             TestClass.create(StandardSocketOptions.SO_RCVBUF, Integer.valueOf(8 * 100)),
78             TestClass.create(StandardSocketOptions.SO_REUSEADDR, Boolean.FALSE),
79             TestClass.create(StandardSocketOptions.SO_REUSEPORT, Boolean.FALSE),
80             TestClass.create(StandardSocketOptions.IP_TOS, Integer.valueOf(100))
81     };
82 
83     static TestClass[] mcSocketTests = new TestClass[] {
84             // Android-removed: unused in Android
85             // TestClass.create(StandardSocketOptions.IP_MULTICAST_IF, getNetworkInterface()),
86             TestClass.create(StandardSocketOptions.IP_MULTICAST_TTL, Integer.valueOf(10)),
87             TestClass.create(StandardSocketOptions.IP_MULTICAST_LOOP, Boolean.TRUE)
88     };
89 
doSocketTests()90     static void doSocketTests() throws Exception {
91         try (
92                 ServerSocket srv = new ServerSocket(0);
93                 Socket c = new Socket("127.0.0.1", srv.getLocalPort());
94                 Socket s = srv.accept();
95         ) {
96             Set<SocketOption<?>> options = c.supportedOptions();
97             boolean reuseport = options.contains(StandardSocketOptions.SO_REUSEPORT);
98             for (int i=0; i<socketTests.length; i++) {
99                 TestClass test = socketTests[i];
100                 if (!(test.option == StandardSocketOptions.SO_REUSEPORT && !reuseport)) {
101                     c.setOption((SocketOption)test.option, test.testValue);
102                     Object getval = c.getOption((SocketOption)test.option);
103                     Object legacyget = legacyGetOption(Socket.class, c,test.option);
104                     if (!getval.equals(legacyget)) {
105                         Formatter f = new Formatter();
106                         f.format("S Err %d: %s/%s", i, getval, legacyget);
107                         throw new RuntimeException(f.toString());
108                     }
109                 }
110             }
111         }
112     }
113 
doDgSocketTests()114     static void doDgSocketTests() throws Exception {
115         try (
116                 DatagramSocket c = new DatagramSocket(0);
117         ) {
118             Set<SocketOption<?>> options = c.supportedOptions();
119             boolean reuseport = options.contains(StandardSocketOptions.SO_REUSEPORT);
120             for (int i=0; i<dgSocketTests.length; i++) {
121                 TestClass test = dgSocketTests[i];
122                 if (!(test.option == StandardSocketOptions.SO_REUSEPORT && !reuseport)) {
123                     c.setOption((SocketOption)test.option, test.testValue);
124                     Object getval = c.getOption((SocketOption)test.option);
125                     Object legacyget = legacyGetOption(DatagramSocket.class, c,test.option);
126                     if (!getval.equals(legacyget)) {
127                         Formatter f = new Formatter();
128                         f.format("DG Err %d: %s/%s", i, getval, legacyget);
129                         throw new RuntimeException(f.toString());
130                     }
131                 }
132             }
133         }
134     }
135 
doMcSocketTests()136     static void doMcSocketTests() throws Exception {
137         try (
138                 MulticastSocket c = new MulticastSocket(0);
139         ) {
140             for (int i=0; i<mcSocketTests.length; i++) {
141                 TestClass test = mcSocketTests[i];
142                 c.setOption((SocketOption)test.option, test.testValue);
143                 Object getval = c.getOption((SocketOption)test.option);
144                 Object legacyget = legacyGetOption(MulticastSocket.class, c,test.option);
145                 if (!getval.equals(legacyget)) {
146                     Formatter f = new Formatter();
147                     f.format("MC Err %d: %s/%s", i, getval, legacyget);
148                     throw new RuntimeException(f.toString());
149                 }
150             }
151         }
152     }
153 
doServerSocketTests()154     static void doServerSocketTests() throws Exception {
155         try (
156                 ServerSocket c = new ServerSocket(0);
157         ) {
158             Set<SocketOption<?>> options = c.supportedOptions();
159             boolean reuseport = options.contains(StandardSocketOptions.SO_REUSEPORT);
160             for (int i=0; i<serverSocketTests.length; i++) {
161                 TestClass test = serverSocketTests[i];
162                 if (!(test.option == StandardSocketOptions.SO_REUSEPORT && !reuseport)) {
163                     c.setOption((SocketOption)test.option, test.testValue);
164                     Object getval = c.getOption((SocketOption)test.option);
165                     Object legacyget = legacyGetOption(
166                             ServerSocket.class, c, test.option
167                     );
168                     if (!getval.equals(legacyget)) {
169                         Formatter f = new Formatter();
170                         f.format("SS Err %d: %s/%s", i, getval, legacyget);
171                         throw new RuntimeException(f.toString());
172                     }
173                 }
174             }
175         }
176     }
177 
legacyGetOption( Class<?> type, Object s, Object option)178     static Object legacyGetOption(
179             Class<?> type, Object s, Object option)
180 
181             throws Exception
182     {
183         if (type.equals(Socket.class)) {
184             Socket socket = (Socket)s;
185             Set<SocketOption<?>> options = socket.supportedOptions();
186             boolean reuseport = options.contains(StandardSocketOptions.SO_REUSEPORT);
187 
188             if (option.equals(StandardSocketOptions.SO_KEEPALIVE)) {
189                 return Boolean.valueOf(socket.getKeepAlive());
190             } else if (option.equals(StandardSocketOptions.SO_SNDBUF)) {
191                 return Integer.valueOf(socket.getSendBufferSize());
192             } else if (option.equals(StandardSocketOptions.SO_RCVBUF)) {
193                 return Integer.valueOf(socket.getReceiveBufferSize());
194             } else if (option.equals(StandardSocketOptions.SO_REUSEADDR)) {
195                 return Boolean.valueOf(socket.getReuseAddress());
196             } else if (option.equals(StandardSocketOptions.SO_REUSEPORT) && reuseport) {
197                 return Boolean.valueOf(socket.getOption(StandardSocketOptions.SO_REUSEPORT));
198             } else if (option.equals(StandardSocketOptions.SO_LINGER)) {
199                 return Integer.valueOf(socket.getSoLinger());
200             } else if (option.equals(StandardSocketOptions.IP_TOS)) {
201                 return Integer.valueOf(socket.getTrafficClass());
202             } else if (option.equals(StandardSocketOptions.TCP_NODELAY)) {
203                 return Boolean.valueOf(socket.getTcpNoDelay());
204             } else {
205                 throw new RuntimeException("unexecpted socket option");
206             }
207         } else if  (type.equals(ServerSocket.class)) {
208             ServerSocket socket = (ServerSocket)s;
209             Set<SocketOption<?>> options = socket.supportedOptions();
210             boolean reuseport = options.contains(StandardSocketOptions.SO_REUSEPORT);
211 
212             if (option.equals(StandardSocketOptions.SO_RCVBUF)) {
213                 return Integer.valueOf(socket.getReceiveBufferSize());
214             } else if (option.equals(StandardSocketOptions.SO_REUSEADDR)) {
215                 return Boolean.valueOf(socket.getReuseAddress());
216             } else if (option.equals(StandardSocketOptions.SO_REUSEPORT) && reuseport) {
217                 return Boolean.valueOf(socket.getOption(StandardSocketOptions.SO_REUSEPORT));
218             } else if (option.equals(StandardSocketOptions.IP_TOS)) {
219                 return getServerSocketTrafficClass(socket);
220             } else {
221                 throw new RuntimeException("unexecpted socket option");
222             }
223         } else if  (type.equals(DatagramSocket.class)) {
224             DatagramSocket socket = (DatagramSocket)s;
225             Set<SocketOption<?>> options = socket.supportedOptions();
226             boolean reuseport = options.contains(StandardSocketOptions.SO_REUSEPORT);
227 
228             if (option.equals(StandardSocketOptions.SO_SNDBUF)) {
229                 return Integer.valueOf(socket.getSendBufferSize());
230             } else if (option.equals(StandardSocketOptions.SO_RCVBUF)) {
231                 return Integer.valueOf(socket.getReceiveBufferSize());
232             } else if (option.equals(StandardSocketOptions.SO_REUSEADDR)) {
233                 return Boolean.valueOf(socket.getReuseAddress());
234             } else if (option.equals(StandardSocketOptions.SO_REUSEPORT) && reuseport) {
235                 return Boolean.valueOf(socket.getOption(StandardSocketOptions.SO_REUSEPORT));
236             } else if (option.equals(StandardSocketOptions.IP_TOS)) {
237                 return Integer.valueOf(socket.getTrafficClass());
238             } else {
239                 throw new RuntimeException("unexecpted socket option");
240             }
241 
242         } else if  (type.equals(MulticastSocket.class)) {
243             MulticastSocket socket = (MulticastSocket)s;
244             Set<SocketOption<?>> options = socket.supportedOptions();
245             boolean reuseport = options.contains(StandardSocketOptions.SO_REUSEPORT);
246 
247             if (option.equals(StandardSocketOptions.SO_SNDBUF)) {
248                 return Integer.valueOf(socket.getSendBufferSize());
249             } else if (option.equals(StandardSocketOptions.SO_RCVBUF)) {
250                 return Integer.valueOf(socket.getReceiveBufferSize());
251             } else if (option.equals(StandardSocketOptions.SO_REUSEADDR)) {
252                 return Boolean.valueOf(socket.getReuseAddress());
253             } else if (option.equals(StandardSocketOptions.SO_REUSEPORT) && reuseport) {
254                 return Boolean.valueOf(socket.getOption(StandardSocketOptions.SO_REUSEPORT));
255             } else if (option.equals(StandardSocketOptions.IP_TOS)) {
256                 return Integer.valueOf(socket.getTrafficClass());
257             } else if (option.equals(StandardSocketOptions.IP_MULTICAST_IF)) {
258                 return socket.getNetworkInterface();
259             } else if (option.equals(StandardSocketOptions.IP_MULTICAST_TTL)) {
260                 return Integer.valueOf(socket.getTimeToLive());
261             } else if (option.equals(StandardSocketOptions.IP_MULTICAST_LOOP)) {
262                 return Boolean.valueOf(socket.getLoopbackMode());
263             } else {
264                 throw new RuntimeException("unexecpted socket option");
265             }
266         }
267         throw new RuntimeException("unexecpted socket type");
268     }
269 
270     @Test
testOptions()271     public void testOptions() throws Exception {
272         doSocketTests();
273         doServerSocketTests();
274         doDgSocketTests();
275         doMcSocketTests();
276     }
277 
278     // Reflectively access jdk.net.Sockets.getOption so that the test can run
279     // without the jdk.net module.
getServerSocketTrafficClass(ServerSocket ss)280     static Object getServerSocketTrafficClass(ServerSocket ss) throws Exception {
281         try {
282             Class<?> c = Class.forName("jdk.net.Sockets");
283             Method m = c.getDeclaredMethod("getOption", ServerSocket.class, SocketOption.class);
284             return m.invoke(null, ss, StandardSocketOptions.IP_TOS);
285         } catch (ClassNotFoundException e) {
286             // Ok, jdk.net module not present, just fall back
287             System.out.println("jdk.net module not present, falling back.");
288             return Integer.valueOf(ss.getOption(StandardSocketOptions.IP_TOS));
289         } catch (ReflectiveOperationException e) {
290             throw new AssertionError(e);
291         }
292     }
293 }