From 451de61b72c57c8d10bcf1c91072018e865bd6a0 Mon Sep 17 00:00:00 2001 From: Austin Foxley Date: Tue, 22 Jan 2013 10:47:31 -0800 Subject: [PATCH] unix, win: add uv_udp_set_multicast_interface() --- include/uv.h | 14 +++++ src/unix/udp.c | 25 +++++++++ src/win/udp.c | 40 +++++++++++++ test/test-list.h | 1 + test/test-udp-multicast-interface.c | 87 +++++++++++++++++++++++++++++ uv.gyp | 1 + 6 files changed, 168 insertions(+) create mode 100644 test/test-udp-multicast-interface.c diff --git a/include/uv.h b/include/uv.h index 4eeade74..6d09dfdc 100644 --- a/include/uv.h +++ b/include/uv.h @@ -978,6 +978,20 @@ UV_EXTERN int uv_udp_set_multicast_loop(uv_udp_t* handle, int on); */ UV_EXTERN int uv_udp_set_multicast_ttl(uv_udp_t* handle, int ttl); + +/* + * Set the multicast interface to send on + * + * Arguments: + * handle UDP handle. Should have been initialized with + * `uv_udp_init`. + * interface_addr interface address + * + * Returns: + * 0 on success, or an error code < 0 on failure. + */ +UV_EXTERN int uv_udp_set_multicast_interface(uv_udp_t* handle, const char* interface_addr); + /* * Set broadcast on or off * diff --git a/src/unix/udp.c b/src/unix/udp.c index a2b3dc32..ae3cc8dc 100644 --- a/src/unix/udp.c +++ b/src/unix/udp.c @@ -539,6 +539,31 @@ int uv_udp_set_multicast_loop(uv_udp_t* handle, int on) { return uv__setsockopt_maybe_char(handle, IP_MULTICAST_LOOP, on); } +int uv_udp_set_multicast_interface(uv_udp_t* handle, const char* interface_addr) { + struct in_addr addr; + int err; + + memset(&addr, 0, sizeof addr); + + if (interface_addr) { + err = uv_inet_pton(AF_INET, interface_addr, &addr.s_addr); + if (err) + return err; + } else { + addr.s_addr = htonl(INADDR_ANY); + } + + if (setsockopt(handle->io_watcher.fd, + IPPROTO_IP, + IP_MULTICAST_IF, + (void*) &addr, + sizeof addr) == -1) { + return -errno; + } + + return 0; +} + int uv_udp_getsockname(uv_udp_t* handle, struct sockaddr* name, int* namelen) { socklen_t socklen; diff --git a/src/win/udp.c b/src/win/udp.c index 31812e46..282d4948 100644 --- a/src/win/udp.c +++ b/src/win/udp.c @@ -585,6 +585,46 @@ int uv_udp_set_membership(uv_udp_t* handle, const char* multicast_addr, } +int uv_udp_set_multicast_interface(uv_udp_t* handle, const char* interface_addr) { + struct in_addr addr; + int err; + + memset(&addr, 0, sizeof addr); + + if (handle->flags & UV_HANDLE_IPV6) { + return UV_ENOSYS; + } + + /* If the socket is unbound, bind to inaddr_any. */ + if (!(handle->flags & UV_HANDLE_BOUND)) { + err = uv_udp_try_bind(handle, + (const struct sockaddr*) &uv_addr_ip4_any_, + sizeof(uv_addr_ip4_any_), + 0); + if (err) + return uv_translate_sys_error(err); + } + + if (interface_addr) { + err = uv_inet_pton(AF_INET, interface_addr, &addr.s_addr); + if (err) + return err; + } else { + addr.s_addr = htonl(INADDR_ANY); + } + + if (setsockopt(handle->socket, + IPPROTO_IP, + IP_MULTICAST_IF, + (char*) &addr, + sizeof addr) == SOCKET_ERROR) { + return uv_translate_sys_error(WSAGetLastError()); + } + + return 0; +} + + int uv_udp_set_broadcast(uv_udp_t* handle, int value) { BOOL optval = (BOOL) value; int err; diff --git a/test/test-list.h b/test/test-list.h index d9a7afb7..f9a54411 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -81,6 +81,7 @@ TEST_DECLARE (tcp_bind6_localhost_ok) TEST_DECLARE (udp_send_and_recv) TEST_DECLARE (udp_multicast_join) TEST_DECLARE (udp_multicast_ttl) +TEST_DECLARE (udp_multicast_interface) TEST_DECLARE (udp_dgram_too_big) TEST_DECLARE (udp_dual_stack) TEST_DECLARE (udp_ipv6_only) diff --git a/test/test-udp-multicast-interface.c b/test/test-udp-multicast-interface.c new file mode 100644 index 00000000..285522e8 --- /dev/null +++ b/test/test-udp-multicast-interface.c @@ -0,0 +1,87 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" + +#include +#include +#include + +#define CHECK_HANDLE(handle) \ + ASSERT((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client) + +static uv_udp_t server; +static uv_udp_t client; + +static int sv_send_cb_called; +static int close_cb_called; + + +static void close_cb(uv_handle_t* handle) { + CHECK_HANDLE(handle); + close_cb_called++; +} + + +static void sv_send_cb(uv_udp_send_t* req, int status) { + ASSERT(req != NULL); + ASSERT(status == 0); + CHECK_HANDLE(req->handle); + + sv_send_cb_called++; + + uv_close((uv_handle_t*) req->handle, close_cb); +} + + +TEST_IMPL(udp_multicast_interface) { + int r; + uv_udp_send_t req; + uv_buf_t buf; + struct sockaddr_in addr = uv_ip4_addr("239.255.0.1", TEST_PORT); + + r = uv_udp_init(uv_default_loop(), &server); + ASSERT(r == 0); + + r = uv_udp_bind(&server, uv_ip4_addr("0.0.0.0", 0), 0); + ASSERT(r == 0); + + r = uv_udp_set_multicast_interface(&server, "0.0.0.0"); + ASSERT(r == 0); + + /* server sends "PING" */ + buf = uv_buf_init("PING", 4); + r = uv_udp_send(&req, &server, &buf, 1, addr, sv_send_cb); + ASSERT(r == 0); + + ASSERT(close_cb_called == 0); + ASSERT(sv_send_cb_called == 0); + + /* run the loop till all events are processed */ + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(sv_send_cb_called == 1); + ASSERT(close_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/uv.gyp b/uv.gyp index 65578f83..f72648de 100644 --- a/uv.gyp +++ b/uv.gyp @@ -387,6 +387,7 @@ 'test/test-udp-multicast-ttl.c', 'test/test-ip4-addr.c', 'test/test-ip6-addr.c', + 'test/test-udp-multicast-interface.c', ], 'conditions': [ [ 'OS=="win"', {