1 //
2 // multicast.cpp
3 // ~~~~~~~~~~~~~
4 //
5 // Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com)
6 //
7 // Distributed under the Boost Software License, Version 1.0. (See accompanying
8 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
9 //
10 
11 // Disable autolinking for unit tests.
12 #if !defined(BOOST_ALL_NO_LIB)
13 #define BOOST_ALL_NO_LIB 1
14 #endif // !defined(BOOST_ALL_NO_LIB)
15 
16 // Test that header file is self-contained.
17 #include "asio/ip/multicast.hpp"
18 
19 #include "asio/io_context.hpp"
20 #include "asio/ip/udp.hpp"
21 #include "../unit_test.hpp"
22 
23 //------------------------------------------------------------------------------
24 
25 // ip_multicast_compile test
26 // ~~~~~~~~~~~~~~~~~~~~~~~~~
27 // The following test checks that all nested classes, enums and constants in
28 // ip::multicast compile and link correctly. Runtime failures are ignored.
29 
30 namespace ip_multicast_compile {
31 
test()32 void test()
33 {
34   using namespace asio;
35   namespace ip = asio::ip;
36 
37   try
38   {
39     io_context ioc;
40     ip::udp::socket sock(ioc);
41     const ip::address address;
42     const ip::address_v4 address_v4;
43     const ip::address_v6 address_v6;
44 
45     // join_group class.
46 
47     ip::multicast::join_group join_group1;
48     ip::multicast::join_group join_group2(address);
49     ip::multicast::join_group join_group3(address_v4);
50     ip::multicast::join_group join_group4(address_v4, address_v4);
51     ip::multicast::join_group join_group5(address_v6);
52     ip::multicast::join_group join_group6(address_v6, 1);
53     sock.set_option(join_group6);
54 
55     // leave_group class.
56 
57     ip::multicast::leave_group leave_group1;
58     ip::multicast::leave_group leave_group2(address);
59     ip::multicast::leave_group leave_group3(address_v4);
60     ip::multicast::leave_group leave_group4(address_v4, address_v4);
61     ip::multicast::leave_group leave_group5(address_v6);
62     ip::multicast::leave_group leave_group6(address_v6, 1);
63     sock.set_option(leave_group6);
64 
65     // outbound_interface class.
66 
67     ip::multicast::outbound_interface outbound_interface1;
68     ip::multicast::outbound_interface outbound_interface2(address_v4);
69     ip::multicast::outbound_interface outbound_interface3(1);
70     sock.set_option(outbound_interface3);
71 
72     // hops class.
73 
74     ip::multicast::hops hops1(1024);
75     sock.set_option(hops1);
76     ip::multicast::hops hops2;
77     sock.get_option(hops2);
78     hops1 = 1;
79     (void)static_cast<int>(hops1.value());
80 
81     // enable_loopback class.
82 
83     ip::multicast::enable_loopback enable_loopback1(true);
84     sock.set_option(enable_loopback1);
85     ip::multicast::enable_loopback enable_loopback2;
86     sock.get_option(enable_loopback2);
87     enable_loopback1 = true;
88     (void)static_cast<bool>(enable_loopback1);
89     (void)static_cast<bool>(!enable_loopback1);
90     (void)static_cast<bool>(enable_loopback1.value());
91   }
92   catch (std::exception&)
93   {
94   }
95 }
96 
97 } // namespace ip_multicast_compile
98 
99 //------------------------------------------------------------------------------
100 
101 // ip_multicast_runtime test
102 // ~~~~~~~~~~~~~~~~~~~~~~~~~
103 // The following test checks the runtime operation of the socket options defined
104 // in the ip::multicast namespace.
105 
106 namespace ip_multicast_runtime {
107 
108 #if defined(__hpux)
109 // HP-UX doesn't declare this function extern "C", so it is declared again here
110 // to avoid a linker error about an undefined symbol.
111 extern "C" unsigned int if_nametoindex(const char*);
112 #endif // defined(__hpux)
113 
test()114 void test()
115 {
116   using namespace asio;
117   namespace ip = asio::ip;
118 
119   io_context ioc;
120   asio::error_code ec;
121 
122   ip::udp::endpoint ep_v4(ip::address_v4::loopback(), 0);
123   ip::udp::socket sock_v4(ioc);
124   sock_v4.open(ep_v4.protocol(), ec);
125   sock_v4.bind(ep_v4, ec);
126   bool have_v4 = !ec;
127 
128   ip::udp::endpoint ep_v6(ip::address_v6::loopback(), 0);
129   ip::udp::socket sock_v6(ioc);
130   sock_v6.open(ep_v6.protocol(), ec);
131   sock_v6.bind(ep_v6, ec);
132   bool have_v6 = !ec;
133 
134   ASIO_CHECK(have_v4 || have_v6);
135 
136 #if defined(ASIO_WINDOWS) && defined(UNDER_CE)
137   // Windows CE seems to have problems with some multicast group addresses.
138   // The following address works on CE, but as it is not a private multicast
139   // address it will not be used on other platforms.
140   const ip::address multicast_address_v4 = ip::make_address("239.0.0.4", ec);
141 #else // defined(ASIO_WINDOWS) && defined(UNDER_CE)
142   const ip::address multicast_address_v4 = ip::make_address("239.255.0.1", ec);
143 #endif // defined(ASIO_WINDOWS) && defined(UNDER_CE)
144   ASIO_CHECK(!have_v4 || !ec);
145 
146 #if (defined(__MACH__) && defined(__APPLE__)) \
147   || defined(__FreeBSD__) \
148   || defined(__NetBSD__) \
149   || defined(__OpenBSD__)
150   const ip::address multicast_address_v6 = ip::make_address("ff02::1%lo0", ec);
151 #else // (defined(__MACH__) && defined(__APPLE__))
152       //   || defined(__FreeBSD__)
153       //   || defined(__NetBSD__)
154       //   || defined(__OpenBSD__)
155   const ip::address multicast_address_v6 = ip::make_address("ff01::1", ec);
156 #endif // (defined(__MACH__) && defined(__APPLE__))
157        //   || defined(__FreeBSD__)
158        //   || defined(__NetBSD__)
159        //   || defined(__OpenBSD__)
160   ASIO_CHECK(!have_v6 || !ec);
161 
162   // join_group class.
163 
164   if (have_v4)
165   {
166     ip::multicast::join_group join_group(multicast_address_v4);
167     sock_v4.set_option(join_group, ec);
168     ASIO_CHECK_MESSAGE(!ec || ec == error::no_such_device,
169                        ec.value() << ", " << ec.message());
170 
171     if (!ec)
172     {
173       // leave_group class.
174 
175       ip::multicast::leave_group leave_group(multicast_address_v4);
176       sock_v4.set_option(leave_group, ec);
177       ASIO_CHECK_MESSAGE(!ec, ec.value() << ", " << ec.message());
178     }
179   }
180 
181   if (have_v6)
182   {
183     ip::multicast::join_group join_group(multicast_address_v6);
184     sock_v6.set_option(join_group, ec);
185     ASIO_CHECK_MESSAGE(!ec || ec == error::no_such_device,
186                        ec.value() << ", " << ec.message());
187 
188     if (!ec)
189     {
190       // leave_group class.
191 
192       ip::multicast::leave_group leave_group(multicast_address_v6);
193       sock_v6.set_option(leave_group, ec);
194       ASIO_CHECK_MESSAGE(!ec, ec.value() << ", " << ec.message());
195     }
196   }
197 
198   // outbound_interface class.
199 
200   if (have_v4)
201   {
202     ip::multicast::outbound_interface outbound_interface(
203         ip::address_v4::loopback());
204     sock_v4.set_option(outbound_interface, ec);
205     ASIO_CHECK_MESSAGE(!ec, ec.value() << ", " << ec.message());
206   }
207 
208   if (have_v6)
209   {
210 #if defined(__hpux)
211     ip::multicast::outbound_interface outbound_interface(if_nametoindex("lo0"));
212 #else
213     ip::multicast::outbound_interface outbound_interface(1);
214 #endif
215     sock_v6.set_option(outbound_interface, ec);
216     ASIO_CHECK_MESSAGE(!ec, ec.value() << ", " << ec.message());
217   }
218 
219   // hops class.
220 
221   if (have_v4)
222   {
223     ip::multicast::hops hops1(1);
224     ASIO_CHECK(hops1.value() == 1);
225     sock_v4.set_option(hops1, ec);
226     ASIO_CHECK_MESSAGE(!ec, ec.value() << ", " << ec.message());
227 
228     ip::multicast::hops hops2;
229     sock_v4.get_option(hops2, ec);
230     ASIO_CHECK_MESSAGE(!ec, ec.value() << ", " << ec.message());
231     ASIO_CHECK(hops2.value() == 1);
232 
233     ip::multicast::hops hops3(0);
234     ASIO_CHECK(hops3.value() == 0);
235     sock_v4.set_option(hops3, ec);
236     ASIO_CHECK_MESSAGE(!ec, ec.value() << ", " << ec.message());
237 
238     ip::multicast::hops hops4;
239     sock_v4.get_option(hops4, ec);
240     ASIO_CHECK_MESSAGE(!ec, ec.value() << ", " << ec.message());
241     ASIO_CHECK(hops4.value() == 0);
242   }
243 
244   if (have_v6)
245   {
246     ip::multicast::hops hops1(1);
247     ASIO_CHECK(hops1.value() == 1);
248     sock_v6.set_option(hops1, ec);
249     ASIO_CHECK_MESSAGE(!ec, ec.value() << ", " << ec.message());
250 
251     ip::multicast::hops hops2;
252     sock_v6.get_option(hops2, ec);
253     ASIO_CHECK_MESSAGE(!ec, ec.value() << ", " << ec.message());
254     ASIO_CHECK(hops2.value() == 1);
255 
256     ip::multicast::hops hops3(0);
257     ASIO_CHECK(hops3.value() == 0);
258     sock_v6.set_option(hops3, ec);
259     ASIO_CHECK_MESSAGE(!ec, ec.value() << ", " << ec.message());
260 
261     ip::multicast::hops hops4;
262     sock_v6.get_option(hops4, ec);
263     ASIO_CHECK_MESSAGE(!ec, ec.value() << ", " << ec.message());
264     ASIO_CHECK(hops4.value() == 0);
265   }
266 
267   // enable_loopback class.
268 
269   if (have_v4)
270   {
271     ip::multicast::enable_loopback enable_loopback1(true);
272     ASIO_CHECK(enable_loopback1.value());
273     ASIO_CHECK(static_cast<bool>(enable_loopback1));
274     ASIO_CHECK(!!enable_loopback1);
275     sock_v4.set_option(enable_loopback1, ec);
276 #if defined(ASIO_WINDOWS) && defined(UNDER_CE)
277     // Option is not supported under Windows CE.
278     ASIO_CHECK_MESSAGE(ec == asio::error::no_protocol_option,
279         ec.value() << ", " << ec.message());
280 #else // defined(ASIO_WINDOWS) && defined(UNDER_CE)
281     ASIO_CHECK_MESSAGE(!ec, ec.value() << ", " << ec.message());
282 #endif // defined(ASIO_WINDOWS) && defined(UNDER_CE)
283 
284     ip::multicast::enable_loopback enable_loopback2;
285     sock_v4.get_option(enable_loopback2, ec);
286 #if defined(ASIO_WINDOWS) && defined(UNDER_CE)
287     // Not supported under Windows CE but can get value.
288     ASIO_CHECK_MESSAGE(!ec, ec.value() << ", " << ec.message());
289 #else // defined(ASIO_WINDOWS) && defined(UNDER_CE)
290     ASIO_CHECK_MESSAGE(!ec, ec.value() << ", " << ec.message());
291     ASIO_CHECK(enable_loopback2.value());
292     ASIO_CHECK(static_cast<bool>(enable_loopback2));
293     ASIO_CHECK(!!enable_loopback2);
294 #endif // defined(ASIO_WINDOWS) && defined(UNDER_CE)
295 
296     ip::multicast::enable_loopback enable_loopback3(false);
297     ASIO_CHECK(!enable_loopback3.value());
298     ASIO_CHECK(!static_cast<bool>(enable_loopback3));
299     ASIO_CHECK(!enable_loopback3);
300     sock_v4.set_option(enable_loopback3, ec);
301 #if defined(ASIO_WINDOWS) && defined(UNDER_CE)
302     // Option is not supported under Windows CE.
303     ASIO_CHECK_MESSAGE(ec == asio::error::no_protocol_option,
304         ec.value() << ", " << ec.message());
305 #else // defined(ASIO_WINDOWS) && defined(UNDER_CE)
306     ASIO_CHECK_MESSAGE(!ec, ec.value() << ", " << ec.message());
307 #endif // defined(ASIO_WINDOWS) && defined(UNDER_CE)
308 
309     ip::multicast::enable_loopback enable_loopback4;
310     sock_v4.get_option(enable_loopback4, ec);
311 #if defined(ASIO_WINDOWS) && defined(UNDER_CE)
312     // Not supported under Windows CE but can get value.
313     ASIO_CHECK_MESSAGE(!ec, ec.value() << ", " << ec.message());
314 #else // defined(ASIO_WINDOWS) && defined(UNDER_CE)
315     ASIO_CHECK_MESSAGE(!ec, ec.value() << ", " << ec.message());
316     ASIO_CHECK(!enable_loopback4.value());
317     ASIO_CHECK(!static_cast<bool>(enable_loopback4));
318     ASIO_CHECK(!enable_loopback4);
319 #endif // defined(ASIO_WINDOWS) && defined(UNDER_CE)
320   }
321 
322   if (have_v6)
323   {
324     ip::multicast::enable_loopback enable_loopback1(true);
325     ASIO_CHECK(enable_loopback1.value());
326     ASIO_CHECK(static_cast<bool>(enable_loopback1));
327     ASIO_CHECK(!!enable_loopback1);
328     sock_v6.set_option(enable_loopback1, ec);
329     ASIO_CHECK_MESSAGE(!ec, ec.value() << ", " << ec.message());
330 
331     ip::multicast::enable_loopback enable_loopback2;
332     sock_v6.get_option(enable_loopback2, ec);
333     ASIO_CHECK_MESSAGE(!ec, ec.value() << ", " << ec.message());
334     ASIO_CHECK(enable_loopback2.value());
335     ASIO_CHECK(static_cast<bool>(enable_loopback2));
336     ASIO_CHECK(!!enable_loopback2);
337 
338     ip::multicast::enable_loopback enable_loopback3(false);
339     ASIO_CHECK(!enable_loopback3.value());
340     ASIO_CHECK(!static_cast<bool>(enable_loopback3));
341     ASIO_CHECK(!enable_loopback3);
342     sock_v6.set_option(enable_loopback3, ec);
343     ASIO_CHECK_MESSAGE(!ec, ec.value() << ", " << ec.message());
344 
345     ip::multicast::enable_loopback enable_loopback4;
346     sock_v6.get_option(enable_loopback4, ec);
347     ASIO_CHECK_MESSAGE(!ec, ec.value() << ", " << ec.message());
348     ASIO_CHECK(!enable_loopback4.value());
349     ASIO_CHECK(!static_cast<bool>(enable_loopback4));
350     ASIO_CHECK(!enable_loopback4);
351   }
352 }
353 
354 } // namespace ip_multicast_runtime
355 
356 //------------------------------------------------------------------------------
357 
358 ASIO_TEST_SUITE
359 (
360   "ip/multicast",
361   ASIO_TEST_CASE(ip_multicast_compile::test)
362   ASIO_TEST_CASE(ip_multicast_runtime::test)
363 )
364