1
0
mirror of https://github.com/libuv/libuv synced 2025-03-28 21:13:16 +00:00

unix, windows: do not set SO_REUSEADDR by default on udp

Add UV_UDP_REUSEADDR flag instead, which can be passed to uv_udp_bind.
If the udp handle is unbound when uv_udp_set_memberhsip or
uv_udp_set_multicast_interface is called, the handle will be bound with
UV_UDP_REUSEADDR set.
This commit is contained in:
Saúl Ibarra Corretgé 2014-03-31 09:56:37 +02:00
parent 872263314e
commit 3558d65d2f
8 changed files with 198 additions and 75 deletions

View File

@ -200,6 +200,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \
test/test-timer-from-check.c \
test/test-timer.c \
test/test-tty.c \
test/test-udp-bind.c \
test/test-udp-dgram-too-big.c \
test/test-udp-ipv6.c \
test/test-udp-multicast-interface.c \

View File

@ -846,7 +846,15 @@ enum uv_udp_flags {
* Indicates message was truncated because read buffer was too small. The
* remainder was discarded by the OS. Used in uv_udp_recv_cb.
*/
UV_UDP_PARTIAL = 2
UV_UDP_PARTIAL = 2,
/* Indicates if SO_REUSEADDR will be set when binding the handle.
* This sets the SO_REUSEPORT socket flag on the BSDs and OS X. On other
* UNIX platforms, it sets the SO_REUSEADDR flag. What that means is that
* multiple threads or processes can bind to the same address without error
* (provided they all set the flag) but only the last one to bind will receive
* any traffic, in effect "stealing" the port from the previous listener.
*/
UV_UDP_REUSEADDR = 4
};
/*
@ -921,18 +929,11 @@ UV_EXTERN int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock);
* handle UDP handle. Should have been initialized with `uv_udp_init`.
* addr struct sockaddr_in or struct sockaddr_in6 with the address and
* port to bind to.
* flags Unused.
* flags Indicate how the socket will be bound, UV_UDP_IPV6ONLY and
* UV_UDP_REUSEADDR are supported.
*
* Returns:
* 0 on success, or an error code < 0 on failure.
*
* This sets the SO_REUSEPORT socket flag on the BSDs and OS X. On other
* UNIX platforms, it sets the SO_REUSEADDR flag. What that means is that
* multiple threads or processes can bind to the same address without error
* (provided they all set the flag) but only the last one to bind will receive
* any traffic, in effect "stealing" the port from the previous listener.
* This behavior is something of an anomaly and may be replaced by an explicit
* opt-in mechanism in future versions of libuv.
*/
UV_EXTERN int uv_udp_bind(uv_udp_t* handle,
const struct sockaddr* addr,

View File

@ -42,7 +42,9 @@ static void uv__udp_run_pending(uv_udp_t* handle);
static void uv__udp_io(uv_loop_t* loop, uv__io_t* w, unsigned int revents);
static void uv__udp_recvmsg(uv_loop_t* loop, uv__io_t* w, unsigned int revents);
static void uv__udp_sendmsg(uv_loop_t* loop, uv__io_t* w, unsigned int revents);
static int uv__udp_maybe_deferred_bind(uv_udp_t* handle, int domain);
static int uv__udp_maybe_deferred_bind(uv_udp_t* handle,
int domain,
unsigned int flags);
void uv__udp_close(uv_udp_t* handle) {
@ -308,7 +310,7 @@ int uv__udp_bind(uv_udp_t* handle,
fd = -1;
/* Check for bad flags. */
if (flags & ~UV_UDP_IPV6ONLY)
if (flags & ~(UV_UDP_IPV6ONLY | UV_UDP_REUSEADDR))
return -EINVAL;
/* Cannot set IPv6-only mode on non-IPv6 socket. */
@ -323,9 +325,11 @@ int uv__udp_bind(uv_udp_t* handle,
handle->io_watcher.fd = fd;
}
err = uv__set_reuse(fd);
if (err)
goto out;
if (flags & UV_UDP_REUSEADDR) {
err = uv__set_reuse(fd);
if (err)
goto out;
}
if (flags & UV_UDP_IPV6ONLY) {
#ifdef IPV6_V6ONLY
@ -354,7 +358,9 @@ out:
}
static int uv__udp_maybe_deferred_bind(uv_udp_t* handle, int domain) {
static int uv__udp_maybe_deferred_bind(uv_udp_t* handle,
int domain,
unsigned int flags) {
unsigned char taddr[sizeof(struct sockaddr_in6)];
socklen_t addrlen;
@ -387,7 +393,7 @@ static int uv__udp_maybe_deferred_bind(uv_udp_t* handle, int domain) {
abort();
}
return uv__udp_bind(handle, (const struct sockaddr*) &taddr, addrlen, 0);
return uv__udp_bind(handle, (const struct sockaddr*) &taddr, addrlen, flags);
}
@ -402,7 +408,7 @@ int uv__udp_send(uv_udp_send_t* req,
assert(nbufs > 0);
err = uv__udp_maybe_deferred_bind(handle, addr->sa_family);
err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0);
if (err)
return err;
@ -547,15 +553,23 @@ int uv_udp_set_membership(uv_udp_t* handle,
const char* multicast_addr,
const char* interface_addr,
uv_membership membership) {
int err;
struct sockaddr_in addr4;
struct sockaddr_in6 addr6;
if (uv_ip4_addr(multicast_addr, 0, &addr4) == 0)
if (uv_ip4_addr(multicast_addr, 0, &addr4) == 0) {
err = uv__udp_maybe_deferred_bind(handle, AF_INET, UV_UDP_REUSEADDR);
if (err)
return err;
return uv__udp_set_membership4(handle, &addr4, interface_addr, membership);
else if (uv_ip6_addr(multicast_addr, 0, &addr6) == 0)
} else if (uv_ip6_addr(multicast_addr, 0, &addr6) == 0) {
err = uv__udp_maybe_deferred_bind(handle, AF_INET6, UV_UDP_REUSEADDR);
if (err)
return err;
return uv__udp_set_membership6(handle, &addr6, interface_addr, membership);
else
} else {
return -EINVAL;
}
}
@ -610,6 +624,7 @@ 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) {
int err;
struct sockaddr_storage addr_st;
struct sockaddr_in* addr4;
struct sockaddr_in6* addr6;
@ -630,6 +645,9 @@ int uv_udp_set_multicast_interface(uv_udp_t* handle, const char* interface_addr)
}
if (addr_st.ss_family == AF_INET) {
err = uv__udp_maybe_deferred_bind(handle, AF_INET, UV_UDP_REUSEADDR);
if (err)
return err;
if (setsockopt(handle->io_watcher.fd,
IPPROTO_IP,
IP_MULTICAST_IF,
@ -638,6 +656,9 @@ int uv_udp_set_multicast_interface(uv_udp_t* handle, const char* interface_addr)
return -errno;
}
} else if (addr_st.ss_family == AF_INET6) {
err = uv__udp_maybe_deferred_bind(handle, AF_INET6, UV_UDP_REUSEADDR);
if (err)
return err;
if (setsockopt(handle->io_watcher.fd,
IPPROTO_IPV6,
IPV6_MULTICAST_IF,
@ -682,7 +703,7 @@ int uv__udp_recv_start(uv_udp_t* handle,
if (uv__io_active(&handle->io_watcher, UV__POLLIN))
return -EALREADY; /* FIXME(bnoordhuis) Should be -EBUSY. */
err = uv__udp_maybe_deferred_bind(handle, AF_INET);
err = uv__udp_maybe_deferred_bind(handle, AF_INET, 0);
if (err)
return err;

View File

@ -62,15 +62,6 @@ static int uv_udp_set_socket(uv_loop_t* loop, uv_udp_t* handle, SOCKET socket,
assert(handle->socket == INVALID_SOCKET);
/* Set SO_REUSEADDR on the socket. */
if (setsockopt(socket,
SOL_SOCKET,
SO_REUSEADDR,
(char*) &yes,
sizeof yes) == SOCKET_ERROR) {
return WSAGetLastError();
}
/* Set the socket to nonblocking mode */
if (ioctlsocket(socket, FIONBIO, &yes) == SOCKET_ERROR) {
return WSAGetLastError();
@ -168,14 +159,17 @@ void uv_udp_endgame(uv_loop_t* loop, uv_udp_t* handle) {
}
static int uv_udp_try_bind(uv_udp_t* handle,
const struct sockaddr* addr,
unsigned int addrlen,
unsigned int flags) {
static int uv_udp_maybe_bind(uv_udp_t* handle,
const struct sockaddr* addr,
unsigned int addrlen,
unsigned int flags) {
int r;
int err;
DWORD no = 0;
if (handle->flags & UV_HANDLE_BOUND)
return 0;
if ((flags & UV_UDP_IPV6ONLY) && addr->sa_family != AF_INET6) {
/* UV_UDP_IPV6ONLY is supported only for IPV6 sockets */
return ERROR_INVALID_PARAMETER;
@ -193,6 +187,20 @@ static int uv_udp_try_bind(uv_udp_t* handle,
return err;
}
if (flags & UV_UDP_REUSEADDR) {
DWORD yes = 1;
/* Set SO_REUSEADDR on the socket. */
if (setsockopt(sock,
SOL_SOCKET,
SO_REUSEADDR,
(char*) &yes,
sizeof yes) == SOCKET_ERROR) {
err = WSAGetLastError();
closesocket(sock);
return err;
}
}
if (addr->sa_family == AF_INET6)
handle->flags |= UV_HANDLE_IPV6;
}
@ -324,14 +332,12 @@ int uv__udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb,
return WSAEALREADY;
}
if (!(handle->flags & UV_HANDLE_BOUND)) {
err = uv_udp_try_bind(handle,
err = uv_udp_maybe_bind(handle,
(const struct sockaddr*) &uv_addr_ip4_any_,
sizeof(uv_addr_ip4_any_),
0);
if (err)
return err;
}
if (err)
return err;
handle->flags |= UV_HANDLE_READING;
INCREASE_ACTIVE_COUNT(loop, handle);
@ -544,14 +550,12 @@ static int uv__udp_set_membership4(uv_udp_t* handle,
return UV_EINVAL;
/* If the socket is unbound, bind to inaddr_any. */
if (!(handle->flags & UV_HANDLE_BOUND)) {
err = uv_udp_try_bind(handle,
err = uv_udp_maybe_bind(handle,
(const struct sockaddr*) &uv_addr_ip4_any_,
sizeof(uv_addr_ip4_any_),
0);
if (err)
return uv_translate_sys_error(err);
}
UV_UDP_REUSEADDR);
if (err)
return uv_translate_sys_error(err);
memset(&mreq, 0, sizeof mreq);
@ -598,15 +602,13 @@ int uv__udp_set_membership6(uv_udp_t* handle,
if ((handle->flags & UV_HANDLE_BOUND) && !(handle->flags & UV_HANDLE_IPV6))
return UV_EINVAL;
if (!(handle->flags & UV_HANDLE_BOUND)) {
err = uv_udp_try_bind(handle,
err = uv_udp_maybe_bind(handle,
(const struct sockaddr*) &uv_addr_ip6_any_,
sizeof(uv_addr_ip6_any_),
0);
UV_UDP_REUSEADDR);
if (err)
return uv_translate_sys_error(err);
}
if (err)
return uv_translate_sys_error(err);
memset(&mreq, 0, sizeof(mreq));
@ -665,16 +667,6 @@ int uv_udp_set_multicast_interface(uv_udp_t* handle, const char* interface_addr)
struct sockaddr_in* addr4;
struct sockaddr_in6* addr6;
/* 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);
}
addr4 = (struct sockaddr_in*) &addr_st;
addr6 = (struct sockaddr_in6*) &addr_st;
@ -696,6 +688,12 @@ int uv_udp_set_multicast_interface(uv_udp_t* handle, const char* interface_addr)
}
if (addr_st.ss_family == AF_INET) {
err = uv_udp_maybe_bind(handle,
(const struct sockaddr*) &uv_addr_ip4_any_,
sizeof(uv_addr_ip4_any_),
UV_UDP_REUSEADDR);
if (err)
return uv_translate_sys_error(err);
if (setsockopt(handle->socket,
IPPROTO_IP,
IP_MULTICAST_IF,
@ -704,6 +702,12 @@ int uv_udp_set_multicast_interface(uv_udp_t* handle, const char* interface_addr)
return uv_translate_sys_error(WSAGetLastError());
}
} else if (addr_st.ss_family == AF_INET6) {
err = uv_udp_maybe_bind(handle,
(const struct sockaddr*) &uv_addr_ip6_any_,
sizeof(uv_addr_ip6_any_),
UV_UDP_REUSEADDR);
if (err)
return uv_translate_sys_error(err);
if (setsockopt(handle->socket,
IPPROTO_IPV6,
IPV6_MULTICAST_IF,
@ -725,14 +729,12 @@ int uv_udp_set_broadcast(uv_udp_t* handle, int value) {
int err;
/* If the socket is unbound, bind to inaddr_any. */
if (!(handle->flags & UV_HANDLE_BOUND)) {
err = uv_udp_try_bind(handle,
err = uv_udp_maybe_bind(handle,
(const struct sockaddr*) &uv_addr_ip4_any_,
sizeof(uv_addr_ip4_any_),
0);
if (err)
return uv_translate_sys_error(err);
}
if (err)
return uv_translate_sys_error(err);
if (setsockopt(handle->socket,
SOL_SOCKET,
@ -779,14 +781,12 @@ int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) {
} \
\
/* If the socket is unbound, bind to inaddr_any. */ \
if (!(handle->flags & UV_HANDLE_BOUND)) { \
err = uv_udp_try_bind(handle, \
err = uv_udp_maybe_bind(handle, \
(const struct sockaddr*) &uv_addr_ip4_any_, \
sizeof(uv_addr_ip4_any_), \
0); \
if (err) \
return uv_translate_sys_error(err); \
} \
if (err) \
return uv_translate_sys_error(err); \
\
if (!(handle->flags & UV_HANDLE_IPV6)) { \
/* Set IPv4 socket option */ \
@ -842,7 +842,7 @@ int uv__udp_bind(uv_udp_t* handle,
unsigned int flags) {
int err;
err = uv_udp_try_bind(handle, addr, addrlen, flags);
err = uv_udp_maybe_bind(handle, addr, addrlen, flags);
if (err)
return uv_translate_sys_error(err);
@ -871,7 +871,7 @@ int uv__udp_send(uv_udp_send_t* req,
} else {
abort();
}
err = uv_udp_try_bind(handle, bind_addr, addrlen, 0);
err = uv_udp_maybe_bind(handle, bind_addr, addrlen, 0);
if (err)
return uv_translate_sys_error(err);
}

View File

@ -79,6 +79,8 @@ TEST_DECLARE (tcp_bind6_error_addrnotavail)
TEST_DECLARE (tcp_bind6_error_fault)
TEST_DECLARE (tcp_bind6_error_inval)
TEST_DECLARE (tcp_bind6_localhost_ok)
TEST_DECLARE (udp_bind)
TEST_DECLARE (udp_bind_reuseaddr)
TEST_DECLARE (udp_send_and_recv)
TEST_DECLARE (udp_multicast_join)
TEST_DECLARE (udp_multicast_join6)
@ -349,6 +351,8 @@ TASK_LIST_START
TEST_ENTRY (tcp_bind6_error_inval)
TEST_ENTRY (tcp_bind6_localhost_ok)
TEST_ENTRY (udp_bind)
TEST_ENTRY (udp_bind_reuseaddr)
TEST_ENTRY (udp_send_and_recv)
TEST_ENTRY (udp_dgram_too_big)
TEST_ENTRY (udp_dual_stack)

93
test/test-udp-bind.c Normal file
View File

@ -0,0 +1,93 @@
/* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
TEST_IMPL(udp_bind) {
struct sockaddr_in addr;
uv_loop_t* loop;
uv_udp_t h1, h2;
int r;
ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr));
loop = uv_default_loop();
r = uv_udp_init(loop, &h1);
ASSERT(r == 0);
r = uv_udp_init(loop, &h2);
ASSERT(r == 0);
r = uv_udp_bind(&h1, (const struct sockaddr*) &addr, 0);
ASSERT(r == 0);
r = uv_udp_bind(&h2, (const struct sockaddr*) &addr, 0);
ASSERT(r == UV_EADDRINUSE);
uv_close((uv_handle_t*) &h1, NULL);
uv_close((uv_handle_t*) &h2, NULL);
r = uv_run(loop, UV_RUN_DEFAULT);
ASSERT(r == 0);
MAKE_VALGRIND_HAPPY();
return 0;
}
TEST_IMPL(udp_bind_reuseaddr) {
struct sockaddr_in addr;
uv_loop_t* loop;
uv_udp_t h1, h2;
int r;
ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr));
loop = uv_default_loop();
r = uv_udp_init(loop, &h1);
ASSERT(r == 0);
r = uv_udp_init(loop, &h2);
ASSERT(r == 0);
r = uv_udp_bind(&h1, (const struct sockaddr*) &addr, UV_UDP_REUSEADDR);
ASSERT(r == 0);
r = uv_udp_bind(&h2, (const struct sockaddr*) &addr, UV_UDP_REUSEADDR);
ASSERT(r == 0);
uv_close((uv_handle_t*) &h1, NULL);
uv_close((uv_handle_t*) &h2, NULL);
r = uv_run(loop, UV_RUN_DEFAULT);
ASSERT(r == 0);
MAKE_VALGRIND_HAPPY();
return 0;
}

View File

@ -73,7 +73,9 @@ TEST_IMPL(watcher_cross_stop) {
for (i = 0; i < ARRAY_SIZE(sockets); i++) {
ASSERT(0 == uv_udp_init(loop, &sockets[i]));
ASSERT(0 == uv_udp_bind(&sockets[i], (const struct sockaddr*) &addr, 0));
ASSERT(0 == uv_udp_bind(&sockets[i],
(const struct sockaddr*) &addr,
UV_UDP_REUSEADDR));
ASSERT(0 == uv_udp_recv_start(&sockets[i], alloc_cb, recv_cb));
ASSERT(0 == uv_udp_send(&reqs[i],
&sockets[i],

1
uv.gyp
View File

@ -383,6 +383,7 @@
'test/test-timer-from-check.c',
'test/test-timer.c',
'test/test-tty.c',
'test/test-udp-bind.c',
'test/test-udp-dgram-too-big.c',
'test/test-udp-ipv6.c',
'test/test-udp-open.c',