xref: /libuv/test/test-udp-multicast-join.c (revision d05744e3)
1 /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining a copy
4  * of this software and associated documentation files (the "Software"), to
5  * deal in the Software without restriction, including without limitation the
6  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7  * sell copies of the Software, and to permit persons to whom the Software is
8  * furnished to do so, subject to the following conditions:
9  *
10  * The above copyright notice and this permission notice shall be included in
11  * all copies or substantial portions of the Software.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19  * IN THE SOFTWARE.
20  */
21 
22 #include "uv.h"
23 #include "task.h"
24 
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 
29 #define CHECK_HANDLE(handle) \
30   ASSERT_NE((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client, 0)
31 
32 #define MULTICAST_ADDR "239.255.0.1"
33 
34 static uv_udp_t server;
35 static uv_udp_t client;
36 static uv_udp_send_t req;
37 static uv_udp_send_t req_ss;
38 
39 static int darwin_ebusy_errors;
40 static int cl_recv_cb_called;
41 static int sv_send_cb_called;
42 static int close_cb_called;
43 
alloc_cb(uv_handle_t * handle,size_t suggested_size,uv_buf_t * buf)44 static void alloc_cb(uv_handle_t* handle,
45                      size_t suggested_size,
46                      uv_buf_t* buf) {
47   static char slab[65536];
48   CHECK_HANDLE(handle);
49   ASSERT_LE(suggested_size, sizeof(slab));
50   buf->base = slab;
51   buf->len = sizeof(slab);
52 }
53 
54 
close_cb(uv_handle_t * handle)55 static void close_cb(uv_handle_t* handle) {
56   CHECK_HANDLE(handle);
57   close_cb_called++;
58 }
59 
60 
sv_send_cb(uv_udp_send_t * req,int status)61 static void sv_send_cb(uv_udp_send_t* req, int status) {
62   ASSERT_NOT_NULL(req);
63   ASSERT_OK(status);
64   CHECK_HANDLE(req->handle);
65 
66   sv_send_cb_called++;
67 
68   if (sv_send_cb_called == 2)
69     uv_close((uv_handle_t*) req->handle, close_cb);
70 }
71 
72 
do_send(uv_udp_send_t * send_req)73 static int do_send(uv_udp_send_t* send_req) {
74   uv_buf_t buf;
75   struct sockaddr_in addr;
76 
77   buf = uv_buf_init("PING", 4);
78 
79   ASSERT_OK(uv_ip4_addr(MULTICAST_ADDR, TEST_PORT, &addr));
80 
81   /* client sends "PING" */
82   return uv_udp_send(send_req,
83                      &client,
84                      &buf,
85                      1,
86                      (const struct sockaddr*) &addr,
87                      sv_send_cb);
88 }
89 
90 
cl_recv_cb(uv_udp_t * handle,ssize_t nread,const uv_buf_t * buf,const struct sockaddr * addr,unsigned flags)91 static void cl_recv_cb(uv_udp_t* handle,
92                        ssize_t nread,
93                        const uv_buf_t* buf,
94                        const struct sockaddr* addr,
95                        unsigned flags) {
96   CHECK_HANDLE(handle);
97   ASSERT_OK(flags);
98 
99   if (nread < 0) {
100     ASSERT(0 && "unexpected error");
101   }
102 
103   if (nread == 0) {
104     /* Returning unused buffer. Don't count towards cl_recv_cb_called */
105     ASSERT_NULL(addr);
106     return;
107   }
108 
109   ASSERT_NOT_NULL(addr);
110   ASSERT_EQ(4, nread);
111   ASSERT(!memcmp("PING", buf->base, nread));
112 
113   cl_recv_cb_called++;
114 
115   if (cl_recv_cb_called == 2) {
116     /* we are done with the server handle, we can close it */
117     uv_close((uv_handle_t*) &server, close_cb);
118   } else {
119     int r;
120     char source_addr[64];
121 
122     r = uv_ip4_name((const struct sockaddr_in*)addr, source_addr, sizeof(source_addr));
123     ASSERT_OK(r);
124 
125     r = uv_udp_set_membership(&server, MULTICAST_ADDR, NULL, UV_LEAVE_GROUP);
126     ASSERT_OK(r);
127 
128 #if !defined(__NetBSD__)
129     r = uv_udp_set_source_membership(&server, MULTICAST_ADDR, NULL, source_addr, UV_JOIN_GROUP);
130 #if defined(__APPLE__)
131     if (r == UV_EBUSY) {
132       uv_close((uv_handle_t*) &server, close_cb);
133       darwin_ebusy_errors++;
134       return;
135     }
136 #endif
137     ASSERT_OK(r);
138 #endif
139 
140     r = do_send(&req_ss);
141     ASSERT_OK(r);
142   }
143 }
144 
145 
TEST_IMPL(udp_multicast_join)146 TEST_IMPL(udp_multicast_join) {
147 #if defined(__OpenBSD__)
148   RETURN_SKIP("Test does not currently work in OpenBSD");
149 #endif
150   int r;
151   struct sockaddr_in addr;
152 
153   ASSERT_OK(uv_ip4_addr("0.0.0.0", TEST_PORT, &addr));
154 
155   r = uv_udp_init(uv_default_loop(), &server);
156   ASSERT_OK(r);
157 
158   r = uv_udp_init(uv_default_loop(), &client);
159   ASSERT_OK(r);
160 
161   /* bind to the desired port */
162   r = uv_udp_bind(&server, (const struct sockaddr*) &addr, 0);
163   ASSERT_OK(r);
164 
165   /* join the multicast channel */
166   r = uv_udp_set_membership(&server, MULTICAST_ADDR, NULL, UV_JOIN_GROUP);
167   if (r == UV_ENODEV)
168     RETURN_SKIP("No multicast support.");
169   if (r == UV_ENOEXEC)
170     RETURN_SKIP("No multicast support (likely a firewall issue).");
171   ASSERT_OK(r);
172 #if defined(__ANDROID__)
173   /* It returns an ENOSYS error */
174   RETURN_SKIP("Test does not currently work in ANDROID");
175 #endif
176 
177   r = uv_udp_recv_start(&server, alloc_cb, cl_recv_cb);
178   ASSERT_OK(r);
179 
180   r = do_send(&req);
181   ASSERT_OK(r);
182 
183   ASSERT_OK(close_cb_called);
184   ASSERT_OK(cl_recv_cb_called);
185   ASSERT_OK(sv_send_cb_called);
186 
187   /* run the loop till all events are processed */
188   uv_run(uv_default_loop(), UV_RUN_DEFAULT);
189 
190   if (darwin_ebusy_errors > 0)
191     RETURN_SKIP("Unexplained macOS IP_ADD_SOURCE_MEMBERSHIP EBUSY bug");
192 
193   ASSERT_EQ(2, cl_recv_cb_called);
194   ASSERT_EQ(2, sv_send_cb_called);
195   ASSERT_EQ(2, close_cb_called);
196 
197   MAKE_VALGRIND_HAPPY(uv_default_loop());
198   return 0;
199 }
200