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((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client) 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 cl_recv_cb_called; 40 41 static int sv_send_cb_called; 42 43 static int close_cb_called; 44 45 static void alloc_cb(uv_handle_t* handle, 46 size_t suggested_size, 47 uv_buf_t* buf) { 48 static char slab[65536]; 49 CHECK_HANDLE(handle); 50 ASSERT(suggested_size <= sizeof(slab)); 51 buf->base = slab; 52 buf->len = sizeof(slab); 53 } 54 55 56 static void close_cb(uv_handle_t* handle) { 57 CHECK_HANDLE(handle); 58 close_cb_called++; 59 } 60 61 62 static void sv_send_cb(uv_udp_send_t* req, int status) { 63 ASSERT_NOT_NULL(req); 64 ASSERT(status == 0); 65 CHECK_HANDLE(req->handle); 66 67 sv_send_cb_called++; 68 69 if (sv_send_cb_called == 2) 70 uv_close((uv_handle_t*) req->handle, close_cb); 71 } 72 73 74 static int do_send(uv_udp_send_t* send_req) { 75 uv_buf_t buf; 76 struct sockaddr_in addr; 77 78 buf = uv_buf_init("PING", 4); 79 80 ASSERT(0 == uv_ip4_addr(MULTICAST_ADDR, TEST_PORT, &addr)); 81 82 /* client sends "PING" */ 83 return uv_udp_send(send_req, 84 &client, 85 &buf, 86 1, 87 (const struct sockaddr*) &addr, 88 sv_send_cb); 89 } 90 91 92 static void cl_recv_cb(uv_udp_t* handle, 93 ssize_t nread, 94 const uv_buf_t* buf, 95 const struct sockaddr* addr, 96 unsigned flags) { 97 CHECK_HANDLE(handle); 98 ASSERT(flags == 0); 99 100 if (nread < 0) { 101 ASSERT(0 && "unexpected error"); 102 } 103 104 if (nread == 0) { 105 /* Returning unused buffer. Don't count towards cl_recv_cb_called */ 106 ASSERT_NULL(addr); 107 return; 108 } 109 110 ASSERT_NOT_NULL(addr); 111 ASSERT(nread == 4); 112 ASSERT(!memcmp("PING", buf->base, nread)); 113 114 cl_recv_cb_called++; 115 116 if (cl_recv_cb_called == 2) { 117 /* we are done with the server handle, we can close it */ 118 uv_close((uv_handle_t*) &server, close_cb); 119 } else { 120 int r; 121 char source_addr[64]; 122 123 r = uv_ip4_name((const struct sockaddr_in*)addr, source_addr, sizeof(source_addr)); 124 ASSERT(r == 0); 125 126 r = uv_udp_set_membership(&server, MULTICAST_ADDR, NULL, UV_LEAVE_GROUP); 127 ASSERT(r == 0); 128 129 #if !defined(__OpenBSD__) && !defined(__NetBSD__) 130 r = uv_udp_set_source_membership(&server, MULTICAST_ADDR, NULL, source_addr, UV_JOIN_GROUP); 131 ASSERT(r == 0); 132 #endif 133 134 r = do_send(&req_ss); 135 ASSERT(r == 0); 136 } 137 } 138 139 140 TEST_IMPL(udp_multicast_join) { 141 int r; 142 struct sockaddr_in addr; 143 144 ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); 145 146 r = uv_udp_init(uv_default_loop(), &server); 147 ASSERT(r == 0); 148 149 r = uv_udp_init(uv_default_loop(), &client); 150 ASSERT(r == 0); 151 152 /* bind to the desired port */ 153 r = uv_udp_bind(&server, (const struct sockaddr*) &addr, 0); 154 ASSERT(r == 0); 155 156 /* join the multicast channel */ 157 r = uv_udp_set_membership(&server, MULTICAST_ADDR, NULL, UV_JOIN_GROUP); 158 if (r == UV_ENODEV) 159 RETURN_SKIP("No multicast support."); 160 ASSERT(r == 0); 161 162 r = uv_udp_recv_start(&server, alloc_cb, cl_recv_cb); 163 ASSERT(r == 0); 164 165 r = do_send(&req); 166 ASSERT(r == 0); 167 168 ASSERT(close_cb_called == 0); 169 ASSERT(cl_recv_cb_called == 0); 170 ASSERT(sv_send_cb_called == 0); 171 172 /* run the loop till all events are processed */ 173 uv_run(uv_default_loop(), UV_RUN_DEFAULT); 174 175 ASSERT(cl_recv_cb_called == 2); 176 ASSERT(sv_send_cb_called == 2); 177 ASSERT(close_cb_called == 2); 178 179 MAKE_VALGRIND_HAPPY(); 180 return 0; 181 } 182