From 08327a7cbd214ae0cb3f3d87eeead2d9ce829904 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Fri, 28 Mar 2014 00:39:09 +0100 Subject: [PATCH] unix, windows: add IPv6 support for uv_udp_multicast_interface --- Makefile.am | 1 + src/unix/udp.c | 47 +++++++++---- src/win/udp.c | 57 ++++++++++----- test/test-list.h | 3 + test/test-udp-multicast-interface6.c | 101 +++++++++++++++++++++++++++ uv.gyp | 1 + 6 files changed, 178 insertions(+), 32 deletions(-) create mode 100644 test/test-udp-multicast-interface6.c diff --git a/Makefile.am b/Makefile.am index 02368515..12d1c4d0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -203,6 +203,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \ test/test-udp-dgram-too-big.c \ test/test-udp-ipv6.c \ test/test-udp-multicast-interface.c \ + test/test-udp-multicast-interface6.c \ test/test-udp-multicast-join.c \ test/test-udp-multicast-join6.c \ test/test-udp-multicast-ttl.c \ diff --git a/src/unix/udp.c b/src/unix/udp.c index f1382491..50fa7350 100644 --- a/src/unix/udp.c +++ b/src/unix/udp.c @@ -610,25 +610,44 @@ int uv_udp_set_multicast_loop(uv_udp_t* handle, int on) { } int uv_udp_set_multicast_interface(uv_udp_t* handle, const char* interface_addr) { - struct in_addr addr; - int err; + struct sockaddr_storage addr_st; + struct sockaddr_in* addr4; + struct sockaddr_in6* addr6; - memset(&addr, 0, sizeof addr); + addr4 = (struct sockaddr_in*) &addr_st; + addr6 = (struct sockaddr_in6*) &addr_st; - if (interface_addr) { - err = uv_inet_pton(AF_INET, interface_addr, &addr.s_addr); - if (err) - return err; + if (!interface_addr) { + memset(&addr_st, 0, sizeof addr_st); + addr_st.ss_family = AF_INET; + addr4->sin_addr.s_addr = htonl(INADDR_ANY); + } else if (uv_ip4_addr(interface_addr, 0, addr4) == 0) { + /* nothing, address was parsed */ + } else if (uv_ip6_addr(interface_addr, 0, addr6) == 0) { + /* nothing, address was parsed */ } else { - addr.s_addr = htonl(INADDR_ANY); + return -EINVAL; } - if (setsockopt(handle->io_watcher.fd, - IPPROTO_IP, - IP_MULTICAST_IF, - (void*) &addr, - sizeof addr) == -1) { - return -errno; + if (addr_st.ss_family == AF_INET) { + if (setsockopt(handle->io_watcher.fd, + IPPROTO_IP, + IP_MULTICAST_IF, + (void*) &addr4->sin_addr, + sizeof(addr4->sin_addr)) == -1) { + return -errno; + } + } else if (addr_st.ss_family == AF_INET6) { + if (setsockopt(handle->io_watcher.fd, + IPPROTO_IPV6, + IPV6_MULTICAST_IF, + &addr6->sin6_scope_id, + sizeof(addr6->sin6_scope_id)) == -1) { + return -errno; + } + } else { + assert(0 && "unexpected address family"); + abort(); } return 0; diff --git a/src/win/udp.c b/src/win/udp.c index 2643a26f..80958404 100644 --- a/src/win/udp.c +++ b/src/win/udp.c @@ -660,14 +660,10 @@ int uv_udp_set_membership(uv_udp_t* handle, 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; - } + struct sockaddr_storage addr_st; + struct sockaddr_in* addr4; + struct sockaddr_in6* addr6; /* If the socket is unbound, bind to inaddr_any. */ if (!(handle->flags & UV_HANDLE_BOUND)) { @@ -679,20 +675,45 @@ int uv_udp_set_multicast_interface(uv_udp_t* handle, const char* interface_addr) return uv_translate_sys_error(err); } - if (interface_addr) { - err = uv_inet_pton(AF_INET, interface_addr, &addr.s_addr); - if (err) - return err; + addr4 = (struct sockaddr_in*) &addr_st; + addr6 = (struct sockaddr_in6*) &addr_st; + + if (!interface_addr) { + memset(&addr_st, 0, sizeof addr_st); + if (handle->flags & UV_HANDLE_IPV6) { + addr_st.ss_family = AF_INET6; + addr6->sin6_scope_id = 0; + } else { + addr_st.ss_family = AF_INET; + addr4->sin_addr.s_addr = htonl(INADDR_ANY); + } + } else if (uv_ip4_addr(interface_addr, 0, addr4) == 0) { + /* nothing, address was parsed */ + } else if (uv_ip6_addr(interface_addr, 0, addr6) == 0) { + /* nothing, address was parsed */ } else { - addr.s_addr = htonl(INADDR_ANY); + return UV_EINVAL; } - if (setsockopt(handle->socket, - IPPROTO_IP, - IP_MULTICAST_IF, - (char*) &addr, - sizeof addr) == SOCKET_ERROR) { - return uv_translate_sys_error(WSAGetLastError()); + if (addr_st.ss_family == AF_INET) { + if (setsockopt(handle->socket, + IPPROTO_IP, + IP_MULTICAST_IF, + (char*) &addr4->sin_addr, + sizeof(addr4->sin_addr)) == SOCKET_ERROR) { + return uv_translate_sys_error(WSAGetLastError()); + } + } else if (addr_st.ss_family == AF_INET6) { + if (setsockopt(handle->socket, + IPPROTO_IPV6, + IPV6_MULTICAST_IF, + (char*) &addr6->sin6_scope_id, + sizeof(addr6->sin6_scope_id)) == SOCKET_ERROR) { + return uv_translate_sys_error(WSAGetLastError()); + } + } else { + assert(0 && "unexpected address family"); + abort(); } return 0; diff --git a/test/test-list.h b/test/test-list.h index fbf7814f..7d365a37 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -84,6 +84,7 @@ TEST_DECLARE (udp_multicast_join) TEST_DECLARE (udp_multicast_join6) TEST_DECLARE (udp_multicast_ttl) TEST_DECLARE (udp_multicast_interface) +TEST_DECLARE (udp_multicast_interface6) TEST_DECLARE (udp_dgram_too_big) TEST_DECLARE (udp_dual_stack) TEST_DECLARE (udp_ipv6_only) @@ -353,6 +354,8 @@ TASK_LIST_START TEST_ENTRY (udp_dual_stack) TEST_ENTRY (udp_ipv6_only) TEST_ENTRY (udp_options) + TEST_ENTRY (udp_multicast_interface) + TEST_ENTRY (udp_multicast_interface6) TEST_ENTRY (udp_multicast_join) TEST_ENTRY (udp_multicast_join6) TEST_ENTRY (udp_multicast_ttl) diff --git a/test/test-udp-multicast-interface6.c b/test/test-udp-multicast-interface6.c new file mode 100644 index 00000000..27b401dc --- /dev/null +++ b/test/test-udp-multicast-interface6.c @@ -0,0 +1,101 @@ +/* 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); + printf("-- sv_send_cb: %s (%d): %s\n", uv_err_name(status), status, uv_strerror(status)); + ASSERT(status == 0); + CHECK_HANDLE(req->handle); + + sv_send_cb_called++; + + uv_close((uv_handle_t*) req->handle, close_cb); +} + + +TEST_IMPL(udp_multicast_interface6) { + int r; + uv_udp_send_t req; + uv_buf_t buf; + struct sockaddr_in6 addr; + struct sockaddr_in6 baddr; + + ASSERT(0 == uv_ip6_addr("::1", TEST_PORT, &addr)); + + r = uv_udp_init(uv_default_loop(), &server); + ASSERT(r == 0); + + ASSERT(0 == uv_ip6_addr("::", 0, &baddr)); + r = uv_udp_bind(&server, (const struct sockaddr*)&baddr, 0); + ASSERT(r == 0); + +#if defined(__APPLE__) + r = uv_udp_set_multicast_interface(&server, "::1%lo0"); +#else + r = uv_udp_set_multicast_interface(&server, "::1"); +#endif + ASSERT(r == 0); + + /* server sends "PING" */ + buf = uv_buf_init("PING", 4); + r = uv_udp_send(&req, + &server, + &buf, + 1, + (const struct sockaddr*)&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 34946c16..f277e842 100644 --- a/uv.gyp +++ b/uv.gyp @@ -395,6 +395,7 @@ 'test/test-ip4-addr.c', 'test/test-ip6-addr.c', 'test/test-udp-multicast-interface.c', + 'test/test-udp-multicast-interface6.c', ], 'conditions': [ [ 'OS=="win"', {