1 /*
2  * Copyright (c) 2020, 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 8239355
27  * @summary Check that new SO_SNDBUF limit on macOS is adhered to
28  * @library /test/lib
29  * @build jdk.test.lib.net.IPSupport
30  * @requires os.family == "mac"
31  * @run testng/othervm MinSendBufferSize
32  * @run testng/othervm -Djava.net.preferIPv4Stack=true MinSendBufferSize
33  */
34 
35 import org.testng.annotations.AfterTest;
36 import org.testng.annotations.BeforeTest;
37 import org.testng.annotations.DataProvider;
38 import org.testng.annotations.Test;
39 
40 import java.io.IOException;
41 import java.net.DatagramSocket;
42 import java.net.InetAddress;
43 import java.net.ProtocolFamily;
44 import java.nio.ByteBuffer;
45 import java.nio.channels.DatagramChannel;
46 import java.util.ArrayList;
47 import java.util.List;
48 
49 import static java.net.StandardSocketOptions.SO_SNDBUF;
50 import static jdk.test.lib.net.IPSupport.hasIPv4;
51 import static jdk.test.lib.net.IPSupport.hasIPv6;
52 import static jdk.test.lib.net.IPSupport.preferIPv4Stack;
53 import static java.net.StandardProtocolFamily.INET;
54 import static java.net.StandardProtocolFamily.INET6;
55 import static org.testng.Assert.assertTrue;
56 import static org.testng.Assert.expectThrows;
57 
58 public class MinSendBufferSize {
59     private int EXPECTED_SNDBUF;
60     private DatagramChannel datagramChannel, datagramChannelIPv4,
61             datagramChannelIPv6;
62 
63     private final static int IPV4_SNDBUF = 65507;
64     private final static int IPV6_SNDBUF = 65527;
65     private final static Class<IOException> IOE = IOException.class;
66 
67     @BeforeTest
setUp()68     public void setUp() throws IOException {
69         datagramChannel = DatagramChannel.open();
70         if (hasIPv4())
71             datagramChannelIPv4 = DatagramChannel.open(INET);
72         if (hasIPv6())
73             datagramChannelIPv6 = DatagramChannel.open(INET6);
74 
75         EXPECTED_SNDBUF = hasIPv6() && !preferIPv4Stack()
76                 ? IPV6_SNDBUF : IPV4_SNDBUF;
77     }
78 
populateDataProvider(List<Object[]> testcases, DatagramChannel datagramChannel, int payloadSize, ProtocolFamily family)79     private void populateDataProvider(List<Object[]> testcases,
80                                       DatagramChannel datagramChannel,
81                                       int payloadSize,
82                                       ProtocolFamily family) {
83 
84         testcases.add(new Object[]{datagramChannel, payloadSize - 1,
85                 family, null});
86         testcases.add(new Object[]{datagramChannel, payloadSize,
87                 family, null});
88         testcases.add(new Object[]{datagramChannel, payloadSize + 1,
89                 family, IOE});
90     }
91 
92     @DataProvider(name = "testGetOptionProvider")
providerIO()93     public Object[][] providerIO() {
94         var testcases = new ArrayList<Object[]>();
95 
96         testcases.add(new Object[]{datagramChannel, EXPECTED_SNDBUF});
97         if (hasIPv4())
98             testcases.add(new Object[]{datagramChannelIPv4, IPV4_SNDBUF});
99         if (hasIPv6())
100             testcases.add(new Object[]{datagramChannelIPv6, IPV6_SNDBUF});
101 
102         return testcases.toArray(Object[][]::new);
103     }
104 
105     @DataProvider(name = "testSendPayloadProvider")
providerIO_Payload()106     public Object[][] providerIO_Payload() {
107         var testcases = new ArrayList<Object[]>();
108 
109         if (hasIPv4())
110             populateDataProvider(testcases, datagramChannel,
111                     IPV4_SNDBUF, INET);
112         if (hasIPv6() && !preferIPv4Stack())
113             populateDataProvider(testcases, datagramChannel,
114                     IPV6_SNDBUF, INET6);
115 
116         if (hasIPv4())
117             populateDataProvider(testcases, datagramChannelIPv4,
118                     IPV4_SNDBUF, INET);
119         if (hasIPv6())
120             populateDataProvider(testcases, datagramChannelIPv6,
121                     IPV6_SNDBUF, INET6);
122 
123         return testcases.toArray(Object[][]::new);
124     }
125 
126     @Test(dataProvider = "testGetOptionProvider")
testGetOption(DatagramChannel channel, int sendBuf)127     public void testGetOption(DatagramChannel channel, int sendBuf)
128             throws IOException {
129 
130         assertTrue(channel.getOption(SO_SNDBUF) >= sendBuf);
131     }
132 
133     @Test(dataProvider = "testSendPayloadProvider")
testSend(DatagramChannel channel, int sendBuf, ProtocolFamily family, Class<? extends Throwable> exception)134     public void testSend(DatagramChannel channel, int sendBuf,
135                             ProtocolFamily family,
136                             Class<? extends Throwable> exception)
137             throws IOException {
138 
139         InetAddress targetAddress;
140         assert family != null;
141         if (family == INET) {
142             targetAddress = InetAddress.getByName("127.0.0.1");
143         } else {
144             targetAddress = InetAddress.getByName("::1");
145         }
146 
147         try (var receiver = new DatagramSocket(0, targetAddress)) {
148             var buf = ByteBuffer.allocate(sendBuf);
149             var addr = receiver.getLocalSocketAddress();
150             if (exception != null) {
151                 expectThrows(exception, () -> channel.send(buf, addr));
152             } else {
153                 channel.send(buf, addr);
154             }
155         }
156     }
157 
158     @AfterTest
tearDown()159     public void tearDown() throws IOException {
160         datagramChannel.close();
161         if (hasIPv4())
162             datagramChannelIPv4.close();
163         if (hasIPv6())
164             datagramChannelIPv6.close();
165     }
166 }
167