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
.. 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)
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
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)
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_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_ex(uv_udp_t* handle,
uv_os_sock_t sock,
unsigned int flags);
UV_EXTERN int uv_udp_bind(uv_udp_t* handle,
const struct sockaddr* addr,
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;
/* Check for bad flags. */
if (flags & ~(UV_UDP_REUSEADDR | UV_UDP_REUSEPORT))
return UV_EINVAL;
/* Check for already active socket. */
if (handle->io_watcher.fd != -1)
return UV_EBUSY;
@ -911,9 +915,17 @@ int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) {
if (err)
return err;
err = uv__sock_reuseaddr(sock);
if (err)
return err;
if (flags & UV_UDP_REUSEADDR) {
err = uv__sock_reuseaddr(sock);
if (err)
return err;
}
if (flags & UV_UDP_REUSEPORT) {
err = uv__sock_reuseport(sock);
if (err)
return err;
}
handle->io_watcher.fd = sock;
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,
const char* multicast_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
* for binding the socket. */
if (flags & UV_UDP_REUSEPORT)
return ERROR_NOT_SUPPORTED;
return UV_ENOTSUP;
if ((flags & UV_UDP_IPV6ONLY) && addr->sa_family != AF_INET6) {
/* 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;
int opt_len;
int err;
/* Check for bad flags. */
if (flags & ~(UV_UDP_REUSEADDR | UV_UDP_REUSEPORT))
return UV_EINVAL;
/* Detect the address family of the socket. */
opt_len = (int) sizeof protocol_info;
if (getsockopt(sock,
@ -930,6 +934,25 @@ int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) {
if (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))
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) \
int uv_udp_set_##name(uv_udp_t* handle, int value) { \
DWORD optval = (DWORD) value; \