1 /* 2 * Copyright (c) 2007, 2012, 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 4527345 7026376 6633549 26 * @summary Unit test for DatagramChannel's multicast support 27 * @library /test/lib 28 * @build jdk.test.lib.NetworkConfiguration 29 * jdk.test.lib.Utils 30 * jdk.test.lib.Asserts 31 * jdk.test.lib.JDKToolFinder 32 * jdk.test.lib.JDKToolLauncher 33 * jdk.test.lib.Platform 34 * jdk.test.lib.process.* 35 * MulticastSendReceiveTests 36 * @run main MulticastSendReceiveTests 37 * @run main/othervm -Djava.net.preferIPv4Stack=true MulticastSendReceiveTests 38 * @key randomness 39 */ 40 41 import java.nio.ByteBuffer; 42 import java.nio.channels.*; 43 import java.net.*; 44 import static java.net.StandardProtocolFamily.*; 45 import java.util.*; 46 import java.io.IOException; 47 import java.util.stream.Collectors; 48 49 import jdk.test.lib.NetworkConfiguration; 50 51 public class MulticastSendReceiveTests { 52 53 static final Random rand = new Random(); 54 55 static final ProtocolFamily UNSPEC = new ProtocolFamily() { 56 public String name() { 57 return "UNSPEC"; 58 } 59 }; 60 61 /** 62 * Send datagram from given local address to given multicast 63 * group. 64 */ 65 static int sendDatagram(InetAddress local, 66 NetworkInterface nif, 67 InetAddress group, 68 int port) 69 throws IOException 70 { 71 ProtocolFamily family = (group instanceof Inet6Address) ? 72 StandardProtocolFamily.INET6 : StandardProtocolFamily.INET; 73 DatagramChannel dc = DatagramChannel.open(family) 74 .bind(new InetSocketAddress(local, 0)) 75 .setOption(StandardSocketOptions.IP_MULTICAST_IF, nif); 76 int id = rand.nextInt(); 77 byte[] msg = Integer.toString(id).getBytes("UTF-8"); 78 ByteBuffer buf = ByteBuffer.wrap(msg); 79 System.out.format("Send message from %s -> group %s (id=0x%x)\n", 80 local.getHostAddress(), group.getHostAddress(), id); 81 dc.send(buf, new InetSocketAddress(group, port)); 82 dc.close(); 83 return id; 84 } 85 86 /** 87 * Wait (with timeout) for datagram. 88 * 89 * @param expectedSender - expected sender address, or 90 * null if no datagram expected 91 * @param id - expected id of datagram 92 */ 93 static void receiveDatagram(DatagramChannel dc, 94 InetAddress expectedSender, 95 int id) 96 throws IOException 97 { 98 Selector sel = Selector.open(); 99 dc.configureBlocking(false); 100 dc.register(sel, SelectionKey.OP_READ); 101 ByteBuffer buf = ByteBuffer.allocateDirect(100); 102 103 try { 104 for (;;) { 105 System.out.println("Waiting to receive message"); 106 sel.select(5*1000); 107 SocketAddress sa = dc.receive(buf); 108 109 // no datagram received 110 if (sa == null) { 111 if (expectedSender != null) { 112 throw new RuntimeException("Expected message not received"); 113 } 114 System.out.println("No message received (correct)"); 115 return; 116 } 117 118 // datagram received 119 120 InetAddress sender = ((InetSocketAddress)sa).getAddress(); 121 buf.flip(); 122 byte[] bytes = new byte[buf.remaining()]; 123 buf.get(bytes); 124 String s = new String(bytes, "UTF-8"); 125 int receivedId = -1; 126 try { 127 receivedId = Integer.parseInt(s); 128 System.out.format("Received message from %s (id=0x%x)\n", 129 sender, receivedId); 130 } catch (NumberFormatException x) { 131 System.out.format("Received message from %s (msg=%s)\n", sender, s); 132 } 133 134 if (expectedSender == null) { 135 if (receivedId == id) 136 throw new RuntimeException("Message not expected"); 137 System.out.println("Message ignored (has wrong id)"); 138 } else { 139 if (sender.equals(expectedSender)) { 140 System.out.println("Message expected"); 141 return; 142 } 143 System.out.println("Message ignored (wrong sender)"); 144 } 145 146 sel.selectedKeys().clear(); 147 buf.rewind(); 148 } 149 } finally { 150 sel.close(); 151 } 152 } 153 154 155 /** 156 * Exercise multicast send/receive on given group/interface 157 */ 158 static void test(ProtocolFamily family, 159 NetworkInterface nif, 160 InetAddress group, 161 InetAddress source) 162 throws IOException 163 { 164 System.out.format("\nTest DatagramChannel to %s socket\n", family.name()); 165 try (DatagramChannel dc = (family == UNSPEC) ? 166 DatagramChannel.open() : DatagramChannel.open(family)) { 167 dc.setOption(StandardSocketOptions.SO_REUSEADDR, true) 168 .bind(new InetSocketAddress(0)); 169 170 // join group 171 System.out.format("join %s @ %s\n", group.getHostAddress(), 172 nif.getName()); 173 MembershipKey key; 174 try { 175 key = dc.join(group, nif); 176 } catch (IllegalArgumentException iae) { 177 if (family == UNSPEC) { 178 System.out.println("Not supported"); 179 return; 180 } 181 throw iae; 182 } 183 184 // send message to group 185 int port = ((InetSocketAddress)dc.getLocalAddress()).getPort(); 186 int id = sendDatagram(source, nif, group, port); 187 188 // receive message and check id matches 189 receiveDatagram(dc, source, id); 190 191 // exclude-mode filtering 192 193 try { 194 System.out.format("block %s\n", source.getHostAddress()); 195 196 // may throw UOE 197 key.block(source); 198 id = sendDatagram(source, nif, group, port); 199 receiveDatagram(dc, null, id); 200 201 // unblock source, send message, message should be received 202 System.out.format("unblock %s\n", source.getHostAddress()); 203 key.unblock(source); 204 id = sendDatagram(source, nif, group, port); 205 receiveDatagram(dc, source, id); 206 } catch (UnsupportedOperationException x) { 207 String os = System.getProperty("os.name"); 208 // Exclude-mode filtering supported on these platforms so UOE should never be thrown 209 if (os.equals("SunOS") || os.equals("Linux")) 210 throw x; 211 System.out.println("Exclude-mode filtering not supported!"); 212 } 213 214 key.drop(); 215 216 // include-mode filtering 217 218 InetAddress bogus = (group instanceof Inet6Address) ? 219 InetAddress.getByName("fe80::1234") : 220 InetAddress.getByName("1.2.3.4"); 221 System.out.format("join %s @ %s only-source %s\n", group.getHostAddress(), 222 nif.getName(), bogus.getHostAddress()); 223 try { 224 // may throw UOE 225 key = dc.join(group, nif, bogus); 226 227 id = sendDatagram(source, nif, group, port); 228 receiveDatagram(dc, null, id); 229 230 System.out.format("join %s @ %s only-source %s\n", group.getHostAddress(), 231 nif.getName(), source.getHostAddress()); 232 key = dc.join(group, nif, source); 233 234 id = sendDatagram(source, nif, group, port); 235 receiveDatagram(dc, source, id); 236 } catch (UnsupportedOperationException x) { 237 String os = System.getProperty("os.name"); 238 // Include-mode filtering supported on these platforms so UOE should never be thrown 239 if (os.equals("SunOS") || os.equals("Linux")) 240 throw x; 241 System.out.println("Include-mode filtering not supported!"); 242 } 243 } 244 } 245 246 public static void main(String[] args) throws IOException { 247 NetworkConfiguration config = NetworkConfiguration.probe(); 248 249 // multicast groups used for the test 250 InetAddress ip4Group = InetAddress.getByName("225.4.5.6"); 251 InetAddress ip6Group = InetAddress.getByName("ff02::a"); 252 for (NetworkInterface nif: config.ip4MulticastInterfaces() 253 .collect(Collectors.toList())) { 254 InetAddress source = config.ip4Addresses(nif).iterator().next(); 255 test(INET, nif, ip4Group, source); 256 test(UNSPEC, nif, ip4Group, source); 257 } 258 259 for (NetworkInterface nif: config.ip6MulticastInterfaces() 260 .collect(Collectors.toList())) { 261 InetAddress source = config.ip6Addresses(nif).iterator().next(); 262 test(INET6, nif, ip6Group, source); 263 test(UNSPEC, nif, ip6Group, source); 264 } 265 } 266 }