1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package libcore.java.nio.channels;
18
19import junit.framework.AssertionFailedError;
20import junit.framework.TestCase;
21import java.io.IOException;
22import java.net.Inet4Address;
23import java.net.Inet6Address;
24import java.net.InetAddress;
25import java.net.InetSocketAddress;
26import java.net.InterfaceAddress;
27import java.net.NetworkInterface;
28import java.net.SocketAddress;
29import java.net.SocketException;
30import java.net.SocketTimeoutException;
31import java.net.StandardSocketOptions;
32import java.nio.ByteBuffer;
33import java.nio.channels.ClosedChannelException;
34import java.nio.channels.DatagramChannel;
35import java.nio.channels.MembershipKey;
36import java.util.ArrayList;
37import java.util.Enumeration;
38
39import libcore.io.IoBridge;
40
41import static android.system.OsConstants.POLLIN;
42
43/**
44 * Tests associated with multicast behavior of DatagramChannel.
45 * These tests require IPv6 multicasting enabled network.
46 */
47public class DatagramChannelMulticastTest extends TestCase {
48
49    private static InetAddress lookup(String s) {
50        try {
51            return InetAddress.getByName(s);
52        } catch (IOException ex) {
53            throw new RuntimeException(ex);
54        }
55    }
56
57    // These IP addresses aren't inherently "good" or "bad"; they're just used like that.
58    // We use the "good" addresses for our actual group, and the "bad" addresses are for
59    // a group that we won't actually set up.
60    private static final InetAddress GOOD_MULTICAST_IPv4 = lookup("239.255.0.1");
61    private static final InetAddress BAD_MULTICAST_IPv4 = lookup("239.255.0.2");
62    private static final InetAddress GOOD_MULTICAST_IPv6 = lookup("ff05::7:7");
63    private static final InetAddress BAD_MULTICAST_IPv6 = lookup("ff05::7:8");
64
65    // Special addresses.
66    private static final InetAddress WILDCARD_IPv4 = lookup("0.0.0.0");
67    private static final InetAddress WILDCARD_IPv6 = lookup("::");
68
69    // Arbitrary unicast addresses. Used when the value doesn't actually matter. e.g. for source
70    // filters.
71    private static final InetAddress UNICAST_IPv4_1 = lookup("192.168.1.1");
72    private static final InetAddress UNICAST_IPv4_2 = lookup("192.168.1.2");
73    private static final InetAddress UNICAST_IPv6_1 = lookup("2001:db8::1");
74    private static final InetAddress UNICAST_IPv6_2 = lookup("2001:db8::2");
75
76    private NetworkInterface ipv4NetworkInterface;
77    private NetworkInterface ipv6NetworkInterface;
78    private NetworkInterface loopbackInterface;
79
80    private boolean supportsMulticast;
81
82    @Override
83    protected void setUp() throws Exception {
84        // The loopback interface isn't actually useful for sending/receiving multicast messages
85        // but it can be used as a dummy for tests where that does not matter.
86        loopbackInterface = NetworkInterface.getByInetAddress(InetAddress.getLoopbackAddress());
87        assertNotNull(loopbackInterface);
88        assertTrue(loopbackInterface.isLoopback());
89        assertFalse(loopbackInterface.supportsMulticast());
90
91        Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
92
93        // Determine if the device is marked to support multicast or not. If this propery is not
94        // set we assume the device has an interface capable of supporting multicast.
95        supportsMulticast = Boolean.parseBoolean(
96                System.getProperty("android.cts.device.multicast", "true"));
97        if (!supportsMulticast) {
98            return;
99        }
100
101        while (interfaces.hasMoreElements()
102                && (ipv4NetworkInterface == null || ipv6NetworkInterface == null)) {
103            NetworkInterface nextInterface = interfaces.nextElement();
104            if (willWorkForMulticast(nextInterface)) {
105                Enumeration<InetAddress> addresses = nextInterface.getInetAddresses();
106                while (addresses.hasMoreElements()) {
107                    final InetAddress nextAddress = addresses.nextElement();
108                    if (nextAddress instanceof Inet6Address && ipv6NetworkInterface == null) {
109                        ipv6NetworkInterface = nextInterface;
110                    } else if (nextAddress instanceof Inet4Address
111                            && ipv4NetworkInterface == null) {
112                        ipv4NetworkInterface = nextInterface;
113                    }
114                }
115            }
116        }
117
118        if (ipv4NetworkInterface == null) {
119            fail("Test environment must have at least one network interface capable of IPv4"
120                    + " multicast");
121        }
122        if (ipv6NetworkInterface == null) {
123            fail("Test environment must have at least one network interface capable of IPv6"
124                    + " multicast");
125        }
126    }
127
128    public void test_open() throws IOException {
129        DatagramChannel dc = DatagramChannel.open();
130
131        // Unlike MulticastSocket, DatagramChannel has SO_REUSEADDR set to false by default.
132        assertFalse(dc.getOption(StandardSocketOptions.SO_REUSEADDR));
133
134        assertNull(dc.getLocalAddress());
135        assertTrue(dc.isOpen());
136        assertFalse(dc.isConnected());
137    }
138
139    public void test_bind_null() throws Exception {
140        DatagramChannel dc = createReceiverChannel();
141        assertNotNull(dc.getLocalAddress());
142        assertTrue(dc.isOpen());
143        assertFalse(dc.isConnected());
144
145        dc.close();
146        try {
147            dc.getLocalAddress();
148            fail();
149        } catch (ClosedChannelException expected) {
150        }
151        assertFalse(dc.isOpen());
152        assertFalse(dc.isConnected());
153    }
154
155    public void test_joinAnySource_afterClose() throws Exception {
156        if (!supportsMulticast) {
157            return;
158        }
159        DatagramChannel dc = createReceiverChannel();
160        dc.close();
161        try {
162            dc.join(GOOD_MULTICAST_IPv4, ipv4NetworkInterface);
163            fail();
164        } catch (ClosedChannelException expected) {
165        }
166    }
167
168    public void test_joinAnySource_nullGroupAddress() throws Exception {
169        if (!supportsMulticast) {
170            return;
171        }
172        DatagramChannel dc = createReceiverChannel();
173        try {
174            dc.join(null, ipv4NetworkInterface);
175            fail();
176        } catch (NullPointerException expected) {
177        }
178        dc.close();
179    }
180
181    public void test_joinAnySource_nullNetworkInterface() throws Exception {
182        if (!supportsMulticast) {
183            return;
184        }
185        DatagramChannel dc = createReceiverChannel();
186        try {
187            dc.join(GOOD_MULTICAST_IPv4, null);
188            fail();
189        } catch (NullPointerException expected) {
190        }
191        dc.close();
192    }
193
194    public void test_joinAnySource_nonMulticastGroupAddress_IPv4() throws Exception {
195        if (!supportsMulticast) {
196            return;
197        }
198        DatagramChannel dc = createReceiverChannel();
199        try {
200            dc.join(UNICAST_IPv4_1, ipv4NetworkInterface);
201            fail();
202        } catch (IllegalArgumentException expected) {
203        }
204        dc.close();
205    }
206
207    public void test_joinAnySource_nonMulticastGroupAddress_IPv6() throws Exception {
208        if (!supportsMulticast) {
209            return;
210        }
211        DatagramChannel dc = createReceiverChannel();
212        try {
213            dc.join(UNICAST_IPv6_1, ipv6NetworkInterface);
214            fail();
215        } catch (IllegalArgumentException expected) {
216        }
217        dc.close();
218    }
219
220    public void test_joinAnySource_IPv4() throws Exception {
221        check_joinAnySource(GOOD_MULTICAST_IPv4, BAD_MULTICAST_IPv4, ipv4NetworkInterface);
222    }
223
224    public void test_joinAnySource_IPv6() throws Exception {
225        check_joinAnySource(GOOD_MULTICAST_IPv6, BAD_MULTICAST_IPv6, ipv6NetworkInterface);
226    }
227
228    private void check_joinAnySource(InetAddress group, InetAddress group2,
229            NetworkInterface networkInterface) throws Exception {
230        if (!supportsMulticast) {
231            return;
232        }
233        // Set up a receiver join the group on ipv4NetworkInterface
234        DatagramChannel receiverChannel = createReceiverChannel();
235        InetSocketAddress localAddress = (InetSocketAddress) receiverChannel.getLocalAddress();
236        receiverChannel.join(group, networkInterface);
237
238        String msg = "Hello World";
239        sendMulticastMessage(group, localAddress.getPort(), msg, networkInterface);
240
241        // now verify that we received the data as expected
242        ByteBuffer recvBuffer = ByteBuffer.allocate(100);
243        SocketAddress sourceAddress = receiverChannel.receive(recvBuffer);
244        assertNotNull(sourceAddress);
245        assertEquals(msg, new String(recvBuffer.array(), 0, recvBuffer.position()));
246
247        // now verify that we didn't receive the second message
248        String msg2 = "Hello World - Different Group";
249        sendMulticastMessage(group2, localAddress.getPort(), msg2, networkInterface);
250        recvBuffer.position(0);
251        SocketAddress sourceAddress2 = receiverChannel.receive(recvBuffer);
252        assertNull(sourceAddress2);
253
254        receiverChannel.close();
255    }
256
257    public void test_joinAnySource_processLimit() throws Exception {
258        if (!supportsMulticast) {
259            return;
260        }
261        DatagramChannel dc = createReceiverChannel();
262        for (byte i = 1; i <= 25; i++) {
263            InetAddress groupAddress = Inet4Address.getByName("239.255.0." + i);
264            try {
265                dc.join(groupAddress, ipv4NetworkInterface);
266            } catch (SocketException e) {
267                // There is a limit, that's ok according to the RI docs. For this test a lower bound of 20
268                // is used, which appears to be the default linux limit.
269                // See /proc/sys/net/ipv4/igmp_max_memberships
270                assertTrue(i > 20);
271                break;
272            }
273        }
274
275        dc.close();
276    }
277
278    public void test_joinAnySource_blockLimit() throws Exception {
279        if (!supportsMulticast) {
280            return;
281        }
282        DatagramChannel dc = createReceiverChannel();
283        MembershipKey key = dc.join(GOOD_MULTICAST_IPv4, ipv4NetworkInterface);
284        for (byte i = 1; i <= 15; i++) {
285            InetAddress sourceAddress = Inet4Address.getByName("10.0.0." + i);
286            try {
287                key.block(sourceAddress);
288            } catch (SocketException e) {
289                // There is a limit, that's ok according to the RI docs. For this test a lower bound of 10
290                // is used, which appears to be the default linux limit.
291                // See /proc/sys/net/ipv4/igmp_max_msf
292                assertTrue(i > 10);
293                break;
294            }
295        }
296
297        dc.close();
298    }
299
300    /** Confirms that calling join() does not cause an implicit bind() to take place. */
301    public void test_joinAnySource_doesNotCauseBind() throws Exception {
302        if (!supportsMulticast) {
303            return;
304        }
305        DatagramChannel dc = DatagramChannel.open();
306        dc.join(GOOD_MULTICAST_IPv4, ipv4NetworkInterface);
307        assertNull(dc.getLocalAddress());
308
309        dc.close();
310    }
311
312    public void test_joinAnySource_networkInterfaces() throws Exception {
313        if (!supportsMulticast) {
314            return;
315        }
316        // Check that we can join on specific interfaces and that we only receive if data is
317        // received on that interface. This test is only really useful on devices with multiple
318        // non-loopback interfaces.
319
320        ArrayList<NetworkInterface> realInterfaces = new ArrayList<NetworkInterface>();
321        Enumeration<NetworkInterface> theInterfaces = NetworkInterface.getNetworkInterfaces();
322        while (theInterfaces.hasMoreElements()) {
323            NetworkInterface thisInterface = theInterfaces.nextElement();
324            if (thisInterface.getInetAddresses().hasMoreElements()) {
325                realInterfaces.add(thisInterface);
326            }
327        }
328
329        for (int i = 0; i < realInterfaces.size(); i++) {
330            NetworkInterface thisInterface = realInterfaces.get(i);
331            if (!thisInterface.supportsMulticast()) {
332                // Skip interfaces that do not support multicast - there's no point in proving
333                // they cannot send / receive multicast messages.
334                continue;
335            }
336
337            // get the first address on the interface
338
339            // start server which is joined to the group and has
340            // only asked for packets on this interface
341            Enumeration<InetAddress> addresses = thisInterface.getInetAddresses();
342
343            NetworkInterface sendingInterface = null;
344            InetAddress group = null;
345            if (addresses.hasMoreElements()) {
346                InetAddress firstAddress = addresses.nextElement();
347                if (firstAddress instanceof Inet4Address) {
348                    group = GOOD_MULTICAST_IPv4;
349                    sendingInterface = ipv4NetworkInterface;
350                } else {
351                    // if this interface only seems to support IPV6 addresses
352                    group = GOOD_MULTICAST_IPv6;
353                    sendingInterface = ipv6NetworkInterface;
354                }
355            }
356
357            DatagramChannel dc = createReceiverChannel();
358            InetSocketAddress localAddress = (InetSocketAddress) dc.getLocalAddress();
359            dc.join(group, thisInterface);
360
361            // Now send out a package on sendingInterface. We should only see the packet if we send
362            // it on the same interface we are listening on (thisInterface).
363            String msg = "Hello World - Again" + thisInterface.getName();
364            sendMulticastMessage(group, localAddress.getPort(), msg, sendingInterface);
365
366            ByteBuffer recvBuffer = ByteBuffer.allocate(100);
367            SocketAddress sourceAddress = dc.receive(recvBuffer);
368            if (thisInterface.equals(sendingInterface)) {
369                assertEquals(msg, new String(recvBuffer.array(), 0, recvBuffer.position()));
370            } else {
371                assertNull(sourceAddress);
372            }
373
374            dc.close();
375        }
376    }
377
378    /** Confirms that the scope of each membership is network interface-level. */
379    public void test_join_canMixTypesOnDifferentInterfaces() throws Exception {
380        if (!supportsMulticast) {
381            return;
382        }
383        DatagramChannel dc = DatagramChannel.open();
384        MembershipKey membershipKey1 = dc.join(GOOD_MULTICAST_IPv4, ipv4NetworkInterface);
385        MembershipKey membershipKey2 = dc.join(GOOD_MULTICAST_IPv4, loopbackInterface, UNICAST_IPv4_1);
386        assertNotSame(membershipKey1, membershipKey2);
387
388        dc.close();
389    }
390
391
392    private DatagramChannel createReceiverChannel() throws Exception {
393        DatagramChannel dc = DatagramChannel.open();
394        dc.bind(null /* leave the OS to determine the port, and use the wildcard address */);
395        configureChannelForReceiving(dc);
396        return dc;
397    }
398
399    public void test_joinAnySource_multiple_joins_IPv4()
400            throws Exception {
401        check_joinAnySource_multiple_joins(GOOD_MULTICAST_IPv4, ipv4NetworkInterface);
402    }
403
404    public void test_joinAnySource_multiple_joins_IPv6()
405            throws Exception {
406        check_joinAnySource_multiple_joins(GOOD_MULTICAST_IPv6, ipv6NetworkInterface);
407    }
408
409    private void check_joinAnySource_multiple_joins(InetAddress group,
410            NetworkInterface networkInterface) throws Exception {
411        if (!supportsMulticast) {
412            return;
413        }
414        DatagramChannel dc = createReceiverChannel();
415
416        MembershipKey membershipKey1 = dc.join(group, networkInterface);
417
418        MembershipKey membershipKey2 = dc.join(group, loopbackInterface);
419        assertFalse(membershipKey1.equals(membershipKey2));
420
421        MembershipKey membershipKey1_2 = dc.join(group, networkInterface);
422        assertEquals(membershipKey1, membershipKey1_2);
423
424        dc.close();
425    }
426
427    public void test_joinAnySource_multicastLoopOption_IPv4() throws Exception {
428        check_joinAnySource_multicastLoopOption(GOOD_MULTICAST_IPv4, ipv4NetworkInterface);
429    }
430
431    public void test_multicastLoopOption_IPv6() throws Exception {
432        check_joinAnySource_multicastLoopOption(GOOD_MULTICAST_IPv6, ipv6NetworkInterface);
433    }
434
435    private void check_joinAnySource_multicastLoopOption(InetAddress group,
436            NetworkInterface networkInterface) throws Exception {
437        if (!supportsMulticast) {
438            return;
439        }
440        final String message = "Hello, world!";
441
442        DatagramChannel dc = createReceiverChannel();
443        dc.setOption(StandardSocketOptions.IP_MULTICAST_LOOP, true /* enable loop */);
444        dc.setOption(StandardSocketOptions.IP_MULTICAST_IF, networkInterface);
445        configureChannelForReceiving(dc);
446        dc.join(group, networkInterface);
447
448        InetSocketAddress localAddress = (InetSocketAddress) dc.getLocalAddress();
449
450        // send the datagram
451        byte[] sendData = message.getBytes();
452        ByteBuffer sendBuffer = ByteBuffer.wrap(sendData);
453        dc.send(sendBuffer, new InetSocketAddress(group, localAddress.getPort()));
454
455        // receive the datagram
456        ByteBuffer recvBuffer = ByteBuffer.allocate(100);
457        SocketAddress sourceAddress = dc.receive(recvBuffer);
458        assertNotNull(sourceAddress);
459
460        String recvMessage = new String(recvBuffer.array(), 0, recvBuffer.position());
461        assertEquals(message, recvMessage);
462
463        // Turn off loop
464        dc.setOption(StandardSocketOptions.IP_MULTICAST_LOOP, false /* enable loopback */);
465
466        // send another datagram
467        recvBuffer.position(0);
468        ByteBuffer sendBuffer2 = ByteBuffer.wrap(sendData);
469        dc.send(sendBuffer2, new InetSocketAddress(group, localAddress.getPort()));
470
471        SocketAddress sourceAddress2 = dc.receive(recvBuffer);
472        assertNull(sourceAddress2);
473
474        dc.close();
475    }
476
477    public void testMembershipKeyAccessors_IPv4() throws Exception {
478        checkMembershipKeyAccessors(GOOD_MULTICAST_IPv4, ipv4NetworkInterface);
479    }
480
481    public void testMembershipKeyAccessors_IPv6() throws Exception {
482        checkMembershipKeyAccessors(GOOD_MULTICAST_IPv6, ipv6NetworkInterface);
483    }
484
485    private void checkMembershipKeyAccessors(InetAddress group,
486            NetworkInterface networkInterface) throws Exception {
487        if (!supportsMulticast) {
488            return;
489        }
490        DatagramChannel dc = createReceiverChannel();
491
492        MembershipKey key = dc.join(group, networkInterface);
493        assertSame(dc, key.channel());
494        assertSame(group, key.group());
495        assertTrue(key.isValid());
496        assertSame(networkInterface, key.networkInterface());
497        assertNull(key.sourceAddress());
498        dc.close();
499    }
500
501    public void test_dropAnySource_twice_IPv4() throws Exception {
502        check_dropAnySource_twice(GOOD_MULTICAST_IPv4, ipv4NetworkInterface);
503    }
504
505    public void test_dropAnySource_twice_IPv6() throws Exception {
506        check_dropAnySource_twice(GOOD_MULTICAST_IPv6, ipv6NetworkInterface);
507    }
508
509    private void check_dropAnySource_twice(InetAddress group,
510            NetworkInterface networkInterface) throws Exception {
511        if (!supportsMulticast) {
512            return;
513        }
514        DatagramChannel dc = createReceiverChannel();
515        MembershipKey membershipKey = dc.join(group, networkInterface);
516
517        assertTrue(membershipKey.isValid());
518        membershipKey.drop();
519        assertFalse(membershipKey.isValid());
520
521        // Try to leave a group we are no longer a member of - should do nothing.
522        membershipKey.drop();
523
524        dc.close();
525    }
526
527    public void test_close_invalidatesMembershipKey() throws Exception {
528        if (!supportsMulticast) {
529            return;
530        }
531        DatagramChannel dc = createReceiverChannel();
532        MembershipKey membershipKey = dc.join(GOOD_MULTICAST_IPv4, ipv4NetworkInterface);
533
534        assertTrue(membershipKey.isValid());
535
536        dc.close();
537
538        assertFalse(membershipKey.isValid());
539    }
540
541    public void test_block_null() throws Exception {
542        if (!supportsMulticast) {
543            return;
544        }
545        DatagramChannel dc = createReceiverChannel();
546        MembershipKey membershipKey = dc.join(GOOD_MULTICAST_IPv4, ipv4NetworkInterface);
547        try {
548            membershipKey.block(null);
549            fail();
550        } catch (NullPointerException expected) {
551        }
552
553        dc.close();
554    }
555
556    public void test_block_mixedAddressTypes_IPv4() throws Exception {
557        if (!supportsMulticast) {
558            return;
559        }
560        DatagramChannel dc = createReceiverChannel();
561        MembershipKey membershipKey = dc.join(GOOD_MULTICAST_IPv4, ipv4NetworkInterface);
562        try {
563            membershipKey.block(UNICAST_IPv6_1);
564            fail();
565        } catch (IllegalArgumentException expected) {
566        }
567
568        dc.close();
569    }
570
571    public void test_block_mixedAddressTypes_IPv6() throws Exception {
572        if (!supportsMulticast) {
573            return;
574        }
575        DatagramChannel dc = createReceiverChannel();
576        MembershipKey membershipKey = dc.join(GOOD_MULTICAST_IPv6, ipv6NetworkInterface);
577        try {
578            membershipKey.block(UNICAST_IPv4_1);
579            fail();
580        } catch (IllegalArgumentException expected) {
581        }
582
583        dc.close();
584    }
585
586    public void test_block_cannotBlockWithSourceSpecificMembership() throws Exception {
587        if (!supportsMulticast) {
588            return;
589        }
590        DatagramChannel dc = createReceiverChannel();
591        MembershipKey membershipKey = dc.join(GOOD_MULTICAST_IPv4, ipv4NetworkInterface, UNICAST_IPv4_1);
592        try {
593            membershipKey.block(UNICAST_IPv4_2);
594            fail();
595        } catch (IllegalStateException expected) {
596        }
597
598        dc.close();
599    }
600
601    public void test_block_multipleBlocksIgnored() throws Exception {
602        if (!supportsMulticast) {
603            return;
604        }
605        DatagramChannel dc = createReceiverChannel();
606        MembershipKey membershipKey = dc.join(GOOD_MULTICAST_IPv4, ipv4NetworkInterface);
607        membershipKey.block(UNICAST_IPv4_1);
608
609        MembershipKey membershipKey2 = membershipKey.block(UNICAST_IPv4_1);
610        assertSame(membershipKey2, membershipKey);
611
612        dc.close();
613    }
614
615    public void test_block_wildcardAddress() throws Exception {
616        if (!supportsMulticast) {
617            return;
618        }
619        DatagramChannel dc = createReceiverChannel();
620        MembershipKey membershipKey = dc.join(GOOD_MULTICAST_IPv4, ipv4NetworkInterface);
621        try {
622            membershipKey.block(WILDCARD_IPv4);
623            fail();
624        } catch (IllegalArgumentException expected) {
625        }
626
627        dc.close();
628    }
629
630    public void test_unblock_multipleUnblocksFail() throws Exception {
631        if (!supportsMulticast) {
632            return;
633        }
634        DatagramChannel dc = createReceiverChannel();
635        MembershipKey membershipKey = dc.join(GOOD_MULTICAST_IPv4, ipv4NetworkInterface);
636
637        try {
638            membershipKey.unblock(UNICAST_IPv4_1);
639            fail();
640        } catch (IllegalStateException expected) {
641        }
642
643        assertTrue(membershipKey.isValid());
644
645        membershipKey.block(UNICAST_IPv4_1);
646        membershipKey.unblock(UNICAST_IPv4_1);
647
648        try {
649            membershipKey.unblock(UNICAST_IPv4_1);
650            fail();
651        } catch (IllegalStateException expected) {
652        }
653
654        dc.close();
655    }
656
657    public void test_unblock_null() throws Exception {
658        if (!supportsMulticast) {
659            return;
660        }
661        DatagramChannel dc = createReceiverChannel();
662        MembershipKey membershipKey = dc.join(GOOD_MULTICAST_IPv4, ipv4NetworkInterface);
663        membershipKey.block(UNICAST_IPv4_1);
664
665        try {
666            membershipKey.unblock(null);
667            fail();
668        } catch (IllegalStateException expected) {
669            // Either of these exceptions are fine
670        } catch (NullPointerException expected) {
671            // Either of these exception are fine
672        }
673
674        dc.close();
675    }
676
677    public void test_unblock_mixedAddressTypes_IPv4() throws Exception {
678        if (!supportsMulticast) {
679            return;
680        }
681        DatagramChannel dc = createReceiverChannel();
682        MembershipKey membershipKey = dc.join(GOOD_MULTICAST_IPv4, ipv4NetworkInterface);
683        try {
684            membershipKey.unblock(UNICAST_IPv6_1);
685            fail();
686        } catch (IllegalStateException expected) {
687            // Either of these exceptions are fine
688        } catch (IllegalArgumentException expected) {
689            // Either of these exceptions are fine
690        }
691
692        dc.close();
693    }
694
695    public void test_unblock_mixedAddressTypes_IPv6() throws Exception {
696        if (!supportsMulticast) {
697            return;
698        }
699        DatagramChannel dc = createReceiverChannel();
700        MembershipKey membershipKey = dc.join(GOOD_MULTICAST_IPv6, ipv6NetworkInterface);
701        try {
702            membershipKey.unblock(UNICAST_IPv4_1);
703            fail();
704        } catch (IllegalStateException expected) {
705            // Either of these exceptions are fine
706        } catch (IllegalArgumentException expected) {
707            // Either of these exceptions are fine
708        }
709
710        dc.close();
711    }
712
713    /** Checks that block() works when the receiver is bound to the multicast group address */
714    public void test_block_filtersAsExpected_groupBind_ipv4() throws Exception {
715        if (!supportsMulticast) {
716            return;
717        }
718        InetAddress ipv4LocalAddress = getLocalIpv4Address(ipv4NetworkInterface);
719        check_block_filtersAsExpected(
720                ipv4LocalAddress /* senderBindAddress */,
721                GOOD_MULTICAST_IPv4 /* receiverBindAddress */,
722                GOOD_MULTICAST_IPv4 /* groupAddress */,
723                ipv4NetworkInterface);
724    }
725
726    /** Checks that block() works when the receiver is bound to the multicast group address */
727    public void test_block_filtersAsExpected_groupBind_ipv6() throws Exception {
728        if (!supportsMulticast) {
729            return;
730        }
731        InetAddress ipv6LocalAddress = getLocalIpv6Address(ipv6NetworkInterface);
732        check_block_filtersAsExpected(
733                ipv6LocalAddress /* senderBindAddress */,
734                GOOD_MULTICAST_IPv6 /* receiverBindAddress */,
735                GOOD_MULTICAST_IPv6 /* groupAddress */,
736                ipv6NetworkInterface);
737    }
738
739    /** Checks that block() works when the receiver is bound to the "any" address */
740    public void test_block_filtersAsExpected_anyBind_ipv4() throws Exception {
741        if (!supportsMulticast) {
742            return;
743        }
744        InetAddress ipv4LocalAddress = getLocalIpv4Address(ipv4NetworkInterface);
745        check_block_filtersAsExpected(
746                ipv4LocalAddress /* senderBindAddress */,
747                WILDCARD_IPv4 /* receiverBindAddress */,
748                GOOD_MULTICAST_IPv4 /* groupAddress */,
749                ipv4NetworkInterface);
750    }
751
752    /** Checks that block() works when the receiver is bound to the "any" address */
753    public void test_block_filtersAsExpected_anyBind_ipv6() throws Exception {
754        if (!supportsMulticast) {
755            return;
756        }
757        InetAddress ipv6LocalAddress = getLocalIpv6Address(ipv6NetworkInterface);
758        check_block_filtersAsExpected(
759                ipv6LocalAddress /* senderBindAddress */,
760                WILDCARD_IPv6 /* receiverBindAddress */,
761                GOOD_MULTICAST_IPv6 /* groupAddress */,
762                ipv6NetworkInterface);
763    }
764
765    private void check_block_filtersAsExpected(
766            InetAddress senderBindAddress, InetAddress receiverBindAddress,
767            InetAddress groupAddress, NetworkInterface networkInterface)
768            throws Exception {
769        DatagramChannel sendingChannel = DatagramChannel.open();
770        // In order to block a sender the sender's address must be known. The sendingChannel is
771        // explicitly bound to a known, non-loopback address.
772        sendingChannel.bind(new InetSocketAddress(senderBindAddress, 0));
773        InetSocketAddress sendingAddress = (InetSocketAddress) sendingChannel.getLocalAddress();
774
775        DatagramChannel receivingChannel = DatagramChannel.open();
776        configureChannelForReceiving(receivingChannel);
777        receivingChannel.bind(
778                new InetSocketAddress(receiverBindAddress, 0) /* local port left to the OS to determine */);
779        InetSocketAddress localReceivingAddress =
780                (InetSocketAddress) receivingChannel.getLocalAddress();
781        InetSocketAddress groupSocketAddress =
782                new InetSocketAddress(groupAddress, localReceivingAddress.getPort());
783        MembershipKey membershipKey =
784                receivingChannel.join(groupSocketAddress.getAddress(), networkInterface);
785
786        ByteBuffer receiveBuffer = ByteBuffer.allocate(10);
787
788        // Send a message. It should be received.
789        String msg1 = "Hello1";
790        sendMessage(sendingChannel, msg1, groupSocketAddress);
791        IoBridge.poll(receivingChannel.socket().getFileDescriptor$(), POLLIN, 1000);
792        InetSocketAddress sourceAddress1 = (InetSocketAddress) receivingChannel.receive(receiveBuffer);
793        assertEquals(sourceAddress1, sendingAddress);
794        assertEquals(msg1, new String(receiveBuffer.array(), 0, receiveBuffer.position()));
795
796        // Now block the sender
797        membershipKey.block(sendingAddress.getAddress());
798
799        // Send a message. It should be filtered.
800        String msg2 = "Hello2";
801        sendMessage(sendingChannel, msg2, groupSocketAddress);
802        try {
803            IoBridge.poll(receivingChannel.socket().getFileDescriptor$(), POLLIN, 1000);
804            fail();
805        } catch (SocketTimeoutException expected) { }
806        receiveBuffer.position(0);
807        InetSocketAddress sourceAddress2 = (InetSocketAddress) receivingChannel.receive(receiveBuffer);
808        assertNull(sourceAddress2);
809
810        // Now unblock the sender
811        membershipKey.unblock(sendingAddress.getAddress());
812
813        // Send a message. It should be received.
814        String msg3 = "Hello3";
815        sendMessage(sendingChannel, msg3, groupSocketAddress);
816        IoBridge.poll(receivingChannel.socket().getFileDescriptor$(), POLLIN, 1000);
817        receiveBuffer.position(0);
818        InetSocketAddress sourceAddress3 = (InetSocketAddress) receivingChannel.receive(receiveBuffer);
819        assertEquals(sourceAddress3, sendingAddress);
820        assertEquals(msg3, new String(receiveBuffer.array(), 0, receiveBuffer.position()));
821
822        sendingChannel.close();
823        receivingChannel.close();
824    }
825
826    public void test_joinSourceSpecific_nullGroupAddress() throws Exception {
827        if (!supportsMulticast) {
828            return;
829        }
830        DatagramChannel dc = createReceiverChannel();
831        try {
832            dc.join(null, ipv4NetworkInterface, UNICAST_IPv4_1);
833            fail();
834        } catch (NullPointerException expected) {
835        }
836        dc.close();
837    }
838
839    public void test_joinSourceSpecific_afterClose() throws Exception {
840        if (!supportsMulticast) {
841            return;
842        }
843        DatagramChannel dc = createReceiverChannel();
844        dc.close();
845        try {
846            dc.join(GOOD_MULTICAST_IPv4, ipv4NetworkInterface, UNICAST_IPv4_1);
847            fail();
848        } catch (ClosedChannelException expected) {
849        }
850    }
851
852    public void test_joinSourceSpecific_nullNetworkInterface() throws Exception {
853        if (!supportsMulticast) {
854            return;
855        }
856        DatagramChannel dc = createReceiverChannel();
857        try {
858            dc.join(GOOD_MULTICAST_IPv4, null, UNICAST_IPv4_1);
859            fail();
860        } catch (NullPointerException expected) {
861        }
862        dc.close();
863    }
864
865    public void test_joinSourceSpecific_nonMulticastGroupAddress_IPv4() throws Exception {
866        if (!supportsMulticast) {
867            return;
868        }
869        DatagramChannel dc = createReceiverChannel();
870        try {
871            dc.join(UNICAST_IPv4_1, ipv4NetworkInterface, UNICAST_IPv4_1);
872            fail();
873        } catch (IllegalArgumentException expected) {
874        }
875        dc.close();
876    }
877
878    public void test_joinSourceSpecific_nonMulticastGroupAddress_IPv6() throws Exception {
879        if (!supportsMulticast) {
880            return;
881        }
882        DatagramChannel dc = createReceiverChannel();
883        try {
884            dc.join(UNICAST_IPv6_1, ipv6NetworkInterface, UNICAST_IPv6_1);
885            fail();
886        } catch (IllegalArgumentException expected) {
887        }
888        dc.close();
889    }
890
891    public void test_joinSourceSpecific_nullSourceAddress_IPv4() throws Exception {
892        if (!supportsMulticast) {
893            return;
894        }
895        DatagramChannel dc = createReceiverChannel();
896        try {
897            dc.join(GOOD_MULTICAST_IPv4, ipv4NetworkInterface, null);
898            fail();
899        } catch (NullPointerException expected) {
900        }
901        dc.close();
902    }
903
904    public void test_joinSourceSpecific_nullSourceAddress_IPv6() throws Exception {
905        if (!supportsMulticast) {
906            return;
907        }
908        DatagramChannel dc = createReceiverChannel();
909        try {
910            dc.join(GOOD_MULTICAST_IPv6, ipv6NetworkInterface, null);
911            fail();
912        } catch (NullPointerException expected) {
913        }
914        dc.close();
915    }
916
917    public void test_joinSourceSpecific_mixedAddressTypes() throws Exception {
918        if (!supportsMulticast) {
919            return;
920        }
921        DatagramChannel dc = createReceiverChannel();
922        try {
923            dc.join(GOOD_MULTICAST_IPv4, ipv4NetworkInterface, UNICAST_IPv6_1);
924            fail();
925        } catch (IllegalArgumentException expected) {
926        }
927        try {
928            dc.join(GOOD_MULTICAST_IPv6, ipv6NetworkInterface, UNICAST_IPv4_1);
929            fail();
930        } catch (IllegalArgumentException expected) {
931        }
932        dc.close();
933    }
934
935    public void test_joinSourceSpecific_nonUnicastSourceAddress_IPv4() throws Exception {
936        if (!supportsMulticast) {
937            return;
938        }
939        DatagramChannel dc = createReceiverChannel();
940        try {
941            dc.join(GOOD_MULTICAST_IPv4, ipv4NetworkInterface, BAD_MULTICAST_IPv4);
942            fail();
943        } catch (IllegalArgumentException expected) {
944        }
945        dc.close();
946    }
947
948    public void test_joinSourceSpecific_nonUniicastSourceAddress_IPv6() throws Exception {
949        if (!supportsMulticast) {
950            return;
951        }
952        DatagramChannel dc = createReceiverChannel();
953        try {
954            dc.join(GOOD_MULTICAST_IPv6, ipv6NetworkInterface, BAD_MULTICAST_IPv6);
955            fail();
956        } catch (IllegalArgumentException expected) {
957        }
958        dc.close();
959    }
960
961    public void test_joinSourceSpecific_multipleSourceAddressLimit() throws Exception {
962        if (!supportsMulticast) {
963            return;
964        }
965        DatagramChannel dc = createReceiverChannel();
966        for (byte i = 1; i <= 20; i++) {
967            InetAddress sourceAddress = Inet4Address.getByAddress(new byte[] { 10, 0, 0, i});
968            try {
969                dc.join(GOOD_MULTICAST_IPv4, ipv4NetworkInterface, sourceAddress);
970            } catch (SocketException e) {
971                // There is a limit, that's ok according to the RI docs. For this test a lower bound of 10
972                // is used, which appears to be the default linux limit. See /proc/sys/net/ipv4/igmp_max_msf
973                assertTrue(i > 10);
974                break;
975            }
976        }
977
978        dc.close();
979    }
980
981    /**
982     * Checks that a source-specific join() works when the receiver is bound to the multicast group
983     * address
984     */
985    public void test_joinSourceSpecific_null() throws Exception {
986        if (!supportsMulticast) {
987            return;
988        }
989        InetAddress ipv4LocalAddress = getLocalIpv4Address(ipv4NetworkInterface);
990        check_joinSourceSpecific(
991                ipv4LocalAddress /* senderBindAddress */,
992                GOOD_MULTICAST_IPv4 /* receiverBindAddress */,
993                GOOD_MULTICAST_IPv4 /* groupAddress */,
994                UNICAST_IPv4_1 /* badSenderAddress */,
995                ipv4NetworkInterface);
996    }
997
998    /**
999     * Checks that a source-specific join() works when the receiver is bound to the multicast group
1000     * address
1001     */
1002    public void test_joinSourceSpecific_groupBind_ipv4() throws Exception {
1003        if (!supportsMulticast) {
1004            return;
1005        }
1006        InetAddress ipv4LocalAddress = getLocalIpv4Address(ipv4NetworkInterface);
1007        check_joinSourceSpecific(
1008                ipv4LocalAddress /* senderBindAddress */,
1009                GOOD_MULTICAST_IPv4 /* receiverBindAddress */,
1010                GOOD_MULTICAST_IPv4 /* groupAddress */,
1011                UNICAST_IPv4_1 /* badSenderAddress */,
1012                ipv4NetworkInterface);
1013    }
1014
1015    /**
1016     * Checks that a source-specific join() works when the receiver is bound to the multicast group
1017     * address
1018     */
1019    public void test_joinSourceSpecific_groupBind_ipv6() throws Exception {
1020        if (!supportsMulticast) {
1021            return;
1022        }
1023        InetAddress ipv6LocalAddress = getLocalIpv6Address(ipv6NetworkInterface);
1024        check_joinSourceSpecific(
1025                ipv6LocalAddress /* senderBindAddress */,
1026                GOOD_MULTICAST_IPv6 /* receiverBindAddress */,
1027                GOOD_MULTICAST_IPv6 /* groupAddress */,
1028                UNICAST_IPv6_1 /* badSenderAddress */,
1029                ipv6NetworkInterface);
1030    }
1031
1032    /** Checks that a source-specific join() works when the receiver is bound to the "any" address */
1033    public void test_joinSourceSpecific_anyBind_ipv4() throws Exception {
1034        if (!supportsMulticast) {
1035            return;
1036        }
1037        InetAddress ipv4LocalAddress = getLocalIpv4Address(ipv4NetworkInterface);
1038        check_joinSourceSpecific(
1039                ipv4LocalAddress /* senderBindAddress */,
1040                WILDCARD_IPv4 /* receiverBindAddress */,
1041                GOOD_MULTICAST_IPv4 /* groupAddress */,
1042                UNICAST_IPv4_1 /* badSenderAddress */,
1043                ipv4NetworkInterface);
1044    }
1045
1046    /** Checks that a source-specific join() works when the receiver is bound to the "any" address */
1047    public void test_joinSourceSpecific_anyBind_ipv6() throws Exception {
1048        if (!supportsMulticast) {
1049            return;
1050        }
1051        InetAddress ipv6LocalAddress = getLocalIpv6Address(ipv6NetworkInterface);
1052        check_joinSourceSpecific(
1053                ipv6LocalAddress /* senderBindAddress */,
1054                WILDCARD_IPv6 /* receiverBindAddress */,
1055                GOOD_MULTICAST_IPv6 /* groupAddress */,
1056                UNICAST_IPv6_1 /* badSenderAddress */,
1057                ipv6NetworkInterface);
1058    }
1059
1060    /**
1061     * Checks that the source-specific membership is correctly source-filtering.
1062     *
1063     * @param senderBindAddress the address to bind the sender socket to
1064     * @param receiverBindAddress the address to bind the receiver socket to
1065     * @param groupAddress the group address to join
1066     * @param badSenderAddress a unicast address to join to perform a negative test
1067     * @param networkInterface The network interface on which to join the multicast group
1068     */
1069    private void check_joinSourceSpecific(
1070            InetAddress senderBindAddress, InetAddress receiverBindAddress, InetAddress groupAddress,
1071            InetAddress badSenderAddress, NetworkInterface networkInterface)
1072            throws Exception {
1073        DatagramChannel sendingChannel = DatagramChannel.open();
1074        // In order to be source-specific the sender's address must be known. The sendingChannel is
1075        // explicitly bound to a known, non-loopback address.
1076        sendingChannel.bind(new InetSocketAddress(senderBindAddress, 0));
1077        InetSocketAddress sendingAddress = (InetSocketAddress) sendingChannel.getLocalAddress();
1078
1079        DatagramChannel receivingChannel = DatagramChannel.open();
1080        receivingChannel.bind(
1081                new InetSocketAddress(receiverBindAddress, 0) /* local port left to the OS to determine */);
1082        configureChannelForReceiving(receivingChannel);
1083
1084        InetSocketAddress localReceivingAddress =
1085                (InetSocketAddress) receivingChannel.getLocalAddress();
1086        InetSocketAddress groupSocketAddress =
1087                new InetSocketAddress(groupAddress, localReceivingAddress.getPort());
1088        MembershipKey membershipKey1 = receivingChannel
1089                .join(groupSocketAddress.getAddress(), networkInterface, senderBindAddress);
1090
1091        ByteBuffer receiveBuffer = ByteBuffer.allocate(10);
1092
1093        // Send a message. It should be received.
1094        String msg1 = "Hello1";
1095        sendMessage(sendingChannel, msg1, groupSocketAddress);
1096        InetSocketAddress sourceAddress1 = (InetSocketAddress) receivingChannel.receive(receiveBuffer);
1097        assertEquals(sourceAddress1, sendingAddress);
1098        assertEquals(msg1, new String(receiveBuffer.array(), 0, receiveBuffer.position()));
1099
1100        membershipKey1.drop();
1101
1102        receivingChannel.join(groupSocketAddress.getAddress(), networkInterface, badSenderAddress);
1103
1104        // Send a message. It should not be received.
1105        String msg2 = "Hello2";
1106        sendMessage(sendingChannel, msg2, groupSocketAddress);
1107        InetSocketAddress sourceAddress2 = (InetSocketAddress) receivingChannel.receive(receiveBuffer);
1108        assertNull(sourceAddress2);
1109
1110        receivingChannel.close();
1111        sendingChannel.close();
1112    }
1113
1114    public void test_dropSourceSpecific_twice_IPv4() throws Exception {
1115        check_dropSourceSpecific_twice(
1116                GOOD_MULTICAST_IPv4 /* groupAddress */, UNICAST_IPv4_1 /* sourceAddress */,
1117                ipv4NetworkInterface);
1118    }
1119
1120    public void test_dropSourceSpecific_twice_IPv6() throws Exception {
1121        check_dropSourceSpecific_twice(
1122                GOOD_MULTICAST_IPv6 /* groupAddress */, UNICAST_IPv6_1 /* sourceAddress */,
1123                ipv6NetworkInterface);
1124    }
1125
1126    private void check_dropSourceSpecific_twice(InetAddress groupAddress, InetAddress sourceAddress,
1127            NetworkInterface networkInterface)
1128            throws Exception {
1129        if (!supportsMulticast) {
1130            return;
1131        }
1132        DatagramChannel dc = createReceiverChannel();
1133        MembershipKey membershipKey = dc.join(groupAddress, networkInterface, sourceAddress);
1134
1135        assertTrue(membershipKey.isValid());
1136        membershipKey.drop();
1137        assertFalse(membershipKey.isValid());
1138
1139        // Try to leave a group we are no longer a member of - should do nothing.
1140        membershipKey.drop();
1141
1142        dc.close();
1143    }
1144
1145    public void test_dropSourceSpecific_sourceKeysAreIndependent_IPv4() throws Exception {
1146        check_dropSourceSpecific_sourceKeysAreIndependent(
1147                GOOD_MULTICAST_IPv4 /* groupAddress */,
1148                UNICAST_IPv4_1 /* sourceAddress1 */,
1149                UNICAST_IPv4_2 /* sourceAddress2 */,
1150                ipv4NetworkInterface);
1151    }
1152
1153    public void test_dropSourceSpecific_sourceKeysAreIndependent_IPv6() throws Exception {
1154        check_dropSourceSpecific_sourceKeysAreIndependent(
1155                GOOD_MULTICAST_IPv6 /* groupAddress */,
1156                UNICAST_IPv6_1 /* sourceAddress1 */,
1157                UNICAST_IPv6_2 /* sourceAddress2 */,
1158                ipv6NetworkInterface);
1159    }
1160
1161    private void check_dropSourceSpecific_sourceKeysAreIndependent(
1162            InetAddress groupAddress, InetAddress sourceAddress1, InetAddress sourceAddress2,
1163            NetworkInterface networkInterface) throws Exception {
1164        if (!supportsMulticast) {
1165            return;
1166        }
1167        DatagramChannel dc = createReceiverChannel();
1168        MembershipKey membershipKey1 = dc.join(groupAddress, networkInterface, sourceAddress1);
1169        MembershipKey membershipKey2 = dc.join(groupAddress, networkInterface, sourceAddress2);
1170        assertFalse(membershipKey1.equals(membershipKey2));
1171        assertTrue(membershipKey1.isValid());
1172        assertTrue(membershipKey2.isValid());
1173
1174        membershipKey1.drop();
1175
1176        assertFalse(membershipKey1.isValid());
1177        assertTrue(membershipKey2.isValid());
1178
1179        dc.close();
1180    }
1181
1182    public void test_drop_keyBehaviorAfterDrop() throws Exception {
1183        if (!supportsMulticast) {
1184            return;
1185        }
1186        DatagramChannel dc = createReceiverChannel();
1187        MembershipKey membershipKey = dc.join(GOOD_MULTICAST_IPv4, ipv4NetworkInterface, UNICAST_IPv4_1);
1188        membershipKey.drop();
1189        assertFalse(membershipKey.isValid());
1190
1191        try {
1192            membershipKey.block(UNICAST_IPv4_1);
1193            fail();
1194        } catch (IllegalStateException expected) {
1195        }
1196
1197        try {
1198            membershipKey.unblock(UNICAST_IPv4_1);
1199            fail();
1200        } catch (IllegalStateException expected) {
1201        }
1202
1203        assertSame(dc, membershipKey.channel());
1204        assertSame(GOOD_MULTICAST_IPv4, membershipKey.group());
1205        assertSame(UNICAST_IPv4_1, membershipKey.sourceAddress());
1206        assertSame(ipv4NetworkInterface, membershipKey.networkInterface());
1207    }
1208
1209    private static void configureChannelForReceiving(DatagramChannel receivingChannel)
1210            throws Exception {
1211
1212        // NOTE: At the time of writing setSoTimeout() has no effect in the RI, making these tests hang
1213        // if the channel is in blocking mode. configureBlocking(false) is used instead and rely on the
1214        // network to the local host being instantaneous.
1215        // receivingChannel.socket().setSoTimeout(200);
1216        // receivingChannel.configureBlocking(true);
1217        receivingChannel.configureBlocking(false);
1218    }
1219
1220    private static boolean willWorkForMulticast(NetworkInterface iface) throws IOException {
1221        return iface.isUp()
1222                // Typically loopback interfaces do not support multicast, but they are ruled out
1223                // explicitly here anyway.
1224                && !iface.isLoopback() && iface.supportsMulticast()
1225                && iface.getInetAddresses().hasMoreElements();
1226    }
1227
1228    private static void sendMulticastMessage(
1229            InetAddress group, int port, String msg, NetworkInterface sendingInterface)
1230            throws IOException {
1231        // Any datagram socket can send to a group. It does not need to have joined the group.
1232        DatagramChannel dc = DatagramChannel.open();
1233        if (sendingInterface != null) {
1234            // For some reason, if set, this must be set to a real (non-loopback) device for an IPv6
1235            // group, but can be loopback for an IPv4 group.
1236            dc.setOption(StandardSocketOptions.IP_MULTICAST_IF, sendingInterface);
1237        }
1238        sendMessage(dc, msg, new InetSocketAddress(group, port));
1239        dc.close();
1240    }
1241
1242    private static void sendMessage(
1243            DatagramChannel sendingChannel, String msg, InetSocketAddress targetAddress)
1244            throws IOException {
1245
1246        ByteBuffer sendBuffer = ByteBuffer.wrap(msg.getBytes());
1247        sendingChannel.send(sendBuffer, targetAddress);
1248    }
1249
1250    private static InetAddress getLocalIpv4Address(NetworkInterface networkInterface) {
1251        for (InterfaceAddress interfaceAddress : networkInterface.getInterfaceAddresses()) {
1252            if (interfaceAddress.getAddress() instanceof Inet4Address) {
1253                return interfaceAddress.getAddress();
1254            }
1255        }
1256        throw new AssertionFailedError("Unable to find local IPv4 address for " + networkInterface);
1257    }
1258
1259    private static InetAddress getLocalIpv6Address(NetworkInterface networkInterface) {
1260        for (InterfaceAddress interfaceAddress : networkInterface.getInterfaceAddresses()) {
1261            if (interfaceAddress.getAddress() instanceof Inet6Address) {
1262                return interfaceAddress.getAddress();
1263            }
1264        }
1265        throw new AssertionFailedError("Unable to find local IPv6 address for " + networkInterface);
1266    }
1267}
1268
1269