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