1/*
2 * Copyright (c) 2009, 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
30import java.net.*;
31import java.io.*;
32import java.util.List;
33import java.util.Set;
34import java.util.Iterator;
35import java.nio.ByteBuffer;
36import java.nio.channels.AlreadyBoundException;
37import java.nio.channels.AlreadyConnectedException;
38import java.nio.channels.ClosedChannelException;
39import java.nio.channels.UnsupportedAddressTypeException;
40import com.sun.nio.sctp.AssociationChangeNotification;
41import com.sun.nio.sctp.AbstractNotificationHandler;
42import com.sun.nio.sctp.HandlerResult;
43import com.sun.nio.sctp.IllegalUnbindException;
44import com.sun.nio.sctp.MessageInfo;
45import com.sun.nio.sctp.PeerAddressChangeNotification;
46import com.sun.nio.sctp.SctpChannel;
47import com.sun.nio.sctp.SctpServerChannel;
48import com.sun.nio.sctp.ShutdownNotification;
49import static java.lang.System.out;
50
51/**
52 * Tests bind, bindAddress, unbindAddress, getLocalAddress, and
53 * getAllLocalAddresses.
54 */
55public class Bind {
56     void test(String[] args) {
57        if (!Util.isSCTPSupported()) {
58            out.println("SCTP protocol is not supported");
59            out.println("Test cannot be run");
60            return;
61        }
62
63        /* Simply bind tests */
64        testBind();
65
66        /* Test unconnected */
67        testBindUnbind(false);
68
69        /* Test connected */
70        /* Adding/Removing addresses from a connected association is optional.
71         * This test can be run on systems that support dynamic address
72         * reconfiguration */
73        //testBindUnbind(true);
74    }
75
76    void testBind() {
77        SctpChannel channel = null;
78        try {
79            channel = SctpChannel.open();
80
81            /* TEST 1: empty set if channel is not bound */
82            check(channel.getAllLocalAddresses().isEmpty(),
83                    "getAllLocalAddresses returned non empty set for unbound channel");
84
85            /* TEST 2: null to bind the channel to an automatically assigned
86             *         socket address */
87            channel.bind(null);
88
89            /* TEST 3: non empty set if the channel is bound */
90            check(!channel.getAllLocalAddresses().isEmpty(),
91                    "getAllLocalAddresses returned empty set for bound channel");
92            debug("getAllLocalAddresses on channel bound to the wildcard:\n"
93                    + channel.getAllLocalAddresses());
94
95            /* TEST 4: AlreadyBoundException if this channel is already bound */
96            try { channel.bind(null); }
97            catch (AlreadyBoundException unused) { pass(); }
98            catch (IOException ioe) { unexpected(ioe); }
99
100            /* TEST 5: UnsupportedAddressTypeException */
101            try {
102                channel.close();  /* open a new unbound channel for test */
103                channel = SctpChannel.open();
104                channel.bind(new UnsupportedSocketAddress());
105                fail("UnsupportedSocketAddress expected");
106            } catch (UnsupportedAddressTypeException unused) { pass();
107            } catch (IOException ioe) { unexpected(ioe); }
108
109            /* TEST 6: AlreadyConnectedException */
110            try {
111                channel.close();  /* open a new unbound channel for test */
112                channel = SctpChannel.open();
113                connectChannel(channel);
114                channel.bind(null);
115                fail("AlreadyConnectedException expected");
116            } catch (AlreadyConnectedException unused) { pass();
117            } catch (IOException ioe) { unexpected(ioe); }
118
119            /* TEST 7: ClosedChannelException - If this channel is closed */
120            try {
121                channel.close();  /* open a new unbound channel for test */
122                channel = SctpChannel.open();
123                channel.close();
124                channel.bind(null);
125                fail("ClosedChannelException expected");
126            } catch (ClosedChannelException unused) { pass();
127            } catch (IOException ioe) { unexpected(ioe); }
128
129            /* TEST 8: ClosedChannelException if channel is closed */
130            try {
131                channel.getAllLocalAddresses();
132                fail("should have thrown ClosedChannelException");
133            } catch (ClosedChannelException cce) {
134               pass();
135            } catch (Exception ioe) {
136                unexpected(ioe);
137            }
138        } catch (IOException ioe) {
139            unexpected(ioe);
140        } finally {
141            try { channel.close(); }
142            catch (IOException ioe) { unexpected(ioe); }
143        }
144    }
145
146    void testBindUnbind(boolean connected) {
147        SctpChannel channel = null;
148        SctpChannel peerChannel = null;
149
150        debug("testBindUnbind, connected: " + connected);
151        try {
152            channel = SctpChannel.open();
153
154            List<InetAddress> addresses = Util.getAddresses(true, false);
155            Iterator iterator = addresses.iterator();
156            InetSocketAddress a = new InetSocketAddress((InetAddress)iterator.next(), 0);
157            debug("channel.bind( " + a + ")");
158            channel.bind(a);
159            while (iterator.hasNext()) {
160                InetAddress ia = (InetAddress)iterator.next();
161                debug("channel.bindAddress(" + ia + ")");
162                channel.bindAddress(ia);
163            }
164            if (debug) {Util.dumpAddresses(channel, out);}
165
166            if (connected) {
167                /* Test with connected channel */
168                peerChannel = connectChannel(channel);
169            }
170
171            /* TEST 1: bind/unbindAddresses on the system addresses */
172            debug("bind/unbindAddresses on the system addresses");
173            List<InetAddress> addrs = Util.getAddresses(true, false);
174            for (InetAddress addr : addrs) {
175                try {
176                    debug("unbindAddress: " + addr);
177                    check(boundAddress(channel, addr), "trying to remove address that is not bound");
178                    channel.unbindAddress(addr);
179                    if (debug) {Util.dumpAddresses(channel, out);}
180                    check(!boundAddress(channel, addr), "address was not removed");
181
182                    debug("bindAddress: " + addr);
183                    channel.bindAddress(addr);
184                    if (debug) {Util.dumpAddresses(channel, out);}
185                    check(boundAddress(channel, addr), "address is not bound");
186                } catch (IOException ioe) {
187                    unexpected(ioe);
188                }
189            }
190
191            /* TEST 2: bindAddress - already bound address. */
192            InetAddress againAddress = addrs.get(0);
193            try {
194                debug("bind already bound address " + againAddress);
195                channel.bindAddress(againAddress);
196            } catch (AlreadyBoundException unused) {
197                debug("Caught AlreadyBoundException - OK");
198                pass();
199            } catch (IOException ioe) {
200                unexpected(ioe);
201            }
202
203            /* TEST 3: bind non local address */
204            try {
205                InetAddress nla = InetAddress.getByName("123.123.123.123");
206                debug("bind non local address " + nla);
207                channel.bindAddress(nla);
208            } catch (IOException ioe) {
209                debug("Informative only " + ioe);
210            }
211
212            /* TEST 4: unbind address that is not bound */
213            try {
214                debug("unbind address that is not bound " + againAddress);
215                /* remove address first then again */
216                channel.unbindAddress(againAddress);
217                channel.unbindAddress(againAddress);
218            } catch (IllegalUnbindException unused) {
219                debug("Caught IllegalUnbindException - OK");
220                pass();
221            } catch (IOException ioe) {
222                unexpected(ioe);
223            }
224
225            /* TEST 5: unbind address that is not bound */
226            try {
227                InetAddress nla = InetAddress.getByName("123.123.123.123");
228                debug("unbind address that is not bound " + nla);
229                channel.unbindAddress(nla);
230
231            } catch (IllegalUnbindException unused) {
232                debug("Caught IllegalUnbindException - OK");
233                pass();
234            } catch (IOException ioe) {
235                unexpected(ioe);
236            }
237
238            if (connected) {
239                channel.shutdown();
240
241                BindNotificationHandler handler = new BindNotificationHandler();
242                ByteBuffer buffer = ByteBuffer.allocate(10);
243                MessageInfo info;
244                while((info = peerChannel.receive(buffer, null, handler)) != null) {
245                    if (info != null) {
246                        if (info.bytes() == -1) {
247                            debug("peerChannel Reached EOF");
248                            break;
249                        }
250                    }
251                }
252
253                while((info = channel.receive(buffer, null, handler)) != null) {
254                    if (info != null) {
255                        if (info.bytes() == -1) {
256                            debug("channel Reached EOF");
257                            break;
258                        }
259                    }
260                }
261            }
262        } catch (IOException ioe) {
263            ioe.printStackTrace();
264        } finally {
265            try { if (channel != null) channel.close(); }
266            catch (IOException ioe) { unexpected(ioe); }
267        }
268    }
269
270    boolean boundAddress(SctpChannel channel, InetAddress addr)
271        throws IOException {
272        for (SocketAddress boundAddr : channel.getAllLocalAddresses()) {
273            if (((InetSocketAddress) boundAddr).getAddress().equals(addr))
274                return true;
275        }
276        return false;
277    }
278
279    SctpChannel connectChannel(SctpChannel channel)
280        throws IOException {
281        debug("connecting channel...");
282        try {
283            SctpServerChannel ssc = SctpServerChannel.open();
284            ssc.bind(null);
285            Set<SocketAddress> addrs = ssc.getAllLocalAddresses();
286            Iterator<SocketAddress> iterator = addrs.iterator();
287            SocketAddress addr = iterator.next();
288            debug("using " + addr + "...");
289            channel.connect(addr);
290            SctpChannel peerChannel = ssc.accept();
291            ssc.close();
292            debug("connected");
293            return peerChannel;
294        } catch (IOException ioe) {
295            debug("Cannot connect channel");
296            unexpected(ioe);
297            throw ioe;
298        }
299    }
300
301    class BindNotificationHandler extends AbstractNotificationHandler<Object>
302    {
303        @Override
304        public HandlerResult handleNotification(
305                AssociationChangeNotification acn, Object unused)
306        {
307            debug("AssociationChangeNotification: " +  acn);
308            return HandlerResult.CONTINUE;
309        }
310
311        @Override
312        public HandlerResult handleNotification(
313                PeerAddressChangeNotification pacn, Object unused)
314        {
315            debug("PeerAddressChangeNotification: " +  pacn);
316            return HandlerResult.CONTINUE;
317        }
318
319        @Override
320        public HandlerResult handleNotification(
321                ShutdownNotification sn, Object unused)
322        {
323            debug("ShutdownNotification: " +  sn);
324            return HandlerResult.CONTINUE;
325        }
326    }
327
328    class UnsupportedSocketAddress extends SocketAddress { }
329
330    //--------------------- Infrastructure ---------------------------
331    boolean debug = true;
332    volatile int passed = 0, failed = 0;
333    void pass() {passed++;}
334    void fail() {failed++; Thread.dumpStack();}
335    void fail(String msg) {System.err.println(msg); fail();}
336    void unexpected(Throwable t) {failed++; t.printStackTrace();}
337    void check(boolean cond) {if (cond) pass(); else fail();}
338    void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);}
339    void debug(String message) {if(debug) { System.out.println(message); }  }
340    public static void main(String[] args) throws Throwable {
341        Class<?> k = new Object(){}.getClass().getEnclosingClass();
342        try {k.getMethod("instanceMain",String[].class)
343                .invoke( k.newInstance(), (Object) args);}
344        catch (Throwable e) {throw e.getCause();}}
345    public void instanceMain(String[] args) throws Throwable {
346        try {test(args);} catch (Throwable t) {unexpected(t);}
347        System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
348        if (failed > 0) throw new AssertionError("Some tests failed");}
349
350}
351