1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.testsuite.transport.socket;
17
18 import io.netty.bootstrap.Bootstrap;
19 import io.netty.buffer.Unpooled;
20 import io.netty.channel.ChannelHandlerContext;
21 import io.netty.channel.ChannelOption;
22 import io.netty.channel.SimpleChannelInboundHandler;
23 import io.netty.channel.socket.DatagramChannel;
24 import io.netty.channel.socket.DatagramPacket;
25 import io.netty.channel.socket.SocketProtocolFamily;
26 import io.netty.channel.socket.oio.OioDatagramChannel;
27 import io.netty.testsuite.transport.TestsuitePermutation;
28 import io.netty.util.NetUtil;
29 import io.netty.util.internal.SocketUtils;
30 import org.junit.jupiter.api.Test;
31 import org.junit.jupiter.api.TestInfo;
32
33 import java.io.IOException;
34 import java.net.InetAddress;
35 import java.net.InetSocketAddress;
36 import java.net.MulticastSocket;
37 import java.net.NetworkInterface;
38 import java.net.UnknownHostException;
39 import java.util.Enumeration;
40 import java.util.List;
41 import java.util.concurrent.CountDownLatch;
42 import java.util.concurrent.TimeUnit;
43
44 import static org.junit.jupiter.api.Assertions.assertEquals;
45 import static org.junit.jupiter.api.Assertions.assertFalse;
46 import static org.junit.jupiter.api.Assertions.assertTrue;
47 import static org.junit.jupiter.api.Assertions.fail;
48 import static org.junit.jupiter.api.Assumptions.assumeTrue;
49
50 public class DatagramMulticastTest extends AbstractDatagramTest {
51
52 @Test
53 public void testMulticast(TestInfo testInfo) throws Throwable {
54 run(testInfo, new Runner<Bootstrap, Bootstrap>() {
55 @Override
56 public void run(Bootstrap bootstrap, Bootstrap bootstrap2) throws Throwable {
57 testMulticast(bootstrap, bootstrap2);
58 }
59 });
60 }
61
62 public void testMulticast(Bootstrap sb, Bootstrap cb) throws Throwable {
63 NetworkInterface iface = multicastNetworkInterface();
64 assumeTrue(iface != null, "No NetworkInterface found that supports multicast and " +
65 socketInternetProtocalFamily());
66
67 MulticastTestHandler mhandler = new MulticastTestHandler();
68
69 sb.handler(new SimpleChannelInboundHandler<Object>() {
70 @Override
71 public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
72
73 }
74 });
75
76 cb.handler(mhandler);
77
78 sb.option(ChannelOption.IP_MULTICAST_IF, iface);
79 sb.option(ChannelOption.SO_REUSEADDR, true);
80
81 cb.option(ChannelOption.IP_MULTICAST_IF, iface);
82 cb.option(ChannelOption.SO_REUSEADDR, true);
83
84 DatagramChannel sc = (DatagramChannel) sb.bind(newSocketAddress(iface)).sync().channel();
85 assertEquals(iface, sc.config().getNetworkInterface());
86 assertInterfaceAddress(iface, sc.config().getInterface());
87
88 InetSocketAddress addr = sc.localAddress();
89 cb.localAddress(addr.getPort());
90
91 if (sc instanceof OioDatagramChannel) {
92
93
94
95 sc.close().awaitUninterruptibly();
96 return;
97 }
98 DatagramChannel cc = (DatagramChannel) cb.bind().sync().channel();
99 assertEquals(iface, cc.config().getNetworkInterface());
100 assertInterfaceAddress(iface, cc.config().getInterface());
101
102 InetSocketAddress groupAddress = SocketUtils.socketAddress(groupAddress(), addr.getPort());
103
104 cc.joinGroup(groupAddress, iface).sync();
105
106 sc.writeAndFlush(new DatagramPacket(Unpooled.copyInt(1), groupAddress)).sync();
107 assertTrue(mhandler.await());
108
109
110 cc.leaveGroup(groupAddress, iface).sync();
111
112
113 Thread.sleep(1000);
114
115
116 sc.writeAndFlush(new DatagramPacket(Unpooled.copyInt(1), groupAddress)).sync();
117 mhandler.await();
118
119 cc.config().setLoopbackModeDisabled(false);
120 sc.config().setLoopbackModeDisabled(false);
121
122 assertFalse(cc.config().isLoopbackModeDisabled());
123 assertFalse(sc.config().isLoopbackModeDisabled());
124
125 cc.config().setLoopbackModeDisabled(true);
126 sc.config().setLoopbackModeDisabled(true);
127
128 assertTrue(cc.config().isLoopbackModeDisabled());
129 assertTrue(sc.config().isLoopbackModeDisabled());
130
131 sc.close().awaitUninterruptibly();
132 cc.close().awaitUninterruptibly();
133 }
134
135 private static void assertInterfaceAddress(NetworkInterface networkInterface, InetAddress expected) {
136 Enumeration<InetAddress> addresses = networkInterface.getInetAddresses();
137 while (addresses.hasMoreElements()) {
138 if (expected.equals(addresses.nextElement())) {
139 return;
140 }
141 }
142 fail();
143 }
144
145 private static final class MulticastTestHandler extends SimpleChannelInboundHandler<DatagramPacket> {
146 private final CountDownLatch latch = new CountDownLatch(1);
147
148 private boolean done;
149 private volatile boolean fail;
150
151 @Override
152 protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket msg) throws Exception {
153 if (done) {
154 fail = true;
155 }
156
157 assertEquals(1, msg.content().readInt());
158
159 latch.countDown();
160
161
162 done = true;
163 }
164
165 public boolean await() throws Exception {
166 boolean success = latch.await(10, TimeUnit.SECONDS);
167 if (fail) {
168
169 fail();
170 }
171 return success;
172 }
173 }
174
175 @Override
176 protected List<TestsuitePermutation.BootstrapComboFactory<Bootstrap, Bootstrap>> newFactories() {
177 return SocketTestPermutation.INSTANCE.datagram(socketInternetProtocalFamily());
178 }
179
180 private InetSocketAddress newAnySocketAddress() throws UnknownHostException {
181 switch (socketInternetProtocalFamily()) {
182 case INET:
183 return new InetSocketAddress(InetAddress.getByName("0.0.0.0"), 0);
184 case INET6:
185 return new InetSocketAddress(InetAddress.getByName("::"), 0);
186 default:
187 throw new AssertionError();
188 }
189 }
190
191 private InetSocketAddress newSocketAddress(NetworkInterface iface) {
192 Enumeration<InetAddress> addresses = iface.getInetAddresses();
193 while (addresses.hasMoreElements()) {
194 InetAddress address = addresses.nextElement();
195 if (isSupported(socketInternetProtocalFamily(), address)) {
196 return new InetSocketAddress(address, 0);
197 }
198 }
199 throw new AssertionError();
200 }
201
202 private NetworkInterface multicastNetworkInterface() throws IOException {
203 for (NetworkInterface iface : NetUtil.NETWORK_INTERFACES) {
204 if (iface.isUp() && iface.supportsMulticast()) {
205 Enumeration<InetAddress> addresses = iface.getInetAddresses();
206 while (addresses.hasMoreElements()) {
207 InetAddress address = addresses.nextElement();
208 if (isSupported(socketInternetProtocalFamily(), address)) {
209 MulticastSocket socket = new MulticastSocket(newAnySocketAddress());
210 socket.setReuseAddress(true);
211 socket.setNetworkInterface(iface);
212 try {
213 socket.send(new java.net.DatagramPacket(new byte[] { 1, 2, 3, 4 }, 4,
214 new InetSocketAddress(groupAddress(), 12345)));
215 return iface;
216 } catch (IOException ignore) {
217
218 } finally {
219 socket.close();
220 }
221 }
222 }
223 }
224 }
225 return null;
226 }
227
228 private String groupAddress() {
229 return groupInternetProtocalFamily() == SocketProtocolFamily.INET?
230 "230.0.0.1" : "FF01:0:0:0:0:0:0:101";
231 }
232 }