1 /*
2  * Copyright (c) 2009, 2014, 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 /* @test
25  * @bug 4927640
26  * @summary Tests the SCTP protocol implementation
27  * @author chegar
28  */
29 
30 import java.io.IOException;
31 import java.net.InetSocketAddress;
32 import java.net.SocketAddress;
33 import java.util.Iterator;
34 import java.util.Set;
35 import java.util.List;
36 import java.util.Arrays;
37 import java.nio.ByteBuffer;
38 import java.nio.channels.ClosedChannelException;
39 import com.sun.nio.sctp.AbstractNotificationHandler;
40 import com.sun.nio.sctp.Association;
41 import com.sun.nio.sctp.AssociationChangeNotification;
42 import com.sun.nio.sctp.AssociationChangeNotification.AssocChangeEvent;
43 import com.sun.nio.sctp.HandlerResult;
44 import com.sun.nio.sctp.MessageInfo;
45 import com.sun.nio.sctp.SctpChannel;
46 import com.sun.nio.sctp.SctpMultiChannel;
47 import com.sun.nio.sctp.SctpServerChannel;
48 import com.sun.nio.sctp.SctpSocketOption;
49 import java.security.AccessController;
50 import java.security.PrivilegedAction;
51 import static com.sun.nio.sctp.SctpStandardSocketOptions.*;
52 import static java.lang.System.out;
53 
54 public class SocketOptionTests {
55     final String osName = AccessController.doPrivileged(
56                     (PrivilegedAction<String>)() -> System.getProperty("os.name"));
57 
checkOption(SctpMultiChannel smc, SctpSocketOption<T> name, T expectedValue)58     <T> void checkOption(SctpMultiChannel smc, SctpSocketOption<T> name,
59             T expectedValue) throws IOException {
60         T value = smc.getOption(name, null);
61         check(value.equals(expectedValue), name + ": value (" + value +
62                 ") not as expected (" + expectedValue + ")");
63        }
64 
optionalSupport(SctpMultiChannel smc, SctpSocketOption<T> name, T value)65     <T> void optionalSupport(SctpMultiChannel smc, SctpSocketOption<T> name,
66             T value) {
67         try {
68             smc.setOption(name, value, null);
69             checkOption(smc, name, value);
70         } catch (IOException e) {
71             /* Informational only, not all options have native support */
72             out.println(name + " not supported. " + e);
73         }
74     }
75 
test(String[] args)76     void test(String[] args) {
77         if (!Util.isSCTPSupported()) {
78             out.println("SCTP protocol is not supported");
79             out.println("Test cannot be run");
80             return;
81         }
82 
83         try {
84             SctpMultiChannel smc = SctpMultiChannel.open();
85 
86             /* check supported options */
87             Set<SctpSocketOption<?>> options = smc.supportedOptions();
88             List<? extends SctpSocketOption<?>> expected = Arrays.<SctpSocketOption<?>>asList(
89                     SCTP_DISABLE_FRAGMENTS, SCTP_EXPLICIT_COMPLETE,
90                     SCTP_FRAGMENT_INTERLEAVE, SCTP_INIT_MAXSTREAMS,
91                     SCTP_NODELAY, SCTP_PRIMARY_ADDR, SCTP_SET_PEER_PRIMARY_ADDR,
92                     SO_SNDBUF, SO_RCVBUF, SO_LINGER);
93 
94             for (SctpSocketOption opt: expected) {
95                 if (!options.contains(opt))
96                     fail(opt.name() + " should be supported");
97             }
98 
99             InitMaxStreams streams = InitMaxStreams.create(1024, 1024);
100             smc.setOption(SCTP_INIT_MAXSTREAMS, streams, null);
101             checkOption(smc, SCTP_INIT_MAXSTREAMS, streams);
102             streams = smc.getOption(SCTP_INIT_MAXSTREAMS, null);
103             check(streams.maxInStreams() == 1024, "Max in streams: value: "
104                     + streams.maxInStreams() + ", expected 1024 ");
105             check(streams.maxOutStreams() == 1024, "Max out streams: value: "
106                     + streams.maxOutStreams() + ", expected 1024 ");
107 
108             optionalSupport(smc, SCTP_DISABLE_FRAGMENTS, true);
109             optionalSupport(smc, SCTP_EXPLICIT_COMPLETE, true);
110             optionalSupport(smc, SCTP_FRAGMENT_INTERLEAVE, 1);
111 
112             smc.setOption(SCTP_NODELAY, true, null);
113             checkOption(smc, SCTP_NODELAY, true);
114             smc.setOption(SO_SNDBUF, 16*1024, null);
115             smc.setOption(SO_RCVBUF, 16*1024, null);
116 
117             checkOption(smc, SO_LINGER, -1);  /* default should be negative */
118 
119             /* Setting SO_LINGER not support for one-to-many on Solaris */
120             if (!"SunOS".equals(osName)) {
121                 smc.setOption(SO_LINGER, 2000, null);
122                 checkOption(smc, SO_LINGER, 2000);
123             }
124 
125             /* SCTP_PRIMARY_ADDR */
126             sctpPrimaryAddr();
127 
128             /* NullPointerException */
129             try {
130                 smc.setOption(null, "value", null);
131                 fail("NullPointerException not thrown for setOption");
132             } catch (NullPointerException unused) {
133                 pass();
134             }
135             try {
136                smc.getOption(null, null);
137                fail("NullPointerException not thrown for getOption");
138             } catch (NullPointerException unused) {
139                pass();
140             }
141 
142             /* ClosedChannelException */
143             smc.close();
144             try {
145                smc.setOption(SCTP_INIT_MAXSTREAMS, streams, null);
146                fail("ClosedChannelException not thrown");
147             } catch (ClosedChannelException unused) {
148                 pass();
149             }
150         } catch (IOException ioe) {
151             unexpected(ioe);
152         }
153     }
154 
155     /* SCTP_PRIMARY_ADDR */
sctpPrimaryAddr()156     void sctpPrimaryAddr() throws IOException {
157         SocketAddress addrToSet = null;
158         ByteBuffer buffer = ByteBuffer.allocate(Util.SMALL_BUFFER);
159 
160         System.out.println("TESTING SCTP_PRIMARY_ADDR");
161 
162         /* create listening channel */
163         SctpServerChannel ssc = SctpServerChannel.open().bind(null);
164         Set<SocketAddress> addrs = ssc.getAllLocalAddresses();
165         if (addrs.isEmpty())
166             debug("addrs should not be empty");
167 
168         InetSocketAddress serverAddr = (InetSocketAddress) addrs.iterator().next();
169 
170         /* setup an association implicitly by sending a small message */
171         int streamNumber = 0;
172         debug("sending to " + serverAddr + " on stream number: " + streamNumber);
173         MessageInfo info = MessageInfo.createOutgoing(serverAddr, streamNumber);
174         buffer.put(Util.SMALL_MESSAGE.getBytes("ISO-8859-1"));
175         buffer.flip();
176 
177         debug("sending small message: " + buffer);
178         SctpMultiChannel smc = SctpMultiChannel.open();
179         int sent = smc.send(buffer, info);
180 
181         /* Receive the COMM_UP */
182         buffer.clear();
183         SOTNotificationHandler handler = new SOTNotificationHandler();
184         info = smc.receive(buffer, null, handler);
185         check(handler.receivedCommUp(), "COMM_UP no received");
186         Set<Association> associations = smc.associations();
187         check(!associations.isEmpty(),"There should be some associations");
188         Association assoc = associations.iterator().next();
189 
190         SctpChannel peerChannel = ssc.accept();
191         ssc.close();
192         Set<SocketAddress> peerAddrs = peerChannel.getAllLocalAddresses();
193         debug("Peer local Addresses: ");
194         for (Iterator<SocketAddress> it = peerAddrs.iterator(); it.hasNext(); ) {
195             InetSocketAddress addr = (InetSocketAddress)it.next();
196             debug("\t" + addr);
197             addrToSet = addr;   // any of the peer addresses will do!
198         }
199 
200         /* retrieval of SCTP_PRIMARY_ADDR is not supported on Solaris */
201         if ("SunOS".equals(osName)) {
202             /* For now do not set this option. There is a bug on Solaris 10 pre Update 5
203              * where setting this option returns Invalid argument */
204             //debug("Set SCTP_PRIMARY_ADDR with " + addrToSet);
205             //smc.setOption(SCTP_PRIMARY_ADDR, addrToSet, assoc);
206             return;
207         } else { /* Linux */
208             SocketAddress primaryAddr = smc.getOption(SCTP_PRIMARY_ADDR, assoc);
209             System.out.println("SCTP_PRIMARY_ADDR returned: " + primaryAddr);
210             /* Verify that this is one of the peer addresses */
211             boolean found = false;
212             addrToSet = primaryAddr; // may not have more than one addr
213             for (Iterator<SocketAddress> it = peerAddrs.iterator(); it.hasNext(); ) {
214                 InetSocketAddress addr = (InetSocketAddress)it.next();
215                 if (addr.equals(primaryAddr)) {
216                     found = true;
217                 }
218                 addrToSet = addr;
219             }
220             check(found, "SCTP_PRIMARY_ADDR returned bogus address!");
221 
222             System.out.println("Try SCTP_PRIMARY_ADDR set to: " + addrToSet);
223             smc.setOption(SCTP_PRIMARY_ADDR, addrToSet, assoc);
224             System.out.println("SCTP_PRIMARY_ADDR set to: " + addrToSet);
225             primaryAddr = smc.getOption(SCTP_PRIMARY_ADDR, assoc);
226             System.out.println("SCTP_PRIMARY_ADDR returned: " + primaryAddr);
227             check(addrToSet.equals(primaryAddr),"SCTP_PRIMARY_ADDR not set correctly");
228         }
229     }
230 
231     class SOTNotificationHandler extends AbstractNotificationHandler<Object>
232     {
233         boolean receivedCommUp;  // false
234 
receivedCommUp()235         boolean receivedCommUp() {
236             return receivedCommUp;
237         }
238 
239         @Override
handleNotification( AssociationChangeNotification notification, Object attachment)240         public HandlerResult handleNotification(
241                 AssociationChangeNotification notification, Object attachment) {
242             AssocChangeEvent event = notification.event();
243             debug("AssociationChangeNotification");
244             debug("  Association: " + notification.association());
245             debug("  Event: " + event);
246 
247             if (event.equals(AssocChangeEvent.COMM_UP))
248                 receivedCommUp = true;
249 
250             return HandlerResult.RETURN;
251         }
252     }
253 
254             //--------------------- Infrastructure ---------------------------
255     boolean debug = true;
256     volatile int passed = 0, failed = 0;
pass()257     void pass() {passed++;}
fail()258     void fail() {failed++; Thread.dumpStack();}
fail(String msg)259     void fail(String msg) {System.err.println(msg); fail();}
unexpected(Throwable t)260     void unexpected(Throwable t) {failed++; t.printStackTrace();}
check(boolean cond)261     void check(boolean cond) {if (cond) pass(); else fail();}
check(boolean cond, String failMessage)262     void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);}
debug(String message)263     void debug(String message) {if(debug) { System.out.println(message); }  }
main(String[] args)264     public static void main(String[] args) throws Throwable {
265         Class<?> k = new Object(){}.getClass().getEnclosingClass();
266         try {k.getMethod("instanceMain",String[].class)
267                 .invoke( k.newInstance(), (Object) args);}
268         catch (Throwable e) {throw e.getCause();}}
instanceMain(String[] args)269     public void instanceMain(String[] args) throws Throwable {
270         try {test(args);} catch (Throwable t) {unexpected(t);}
271         System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
272         if (failed > 0) throw new AssertionError("Some tests failed");}
273 }
274