unix,win: implement uv_udp_open_ex

Fixes: https://github.com/libuv/libuv/issues/4551
Signed-off-by: Juan José Arboleda <soyjuanarbol@gmail.com>
This commit is contained in:
Juan José Arboleda 2025-09-30 17:29:03 -05:00 committed by Juan José
parent 74079a8e47
commit 54063830a6
4 changed files with 78 additions and 6 deletions

View File

@ -173,6 +173,22 @@ API
.. versionadded:: 1.7.0 .. versionadded:: 1.7.0
.. versionchanged:: 1.37.0 added the `UV_UDP_RECVMMSG` flag. .. versionchanged:: 1.37.0 added the `UV_UDP_RECVMMSG` flag.
.. c:function:: int uv_udp_open_ex(uv_udp_t* handle, uv_os_sock_t sock, unsigned int flags)
Opens an existing file descriptor or Windows SOCKET as a UDP handle.
:param handle: UDP handle. Should have been initialized with
:c:func:`uv_udp_init`.
:param sock: An existing socket to associate with the handle.
:param flags: Flags that control socket behavior,
``UV_UDP_REUSEADDR``, and ``UV_UDP_REUSEPORT`` are supported.
:returns: 0 on success, or an error code < 0 on failure.
.. versionadded:: 1.52.0
.. c:function:: int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) .. c:function:: int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock)
Opens an existing file descriptor or Windows SOCKET as a UDP handle. Opens an existing file descriptor or Windows SOCKET as a UDP handle.
@ -189,6 +205,10 @@ API
The passed file descriptor or SOCKET is not checked for its type, but The passed file descriptor or SOCKET is not checked for its type, but
it's required that it represents a valid datagram socket. it's required that it represents a valid datagram socket.
Internally sets the SO_REUSEADDR socket option unconditionally. This
means the reuse flag is always enabled, regardless of user intent. For
more control use :c:func:`uv_udp_open_ex`.
.. c:function:: int uv_udp_bind(uv_udp_t* handle, const struct sockaddr* addr, unsigned int flags) .. c:function:: int uv_udp_bind(uv_udp_t* handle, const struct sockaddr* addr, unsigned int flags)
Bind the UDP handle to an IP address and port. Bind the UDP handle to an IP address and port.

View File

@ -746,6 +746,9 @@ struct uv_udp_send_s {
UV_EXTERN int uv_udp_init(uv_loop_t*, uv_udp_t* handle); UV_EXTERN int uv_udp_init(uv_loop_t*, uv_udp_t* handle);
UV_EXTERN int uv_udp_init_ex(uv_loop_t*, uv_udp_t* handle, unsigned int flags); UV_EXTERN int uv_udp_init_ex(uv_loop_t*, uv_udp_t* handle, unsigned int flags);
UV_EXTERN int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock); UV_EXTERN int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock);
UV_EXTERN int uv_udp_open_ex(uv_udp_t* handle,
uv_os_sock_t sock,
unsigned int flags);
UV_EXTERN int uv_udp_bind(uv_udp_t* handle, UV_EXTERN int uv_udp_bind(uv_udp_t* handle,
const struct sockaddr* addr, const struct sockaddr* addr,
unsigned int flags); unsigned int flags);

View File

@ -897,9 +897,13 @@ int uv_udp_using_recvmmsg(const uv_udp_t* handle) {
} }
int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) { int uv_udp_open_ex(uv_udp_t* handle, uv_os_sock_t sock, unsigned int flags) {
int err; int err;
/* Check for bad flags. */
if (flags & ~(UV_UDP_REUSEADDR | UV_UDP_REUSEPORT))
return UV_EINVAL;
/* Check for already active socket. */ /* Check for already active socket. */
if (handle->io_watcher.fd != -1) if (handle->io_watcher.fd != -1)
return UV_EBUSY; return UV_EBUSY;
@ -911,9 +915,17 @@ int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) {
if (err) if (err)
return err; return err;
err = uv__sock_reuseaddr(sock); if (flags & UV_UDP_REUSEADDR) {
if (err) err = uv__sock_reuseaddr(sock);
return err; if (err)
return err;
}
if (flags & UV_UDP_REUSEPORT) {
err = uv__sock_reuseport(sock);
if (err)
return err;
}
handle->io_watcher.fd = sock; handle->io_watcher.fd = sock;
if (uv__udp_is_connected(handle)) if (uv__udp_is_connected(handle))
@ -923,6 +935,15 @@ int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) {
} }
int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) {
/*
* Keep backward compatibility, always set REUSEADDR.
* Refs: https://github.com/libuv/libuv/issues/4551
*/
return uv_udp_open_ex(handle, sock, UV_UDP_REUSEADDR);
}
int uv_udp_set_membership(uv_udp_t* handle, int uv_udp_set_membership(uv_udp_t* handle,
const char* multicast_addr, const char* multicast_addr,
const char* interface_addr, const char* interface_addr,

View File

@ -204,7 +204,7 @@ static int uv__udp_maybe_bind(uv_udp_t* handle,
* so we just return an error directly when UV_UDP_REUSEPORT is requested * so we just return an error directly when UV_UDP_REUSEPORT is requested
* for binding the socket. */ * for binding the socket. */
if (flags & UV_UDP_REUSEPORT) if (flags & UV_UDP_REUSEPORT)
return ERROR_NOT_SUPPORTED; return UV_ENOTSUP;
if ((flags & UV_UDP_IPV6ONLY) && addr->sa_family != AF_INET6) { if ((flags & UV_UDP_IPV6ONLY) && addr->sa_family != AF_INET6) {
/* UV_UDP_IPV6ONLY is supported only for IPV6 sockets */ /* UV_UDP_IPV6ONLY is supported only for IPV6 sockets */
@ -908,11 +908,15 @@ int uv__udp_is_bound(uv_udp_t* handle) {
} }
int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) { int uv_udp_open_ex(uv_udp_t* handle, uv_os_sock_t sock, unsigned int flags) {
WSAPROTOCOL_INFOW protocol_info; WSAPROTOCOL_INFOW protocol_info;
int opt_len; int opt_len;
int err; int err;
/* Check for bad flags. */
if (flags & ~(UV_UDP_REUSEADDR | UV_UDP_REUSEPORT))
return UV_EINVAL;
/* Detect the address family of the socket. */ /* Detect the address family of the socket. */
opt_len = (int) sizeof protocol_info; opt_len = (int) sizeof protocol_info;
if (getsockopt(sock, if (getsockopt(sock,
@ -930,6 +934,25 @@ int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) {
if (err) if (err)
return uv_translate_sys_error(err); return uv_translate_sys_error(err);
/* There is no SO_REUSEPORT on Windows, Windows only knows SO_REUSEADDR.
* so we just return an error directly when UV_UDP_REUSEPORT is requested
* for binding the socket. */
if (flags & UV_UDP_REUSEPORT)
return UV_ENOTSUP;
if (flags & UV_UDP_REUSEADDR) {
DWORD yes = 1;
/* Set SO_REUSEADDR on the socket. */
if (setsockopt(handle->socket,
SOL_SOCKET,
SO_REUSEADDR,
(char*) &yes,
sizeof yes) == SOCKET_ERROR) {
err = WSAGetLastError();
return uv_translate_sys_error(err);
}
}
if (uv__udp_is_bound(handle)) if (uv__udp_is_bound(handle))
handle->flags |= UV_HANDLE_BOUND; handle->flags |= UV_HANDLE_BOUND;
@ -940,6 +963,11 @@ int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) {
} }
int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) {
return uv_udp_open_ex(handle, sock, 0);
}
#define SOCKOPT_SETTER(name, option4, option6, validate) \ #define SOCKOPT_SETTER(name, option4, option6, validate) \
int uv_udp_set_##name(uv_udp_t* handle, int value) { \ int uv_udp_set_##name(uv_udp_t* handle, int value) { \
DWORD optval = (DWORD) value; \ DWORD optval = (DWORD) value; \