mirror of
https://github.com/libuv/libuv
synced 2025-03-28 21:13:16 +00:00
udp: add support for UDP connected sockets
Add two new methods: `uv_udp_connect()` to connect / disconnect an UDP handle. `uv_udp_getpeername()` to get the remote peer address of a connected UDP handle. Modify `uv_udp_send()` and `uv_udp_try_send()` to accept a `NULL` `addr` to send messages over an "UDP connection". Refs: https://github.com/libuv/leps/pull/10 PR-URL: https://github.com/libuv/libuv/pull/1872 Backport-PR-URL: https://github.com/libuv/libuv/pull/2217 Reviewed-By: Saúl Ibarra Corretgé <saghul@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
This commit is contained in:
parent
03061d54f0
commit
90415a3394
@ -153,6 +153,7 @@ set(uv_test_sources
|
||||
test/test-tty.c
|
||||
test/test-udp-alloc-cb-fail.c
|
||||
test/test-udp-bind.c
|
||||
test/test-udp-connect.c
|
||||
test/test-udp-create-socket-early.c
|
||||
test/test-udp-dgram-too-big.c
|
||||
test/test-udp-ipv6.c
|
||||
|
@ -281,6 +281,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \
|
||||
test/test-tty.c \
|
||||
test/test-udp-alloc-cb-fail.c \
|
||||
test/test-udp-bind.c \
|
||||
test/test-udp-connect.c \
|
||||
test/test-udp-create-socket-early.c \
|
||||
test/test-udp-dgram-too-big.c \
|
||||
test/test-udp-ipv6.c \
|
||||
|
@ -150,6 +150,44 @@ API
|
||||
|
||||
:returns: 0 on success, or an error code < 0 on failure.
|
||||
|
||||
.. c:function:: int uv_udp_connect(uv_udp_t* handle, const struct sockaddr* addr)
|
||||
|
||||
Associate the UDP handle to a remote address and port, so every
|
||||
message sent by this handle is automatically sent to that destination.
|
||||
Calling this function with a `NULL` `addr` disconnects the handle.
|
||||
Trying to call `uv_udp_connect()` on an already connected handle will result
|
||||
in an `UV_EISCONN` error. Trying to disconnect a handle that is not
|
||||
connected will return an `UV_ENOTCONN` error.
|
||||
|
||||
:param handle: UDP handle. Should have been initialized with
|
||||
:c:func:`uv_udp_init`.
|
||||
|
||||
:param addr: `struct sockaddr_in` or `struct sockaddr_in6`
|
||||
with the address and port to associate to.
|
||||
|
||||
:returns: 0 on success, or an error code < 0 on failure.
|
||||
|
||||
.. versionadded:: 1.27.0
|
||||
|
||||
.. c:function:: int uv_udp_getpeername(const uv_udp_t* handle, struct sockaddr* name, int* namelen)
|
||||
|
||||
Get the remote IP and port of the UDP handle on connected UDP handles.
|
||||
On unconnected handles, it returns `UV_ENOTCONN`.
|
||||
|
||||
:param handle: UDP handle. Should have been initialized with
|
||||
:c:func:`uv_udp_init` and bound.
|
||||
|
||||
:param name: Pointer to the structure to be filled with the address data.
|
||||
In order to support IPv4 and IPv6 `struct sockaddr_storage` should be
|
||||
used.
|
||||
|
||||
:param namelen: On input it indicates the data of the `name` field. On
|
||||
output it indicates how much of it was filled.
|
||||
|
||||
:returns: 0 on success, or an error code < 0 on failure
|
||||
|
||||
.. versionadded:: 1.27.0
|
||||
|
||||
.. c:function:: int uv_udp_getsockname(const uv_udp_t* handle, struct sockaddr* name, int* namelen)
|
||||
|
||||
Get the local IP and port of the UDP handle.
|
||||
@ -247,6 +285,12 @@ API
|
||||
(``0.0.0.0`` or ``::``) it will be changed to point to ``localhost``.
|
||||
This is done to match the behavior of Linux systems.
|
||||
|
||||
For connected UDP handles, `addr` must be set to `NULL`, otherwise it will
|
||||
return `UV_EISCONN` error.
|
||||
|
||||
For connectionless UDP handles, `addr` cannot be `NULL`, otherwise it will
|
||||
return `UV_EDESTADDRREQ` error.
|
||||
|
||||
:param req: UDP request handle. Need not be initialized.
|
||||
|
||||
:param handle: UDP handle. Should have been initialized with
|
||||
@ -266,15 +310,25 @@ API
|
||||
.. versionchanged:: 1.19.0 added ``0.0.0.0`` and ``::`` to ``localhost``
|
||||
mapping
|
||||
|
||||
.. versionchanged:: 1.27.0 added support for connected sockets
|
||||
|
||||
.. c:function:: int uv_udp_try_send(uv_udp_t* handle, const uv_buf_t bufs[], unsigned int nbufs, const struct sockaddr* addr)
|
||||
|
||||
Same as :c:func:`uv_udp_send`, but won't queue a send request if it can't
|
||||
be completed immediately.
|
||||
|
||||
For connected UDP handles, `addr` must be set to `NULL`, otherwise it will
|
||||
return `UV_EISCONN` error.
|
||||
|
||||
For connectionless UDP handles, `addr` cannot be `NULL`, otherwise it will
|
||||
return `UV_EDESTADDRREQ` error.
|
||||
|
||||
:returns: >= 0: number of bytes sent (it matches the given buffer size).
|
||||
< 0: negative error code (``UV_EAGAIN`` is returned when the message
|
||||
can't be sent immediately).
|
||||
|
||||
.. versionchanged:: 1.27.0 added support for connected sockets
|
||||
|
||||
.. c:function:: int uv_udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb, uv_udp_recv_cb recv_cb)
|
||||
|
||||
Prepare for receiving data. If the socket has not previously been bound
|
||||
|
@ -630,7 +630,11 @@ UV_EXTERN int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock);
|
||||
UV_EXTERN int uv_udp_bind(uv_udp_t* handle,
|
||||
const struct sockaddr* addr,
|
||||
unsigned int flags);
|
||||
UV_EXTERN int uv_udp_connect(uv_udp_t* handle, const struct sockaddr* addr);
|
||||
|
||||
UV_EXTERN int uv_udp_getpeername(const uv_udp_t* handle,
|
||||
struct sockaddr* name,
|
||||
int* namelen);
|
||||
UV_EXTERN int uv_udp_getsockname(const uv_udp_t* handle,
|
||||
struct sockaddr* name,
|
||||
int* namelen);
|
||||
|
@ -244,9 +244,9 @@ static int uv__pipe_getsockpeername(const uv_pipe_t* handle,
|
||||
addrlen = sizeof(sa);
|
||||
memset(&sa, 0, addrlen);
|
||||
err = uv__getsockpeername((const uv_handle_t*) handle,
|
||||
func,
|
||||
(struct sockaddr*) &sa,
|
||||
(int*) &addrlen);
|
||||
func,
|
||||
(struct sockaddr*) &sa,
|
||||
(int*) &addrlen);
|
||||
if (err < 0) {
|
||||
*size = 0;
|
||||
return err;
|
||||
|
@ -227,9 +227,14 @@ static void uv__udp_sendmsg(uv_udp_t* handle) {
|
||||
assert(req != NULL);
|
||||
|
||||
memset(&h, 0, sizeof h);
|
||||
h.msg_name = &req->addr;
|
||||
h.msg_namelen = (req->addr.ss_family == AF_INET6 ?
|
||||
sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in));
|
||||
if (req->addr.ss_family == AF_UNSPEC) {
|
||||
h.msg_name = NULL;
|
||||
h.msg_namelen = 0;
|
||||
} else {
|
||||
h.msg_name = &req->addr;
|
||||
h.msg_namelen = req->addr.ss_family == AF_INET6 ?
|
||||
sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
|
||||
}
|
||||
h.msg_iov = (struct iovec*) req->bufs;
|
||||
h.msg_iovlen = req->nbufs;
|
||||
|
||||
@ -383,6 +388,50 @@ static int uv__udp_maybe_deferred_bind(uv_udp_t* handle,
|
||||
}
|
||||
|
||||
|
||||
int uv__udp_connect(uv_udp_t* handle,
|
||||
const struct sockaddr* addr,
|
||||
unsigned int addrlen) {
|
||||
int err;
|
||||
|
||||
err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
do {
|
||||
errno = 0;
|
||||
err = connect(handle->io_watcher.fd, addr, addrlen);
|
||||
} while (err == -1 && errno == EINTR);
|
||||
|
||||
if (err)
|
||||
return UV__ERR(errno);
|
||||
|
||||
handle->flags |= UV_HANDLE_UDP_CONNECTED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv__udp_disconnect(uv_udp_t* handle) {
|
||||
int r;
|
||||
struct sockaddr addr;
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
|
||||
addr.sa_family = AF_UNSPEC;
|
||||
|
||||
do {
|
||||
errno = 0;
|
||||
r = connect(handle->io_watcher.fd, &addr, sizeof(addr));
|
||||
} while (r == -1 && errno == EINTR);
|
||||
|
||||
if (r == -1 && errno != EAFNOSUPPORT)
|
||||
return UV__ERR(errno);
|
||||
|
||||
handle->flags &= ~UV_HANDLE_UDP_CONNECTED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv__udp_send(uv_udp_send_t* req,
|
||||
uv_udp_t* handle,
|
||||
const uv_buf_t bufs[],
|
||||
@ -395,9 +444,11 @@ int uv__udp_send(uv_udp_send_t* req,
|
||||
|
||||
assert(nbufs > 0);
|
||||
|
||||
err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0);
|
||||
if (err)
|
||||
return err;
|
||||
if (addr) {
|
||||
err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* It's legal for send_queue_count > 0 even when the write_queue is empty;
|
||||
* it means there are error-state requests in the write_completed_queue that
|
||||
@ -407,7 +458,10 @@ int uv__udp_send(uv_udp_send_t* req,
|
||||
|
||||
uv__req_init(handle->loop, req, UV_UDP_SEND);
|
||||
assert(addrlen <= sizeof(req->addr));
|
||||
memcpy(&req->addr, addr, addrlen);
|
||||
if (addr == NULL)
|
||||
req->addr.ss_family = AF_UNSPEC;
|
||||
else
|
||||
memcpy(&req->addr, addr, addrlen);
|
||||
req->send_cb = send_cb;
|
||||
req->handle = handle;
|
||||
req->nbufs = nbufs;
|
||||
@ -459,9 +513,13 @@ int uv__udp_try_send(uv_udp_t* handle,
|
||||
if (handle->send_queue_count != 0)
|
||||
return UV_EAGAIN;
|
||||
|
||||
err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0);
|
||||
if (err)
|
||||
return err;
|
||||
if (addr) {
|
||||
err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0);
|
||||
if (err)
|
||||
return err;
|
||||
} else {
|
||||
assert(handle->flags & UV_HANDLE_UDP_CONNECTED);
|
||||
}
|
||||
|
||||
memset(&h, 0, sizeof h);
|
||||
h.msg_name = (struct sockaddr*) addr;
|
||||
@ -608,6 +666,7 @@ int uv_udp_init_ex(uv_loop_t* loop, uv_udp_t* handle, unsigned int flags) {
|
||||
uv__io_init(&handle->io_watcher, uv__udp_io, fd);
|
||||
QUEUE_INIT(&handle->write_queue);
|
||||
QUEUE_INIT(&handle->write_completed_queue);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -636,6 +695,9 @@ int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) {
|
||||
return err;
|
||||
|
||||
handle->io_watcher.fd = sock;
|
||||
if (uv__udp_is_connected(handle))
|
||||
handle->flags |= UV_HANDLE_UDP_CONNECTED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -851,6 +913,15 @@ int uv_udp_set_multicast_interface(uv_udp_t* handle, const char* interface_addr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int uv_udp_getpeername(const uv_udp_t* handle,
|
||||
struct sockaddr* name,
|
||||
int* namelen) {
|
||||
|
||||
return uv__getsockpeername((const uv_handle_t*) handle,
|
||||
getpeername,
|
||||
name,
|
||||
namelen);
|
||||
}
|
||||
|
||||
int uv_udp_getsockname(const uv_udp_t* handle,
|
||||
struct sockaddr* name,
|
||||
|
@ -317,17 +317,20 @@ int uv_tcp_connect(uv_connect_t* req,
|
||||
}
|
||||
|
||||
|
||||
int uv_udp_send(uv_udp_send_t* req,
|
||||
uv_udp_t* handle,
|
||||
const uv_buf_t bufs[],
|
||||
unsigned int nbufs,
|
||||
const struct sockaddr* addr,
|
||||
uv_udp_send_cb send_cb) {
|
||||
int uv_udp_connect(uv_udp_t* handle, const struct sockaddr* addr) {
|
||||
unsigned int addrlen;
|
||||
|
||||
if (handle->type != UV_UDP)
|
||||
return UV_EINVAL;
|
||||
|
||||
/* Disconnect the handle */
|
||||
if (addr == NULL) {
|
||||
if (!(handle->flags & UV_HANDLE_UDP_CONNECTED))
|
||||
return UV_ENOTCONN;
|
||||
|
||||
return uv__udp_disconnect(handle);
|
||||
}
|
||||
|
||||
if (addr->sa_family == AF_INET)
|
||||
addrlen = sizeof(struct sockaddr_in);
|
||||
else if (addr->sa_family == AF_INET6)
|
||||
@ -335,6 +338,66 @@ int uv_udp_send(uv_udp_send_t* req,
|
||||
else
|
||||
return UV_EINVAL;
|
||||
|
||||
if (handle->flags & UV_HANDLE_UDP_CONNECTED)
|
||||
return UV_EISCONN;
|
||||
|
||||
return uv__udp_connect(handle, addr, addrlen);
|
||||
}
|
||||
|
||||
|
||||
int uv__udp_is_connected(uv_udp_t* handle) {
|
||||
struct sockaddr_storage addr;
|
||||
int addrlen;
|
||||
if (handle->type != UV_UDP)
|
||||
return 0;
|
||||
|
||||
addrlen = sizeof(addr);
|
||||
if (uv_udp_getpeername(handle, (struct sockaddr*) &addr, &addrlen) != 0)
|
||||
return 0;
|
||||
|
||||
return addrlen > 0;
|
||||
}
|
||||
|
||||
|
||||
int uv__udp_check_before_send(uv_udp_t* handle, const struct sockaddr* addr) {
|
||||
unsigned int addrlen;
|
||||
|
||||
if (handle->type != UV_UDP)
|
||||
return UV_EINVAL;
|
||||
|
||||
if (addr != NULL && (handle->flags & UV_HANDLE_UDP_CONNECTED))
|
||||
return UV_EISCONN;
|
||||
|
||||
if (addr == NULL && !(handle->flags & UV_HANDLE_UDP_CONNECTED))
|
||||
return UV_EDESTADDRREQ;
|
||||
|
||||
if (addr != NULL) {
|
||||
if (addr->sa_family == AF_INET)
|
||||
addrlen = sizeof(struct sockaddr_in);
|
||||
else if (addr->sa_family == AF_INET6)
|
||||
addrlen = sizeof(struct sockaddr_in6);
|
||||
else
|
||||
return UV_EINVAL;
|
||||
} else {
|
||||
addrlen = 0;
|
||||
}
|
||||
|
||||
return addrlen;
|
||||
}
|
||||
|
||||
|
||||
int uv_udp_send(uv_udp_send_t* req,
|
||||
uv_udp_t* handle,
|
||||
const uv_buf_t bufs[],
|
||||
unsigned int nbufs,
|
||||
const struct sockaddr* addr,
|
||||
uv_udp_send_cb send_cb) {
|
||||
int addrlen;
|
||||
|
||||
addrlen = uv__udp_check_before_send(handle, addr);
|
||||
if (addrlen < 0)
|
||||
return addrlen;
|
||||
|
||||
return uv__udp_send(req, handle, bufs, nbufs, addr, addrlen, send_cb);
|
||||
}
|
||||
|
||||
@ -343,17 +406,11 @@ int uv_udp_try_send(uv_udp_t* handle,
|
||||
const uv_buf_t bufs[],
|
||||
unsigned int nbufs,
|
||||
const struct sockaddr* addr) {
|
||||
unsigned int addrlen;
|
||||
int addrlen;
|
||||
|
||||
if (handle->type != UV_UDP)
|
||||
return UV_EINVAL;
|
||||
|
||||
if (addr->sa_family == AF_INET)
|
||||
addrlen = sizeof(struct sockaddr_in);
|
||||
else if (addr->sa_family == AF_INET6)
|
||||
addrlen = sizeof(struct sockaddr_in6);
|
||||
else
|
||||
return UV_EINVAL;
|
||||
addrlen = uv__udp_check_before_send(handle, addr);
|
||||
if (addrlen < 0)
|
||||
return addrlen;
|
||||
|
||||
return uv__udp_try_send(handle, bufs, nbufs, addr, addrlen);
|
||||
}
|
||||
|
@ -103,6 +103,7 @@ enum {
|
||||
|
||||
/* Only used by uv_udp_t handles. */
|
||||
UV_HANDLE_UDP_PROCESSING = 0x01000000,
|
||||
UV_HANDLE_UDP_CONNECTED = 0x02000000,
|
||||
|
||||
/* Only used by uv_pipe_t handles. */
|
||||
UV_HANDLE_NON_OVERLAPPED_PIPE = 0x01000000,
|
||||
@ -142,6 +143,14 @@ int uv__udp_bind(uv_udp_t* handle,
|
||||
unsigned int addrlen,
|
||||
unsigned int flags);
|
||||
|
||||
int uv__udp_connect(uv_udp_t* handle,
|
||||
const struct sockaddr* addr,
|
||||
unsigned int addrlen);
|
||||
|
||||
int uv__udp_disconnect(uv_udp_t* handle);
|
||||
|
||||
int uv__udp_is_connected(uv_udp_t* handle);
|
||||
|
||||
int uv__udp_send(uv_udp_send_t* req,
|
||||
uv_udp_t* handle,
|
||||
const uv_buf_t bufs[],
|
||||
|
@ -36,6 +36,17 @@ const unsigned int uv_active_udp_streams_threshold = 0;
|
||||
|
||||
/* A zero-size buffer for use by uv_udp_read */
|
||||
static char uv_zero_[] = "";
|
||||
int uv_udp_getpeername(const uv_udp_t* handle,
|
||||
struct sockaddr* name,
|
||||
int* namelen) {
|
||||
|
||||
return uv__getsockpeername((const uv_handle_t*) handle,
|
||||
getpeername,
|
||||
name,
|
||||
namelen,
|
||||
0);
|
||||
}
|
||||
|
||||
|
||||
int uv_udp_getsockname(const uv_udp_t* handle,
|
||||
struct sockaddr* name,
|
||||
@ -815,6 +826,9 @@ int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) {
|
||||
if (uv__udp_is_bound(handle))
|
||||
handle->flags |= UV_HANDLE_BOUND;
|
||||
|
||||
if (uv__udp_is_connected(handle))
|
||||
handle->flags |= UV_HANDLE_UDP_CONNECTED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -892,6 +906,50 @@ int uv__udp_bind(uv_udp_t* handle,
|
||||
}
|
||||
|
||||
|
||||
int uv__udp_connect(uv_udp_t* handle,
|
||||
const struct sockaddr* addr,
|
||||
unsigned int addrlen) {
|
||||
const struct sockaddr* bind_addr;
|
||||
int err;
|
||||
|
||||
if (!(handle->flags & UV_HANDLE_BOUND)) {
|
||||
if (addrlen == sizeof(uv_addr_ip4_any_))
|
||||
bind_addr = (const struct sockaddr*) &uv_addr_ip4_any_;
|
||||
else if (addrlen == sizeof(uv_addr_ip6_any_))
|
||||
bind_addr = (const struct sockaddr*) &uv_addr_ip6_any_;
|
||||
else
|
||||
return UV_EINVAL;
|
||||
|
||||
err = uv_udp_maybe_bind(handle, bind_addr, addrlen, 0);
|
||||
if (err)
|
||||
return uv_translate_sys_error(err);
|
||||
}
|
||||
|
||||
err = connect(handle->socket, addr, addrlen);
|
||||
if (err)
|
||||
return uv_translate_sys_error(err);
|
||||
|
||||
handle->flags |= UV_HANDLE_UDP_CONNECTED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv__udp_disconnect(uv_udp_t* handle) {
|
||||
int err;
|
||||
struct sockaddr addr;
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
|
||||
err = connect(handle->socket, &addr, sizeof(addr));
|
||||
if (err)
|
||||
return uv_translate_sys_error(err);
|
||||
|
||||
handle->flags &= ~UV_HANDLE_UDP_CONNECTED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* This function is an egress point, i.e. it returns libuv errors rather than
|
||||
* system errors.
|
||||
*/
|
||||
@ -912,6 +970,7 @@ int uv__udp_send(uv_udp_send_t* req,
|
||||
bind_addr = (const struct sockaddr*) &uv_addr_ip6_any_;
|
||||
else
|
||||
return UV_EINVAL;
|
||||
|
||||
err = uv_udp_maybe_bind(handle, bind_addr, addrlen, 0);
|
||||
if (err)
|
||||
return uv_translate_sys_error(err);
|
||||
@ -937,9 +996,11 @@ int uv__udp_try_send(uv_udp_t* handle,
|
||||
|
||||
assert(nbufs > 0);
|
||||
|
||||
err = uv__convert_to_localhost_if_unspecified(addr, &converted);
|
||||
if (err)
|
||||
return err;
|
||||
if (addr != NULL) {
|
||||
err = uv__convert_to_localhost_if_unspecified(addr, &converted);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Already sending a message.*/
|
||||
if (handle->send_queue_count != 0)
|
||||
|
@ -132,6 +132,7 @@ TEST_DECLARE (tcp_write_ready)
|
||||
TEST_DECLARE (udp_alloc_cb_fail)
|
||||
TEST_DECLARE (udp_bind)
|
||||
TEST_DECLARE (udp_bind_reuseaddr)
|
||||
TEST_DECLARE (udp_connect)
|
||||
TEST_DECLARE (udp_create_early)
|
||||
TEST_DECLARE (udp_create_early_bad_bind)
|
||||
TEST_DECLARE (udp_create_early_bad_domain)
|
||||
@ -153,6 +154,7 @@ TEST_DECLARE (udp_no_autobind)
|
||||
TEST_DECLARE (udp_open)
|
||||
TEST_DECLARE (udp_open_twice)
|
||||
TEST_DECLARE (udp_open_bound)
|
||||
TEST_DECLARE (udp_open_connect)
|
||||
TEST_DECLARE (udp_try_send)
|
||||
TEST_DECLARE (pipe_bind_error_addrinuse)
|
||||
TEST_DECLARE (pipe_bind_error_addrnotavail)
|
||||
@ -623,6 +625,7 @@ TASK_LIST_START
|
||||
TEST_ENTRY (udp_alloc_cb_fail)
|
||||
TEST_ENTRY (udp_bind)
|
||||
TEST_ENTRY (udp_bind_reuseaddr)
|
||||
TEST_ENTRY (udp_connect)
|
||||
TEST_ENTRY (udp_create_early)
|
||||
TEST_ENTRY (udp_create_early_bad_bind)
|
||||
TEST_ENTRY (udp_create_early_bad_domain)
|
||||
@ -647,6 +650,8 @@ TASK_LIST_START
|
||||
TEST_HELPER (udp_open, udp4_echo_server)
|
||||
TEST_ENTRY (udp_open_twice)
|
||||
TEST_ENTRY (udp_open_bound)
|
||||
TEST_ENTRY (udp_open_connect)
|
||||
TEST_HELPER (udp_open_connect, udp4_echo_server)
|
||||
|
||||
TEST_ENTRY (pipe_bind_error_addrinuse)
|
||||
TEST_ENTRY (pipe_bind_error_addrnotavail)
|
||||
|
182
test/test-udp-connect.c
Normal file
182
test/test-udp-connect.c
Normal file
@ -0,0 +1,182 @@
|
||||
/* Copyright libuv project and 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>
|
||||
|
||||
#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 uv_buf_t buf;
|
||||
static struct sockaddr_in lo_addr;
|
||||
|
||||
static int cl_send_cb_called;
|
||||
static int sv_recv_cb_called;
|
||||
|
||||
static int close_cb_called;
|
||||
|
||||
|
||||
static void alloc_cb(uv_handle_t* handle,
|
||||
size_t suggested_size,
|
||||
uv_buf_t* buf) {
|
||||
static char slab[65536];
|
||||
CHECK_HANDLE(handle);
|
||||
ASSERT(suggested_size <= sizeof(slab));
|
||||
buf->base = slab;
|
||||
buf->len = sizeof(slab);
|
||||
}
|
||||
|
||||
|
||||
static void close_cb(uv_handle_t* handle) {
|
||||
CHECK_HANDLE(handle);
|
||||
ASSERT(uv_is_closing(handle));
|
||||
close_cb_called++;
|
||||
}
|
||||
|
||||
|
||||
static void cl_send_cb(uv_udp_send_t* req, int status) {
|
||||
int r;
|
||||
|
||||
ASSERT(req != NULL);
|
||||
ASSERT(status == 0);
|
||||
CHECK_HANDLE(req->handle);
|
||||
if (++cl_send_cb_called == 1) {
|
||||
uv_udp_connect(&client, NULL);
|
||||
r = uv_udp_send(req, &client, &buf, 1, NULL, cl_send_cb);
|
||||
ASSERT(r == UV_EDESTADDRREQ);
|
||||
r = uv_udp_send(req,
|
||||
&client,
|
||||
&buf,
|
||||
1,
|
||||
(const struct sockaddr*) &lo_addr,
|
||||
cl_send_cb);
|
||||
ASSERT(r == 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void sv_recv_cb(uv_udp_t* handle,
|
||||
ssize_t nread,
|
||||
const uv_buf_t* rcvbuf,
|
||||
const struct sockaddr* addr,
|
||||
unsigned flags) {
|
||||
if (nread > 0) {
|
||||
ASSERT(nread == 4);
|
||||
ASSERT(addr != NULL);
|
||||
ASSERT(memcmp("EXIT", rcvbuf->base, nread) == 0);
|
||||
if (++sv_recv_cb_called == 4) {
|
||||
uv_close((uv_handle_t*) &server, close_cb);
|
||||
uv_close((uv_handle_t*) &client, close_cb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_IMPL(udp_connect) {
|
||||
uv_udp_send_t req;
|
||||
struct sockaddr_in ext_addr;
|
||||
struct sockaddr_in tmp_addr;
|
||||
int r;
|
||||
int addrlen;
|
||||
|
||||
ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &lo_addr));
|
||||
|
||||
r = uv_udp_init(uv_default_loop(), &server);
|
||||
ASSERT(r == 0);
|
||||
|
||||
r = uv_udp_bind(&server, (const struct sockaddr*) &lo_addr, 0);
|
||||
ASSERT(r == 0);
|
||||
|
||||
r = uv_udp_recv_start(&server, alloc_cb, sv_recv_cb);
|
||||
ASSERT(r == 0);
|
||||
|
||||
r = uv_udp_init(uv_default_loop(), &client);
|
||||
ASSERT(r == 0);
|
||||
|
||||
buf = uv_buf_init("EXIT", 4);
|
||||
|
||||
ASSERT(0 == uv_ip4_addr("8.8.8.8", TEST_PORT, &ext_addr));
|
||||
ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &lo_addr));
|
||||
|
||||
r = uv_udp_connect(&client, (const struct sockaddr*) &lo_addr);
|
||||
ASSERT(r == 0);
|
||||
r = uv_udp_connect(&client, (const struct sockaddr*) &ext_addr);
|
||||
ASSERT(r == UV_EISCONN);
|
||||
|
||||
addrlen = sizeof(tmp_addr);
|
||||
r = uv_udp_getpeername(&client, (struct sockaddr*) &tmp_addr, &addrlen);
|
||||
ASSERT(r == 0);
|
||||
|
||||
/* To send messages in connected UDP sockets addr must be NULL */
|
||||
r = uv_udp_try_send(&client, &buf, 1, (const struct sockaddr*) &lo_addr);
|
||||
ASSERT(r == UV_EISCONN);
|
||||
r = uv_udp_try_send(&client, &buf, 1, NULL);
|
||||
ASSERT(r == 4);
|
||||
r = uv_udp_try_send(&client, &buf, 1, (const struct sockaddr*) &ext_addr);
|
||||
ASSERT(r == UV_EISCONN);
|
||||
|
||||
r = uv_udp_connect(&client, NULL);
|
||||
ASSERT(r == 0);
|
||||
r = uv_udp_connect(&client, NULL);
|
||||
ASSERT(r == UV_ENOTCONN);
|
||||
|
||||
addrlen = sizeof(tmp_addr);
|
||||
r = uv_udp_getpeername(&client, (struct sockaddr*) &tmp_addr, &addrlen);
|
||||
ASSERT(r == UV_ENOTCONN);
|
||||
|
||||
/* To send messages in disconnected UDP sockets addr must be set */
|
||||
r = uv_udp_try_send(&client, &buf, 1, (const struct sockaddr*) &lo_addr);
|
||||
ASSERT(r == 4);
|
||||
r = uv_udp_try_send(&client, &buf, 1, NULL);
|
||||
ASSERT(r == UV_EDESTADDRREQ);
|
||||
|
||||
|
||||
r = uv_udp_connect(&client, (const struct sockaddr*) &lo_addr);
|
||||
ASSERT(r == 0);
|
||||
r = uv_udp_send(&req,
|
||||
&client,
|
||||
&buf,
|
||||
1,
|
||||
(const struct sockaddr*) &lo_addr,
|
||||
cl_send_cb);
|
||||
ASSERT(r == UV_EISCONN);
|
||||
r = uv_udp_send(&req, &client, &buf, 1, NULL, cl_send_cb);
|
||||
ASSERT(r == 0);
|
||||
|
||||
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
|
||||
|
||||
ASSERT(close_cb_called == 2);
|
||||
ASSERT(sv_recv_cb_called == 4);
|
||||
ASSERT(cl_send_cb_called == 2);
|
||||
|
||||
ASSERT(client.send_queue_size == 0);
|
||||
ASSERT(server.send_queue_size == 0);
|
||||
|
||||
MAKE_VALGRIND_HAPPY();
|
||||
return 0;
|
||||
}
|
@ -129,6 +129,7 @@ static void send_cb(uv_udp_send_t* req, int status) {
|
||||
ASSERT(status == 0);
|
||||
|
||||
send_cb_called++;
|
||||
uv_close((uv_handle_t*)req->handle, close_cb);
|
||||
}
|
||||
|
||||
|
||||
@ -245,3 +246,53 @@ TEST_IMPL(udp_open_bound) {
|
||||
MAKE_VALGRIND_HAPPY();
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST_IMPL(udp_open_connect) {
|
||||
struct sockaddr_in addr;
|
||||
uv_buf_t buf = uv_buf_init("PING", 4);
|
||||
uv_udp_t client;
|
||||
uv_udp_t server;
|
||||
uv_os_sock_t sock;
|
||||
int r;
|
||||
|
||||
ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr));
|
||||
|
||||
startup();
|
||||
sock = create_udp_socket();
|
||||
|
||||
r = uv_udp_init(uv_default_loop(), &client);
|
||||
ASSERT(r == 0);
|
||||
|
||||
r = connect(sock, (const struct sockaddr*) &addr, sizeof(addr));
|
||||
ASSERT(r == 0);
|
||||
|
||||
r = uv_udp_open(&client, sock);
|
||||
ASSERT(r == 0);
|
||||
|
||||
r = uv_udp_init(uv_default_loop(), &server);
|
||||
ASSERT(r == 0);
|
||||
|
||||
r = uv_udp_bind(&server, (const struct sockaddr*) &addr, 0);
|
||||
ASSERT(r == 0);
|
||||
|
||||
r = uv_udp_recv_start(&server, alloc_cb, recv_cb);
|
||||
ASSERT(r == 0);
|
||||
|
||||
r = uv_udp_send(&send_req,
|
||||
&client,
|
||||
&buf,
|
||||
1,
|
||||
NULL,
|
||||
send_cb);
|
||||
ASSERT(r == 0);
|
||||
|
||||
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
|
||||
|
||||
ASSERT(send_cb_called == 1);
|
||||
ASSERT(close_cb_called == 2);
|
||||
|
||||
ASSERT(client.send_queue_size == 0);
|
||||
|
||||
MAKE_VALGRIND_HAPPY();
|
||||
return 0;
|
||||
}
|
||||
|
@ -137,6 +137,7 @@
|
||||
'test-tty.c',
|
||||
'test-udp-alloc-cb-fail.c',
|
||||
'test-udp-bind.c',
|
||||
'test-udp-connect.c',
|
||||
'test-udp-create-socket-early.c',
|
||||
'test-udp-dgram-too-big.c',
|
||||
'test-udp-ipv6.c',
|
||||
|
Loading…
x
Reference in New Issue
Block a user