From 3487453788bbe54b7d3c534fdd7f97d6be91b2d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Duval?= Date: Sun, 6 Aug 2023 20:21:49 +0200 Subject: [PATCH] unix: respect MSG_DONTWAIT on sendmsg() fix bug #18539 Change-Id: Id21362028287d1cbdac469226e6b52f4547a276f Reviewed-on: https://review.haiku-os.org/c/haiku/+/6796 Tested-by: Commit checker robot Reviewed-by: Adrien Destugues --- headers/private/net/net_protocol.h | 3 +- .../protocols/unix/UnixDatagramEndpoint.cpp | 16 ++++--- .../protocols/unix/UnixDatagramEndpoint.h | 2 +- .../network/protocols/unix/UnixEndpoint.h | 2 +- .../protocols/unix/UnixStreamEndpoint.cpp | 15 +++--- .../protocols/unix/UnixStreamEndpoint.h | 2 +- .../kernel/network/protocols/unix/unix.cpp | 4 +- .../kernel/network/stack/net_socket.cpp | 2 +- src/tests/system/network/Jamfile | 2 + src/tests/system/network/unix_send_test.c | 46 +++++++++++++++++++ 10 files changed, 74 insertions(+), 20 deletions(-) create mode 100644 src/tests/system/network/unix_send_test.c diff --git a/headers/private/net/net_protocol.h b/headers/private/net/net_protocol.h index 352b8a5798..ef4745d95a 100644 --- a/headers/private/net/net_protocol.h +++ b/headers/private/net/net_protocol.h @@ -108,7 +108,8 @@ struct net_protocol_module_info { ssize_t (*send_data_no_buffer)(net_protocol* self, const iovec* vecs, size_t vecCount, ancillary_data_container* ancillaryData, - const struct sockaddr* address, socklen_t addressLength); + const struct sockaddr* address, socklen_t addressLength, + int flags); ssize_t (*read_data_no_buffer)(net_protocol* self, const iovec* vecs, size_t vecCount, ancillary_data_container** _ancillaryData, struct sockaddr* _address, socklen_t* _addressLength); diff --git a/src/add-ons/kernel/network/protocols/unix/UnixDatagramEndpoint.cpp b/src/add-ons/kernel/network/protocols/unix/UnixDatagramEndpoint.cpp index 8a0fb61e1e..0af3bfb7ec 100644 --- a/src/add-ons/kernel/network/protocols/unix/UnixDatagramEndpoint.cpp +++ b/src/add-ons/kernel/network/protocols/unix/UnixDatagramEndpoint.cpp @@ -210,17 +210,19 @@ UnixDatagramEndpoint::Accept(net_socket** _acceptedSocket) ssize_t UnixDatagramEndpoint::Send(const iovec* vecs, size_t vecCount, ancillary_data_container* ancillaryData, const struct sockaddr* address, - socklen_t addressLength) + socklen_t addressLength, int flags) { TRACE("[%" B_PRId32 "] %p->UnixDatagramEndpoint::Send()\n", find_thread(NULL), this); - bigtime_t timeout = absolute_timeout(socket->send.timeout); - if (gStackModule->is_restarted_syscall()) - timeout = gStackModule->restore_syscall_restart_timeout(); - else - gStackModule->store_syscall_restart_timeout(timeout); - + bigtime_t timeout = 0; + if ((flags & MSG_DONTWAIT) == 0) { + timeout = absolute_timeout(socket->send.timeout); + if (gStackModule->is_restarted_syscall()) + timeout = gStackModule->restore_syscall_restart_timeout(); + else + gStackModule->store_syscall_restart_timeout(timeout); + } UnixDatagramEndpointLocker endpointLocker(this); if (fShutdownWrite) diff --git a/src/add-ons/kernel/network/protocols/unix/UnixDatagramEndpoint.h b/src/add-ons/kernel/network/protocols/unix/UnixDatagramEndpoint.h index 1b142c864f..7353ef4581 100644 --- a/src/add-ons/kernel/network/protocols/unix/UnixDatagramEndpoint.h +++ b/src/add-ons/kernel/network/protocols/unix/UnixDatagramEndpoint.h @@ -35,7 +35,7 @@ public: ssize_t Send(const iovec* vecs, size_t vecCount, ancillary_data_container* ancillaryData, const struct sockaddr* address, - socklen_t addressLength) override; + socklen_t addressLength, int flags) override; ssize_t Receive(const iovec* vecs, size_t vecCount, ancillary_data_container** _ancillaryData, struct sockaddr* _address, diff --git a/src/add-ons/kernel/network/protocols/unix/UnixEndpoint.h b/src/add-ons/kernel/network/protocols/unix/UnixEndpoint.h index be816fc71e..143589bdde 100644 --- a/src/add-ons/kernel/network/protocols/unix/UnixEndpoint.h +++ b/src/add-ons/kernel/network/protocols/unix/UnixEndpoint.h @@ -56,7 +56,7 @@ public: virtual ssize_t Send(const iovec* vecs, size_t vecCount, ancillary_data_container* ancillaryData, const struct sockaddr* address, - socklen_t addressLength) = 0; + socklen_t addressLength, int flags) = 0; virtual ssize_t Receive(const iovec* vecs, size_t vecCount, ancillary_data_container** _ancillaryData, struct sockaddr* _address, socklen_t* _addressLength) = 0; diff --git a/src/add-ons/kernel/network/protocols/unix/UnixStreamEndpoint.cpp b/src/add-ons/kernel/network/protocols/unix/UnixStreamEndpoint.cpp index e327456cf5..478b025d24 100644 --- a/src/add-ons/kernel/network/protocols/unix/UnixStreamEndpoint.cpp +++ b/src/add-ons/kernel/network/protocols/unix/UnixStreamEndpoint.cpp @@ -374,16 +374,19 @@ UnixStreamEndpoint::Accept(net_socket** _acceptedSocket) ssize_t UnixStreamEndpoint::Send(const iovec* vecs, size_t vecCount, ancillary_data_container* ancillaryData, - const struct sockaddr* address, socklen_t addressLength) + const struct sockaddr* address, socklen_t addressLength, int flags) { TRACE("[%" B_PRId32 "] %p->UnixStreamEndpoint::Send(%p, %ld, %p)\n", find_thread(NULL), this, vecs, vecCount, ancillaryData); - bigtime_t timeout = absolute_timeout(socket->send.timeout); - if (gStackModule->is_restarted_syscall()) - timeout = gStackModule->restore_syscall_restart_timeout(); - else - gStackModule->store_syscall_restart_timeout(timeout); + bigtime_t timeout = 0; + if ((flags & MSG_DONTWAIT) == 0) { + timeout = absolute_timeout(socket->send.timeout); + if (gStackModule->is_restarted_syscall()) + timeout = gStackModule->restore_syscall_restart_timeout(); + else + gStackModule->store_syscall_restart_timeout(timeout); + } UnixStreamEndpointLocker locker(this); diff --git a/src/add-ons/kernel/network/protocols/unix/UnixStreamEndpoint.h b/src/add-ons/kernel/network/protocols/unix/UnixStreamEndpoint.h index 03a4421f7e..fd773adecb 100644 --- a/src/add-ons/kernel/network/protocols/unix/UnixStreamEndpoint.h +++ b/src/add-ons/kernel/network/protocols/unix/UnixStreamEndpoint.h @@ -51,7 +51,7 @@ public: ssize_t Send(const iovec* vecs, size_t vecCount, ancillary_data_container* ancillaryData, const struct sockaddr* address, - socklen_t addressLength) override; + socklen_t addressLength, int flags) override; ssize_t Receive(const iovec* vecs, size_t vecCount, ancillary_data_container** _ancillaryData, struct sockaddr* _address, diff --git a/src/add-ons/kernel/network/protocols/unix/unix.cpp b/src/add-ons/kernel/network/protocols/unix/unix.cpp index 83fd98383b..45ff2dd2de 100644 --- a/src/add-ons/kernel/network/protocols/unix/unix.cpp +++ b/src/add-ons/kernel/network/protocols/unix/unix.cpp @@ -408,10 +408,10 @@ unix_process_ancillary_data(net_protocol *self, ssize_t unix_send_data_no_buffer(net_protocol *_protocol, const iovec *vecs, size_t vecCount, ancillary_data_container *ancillaryData, - const struct sockaddr *address, socklen_t addressLength) + const struct sockaddr *address, socklen_t addressLength, int flags) { return ((UnixEndpoint*)_protocol)->Send(vecs, vecCount, ancillaryData, - address, addressLength); + address, addressLength, flags); } diff --git a/src/add-ons/kernel/network/stack/net_socket.cpp b/src/add-ons/kernel/network/stack/net_socket.cpp index 941e7489b2..c15f51d292 100644 --- a/src/add-ons/kernel/network/stack/net_socket.cpp +++ b/src/add-ons/kernel/network/stack/net_socket.cpp @@ -1393,7 +1393,7 @@ socket_send(net_socket* socket, msghdr* header, const void* data, size_t length, ssize_t written = socket->first_info->send_data_no_buffer( socket->first_protocol, vecs, vecCount, ancillaryData, address, - addressLength); + addressLength, flags); if (written > 0) ancillaryDataDeleter.Detach(); return written; diff --git a/src/tests/system/network/Jamfile b/src/tests/system/network/Jamfile index 04693d6545..fb9e07c487 100644 --- a/src/tests/system/network/Jamfile +++ b/src/tests/system/network/Jamfile @@ -19,6 +19,8 @@ SimpleTest if_nameindex : if_nameindex.c : $(TARGET_NETWORK_LIBS) ; SimpleTest unix_dgram_test : unix_dgram_test.cpp : $(TARGET_NETWORK_LIBS) ; +SimpleTest unix_send_test : unix_send_test.c : $(TARGET_NETWORK_LIBS) ; + SimpleTest tcp_connection_test : tcp_connection_test.cpp : $(TARGET_NETWORK_LIBS) ; diff --git a/src/tests/system/network/unix_send_test.c b/src/tests/system/network/unix_send_test.c new file mode 100644 index 0000000000..90cb4e62e4 --- /dev/null +++ b/src/tests/system/network/unix_send_test.c @@ -0,0 +1,46 @@ +#include +#include +#include +#include +#include + +int main() { + int fds[2]; + int domain; + domain = AF_UNIX; + // domain = AF_INET; + printf("Domain: %i\n", domain); + int ret = socketpair(domain, SOCK_DGRAM, 0, fds); // try also: SOCK_STREAM + if(ret) { + perror("Could not get socketpair"); + return 1; + } + + struct timeval v = { + .tv_sec = 1, + .tv_usec = 0 + }; + + // uncomment to allow send to timeout with ETIMEDOUT after 1 second + //ret = setsockopt(fds[0], SOL_SOCKET, SO_SNDTIMEO, &v, sizeof(v)); + if(ret) { + perror("setsockopt"); + } + + size_t bufLen = 1024; + char *buf = calloc(bufLen, 1); + int ok = 0; + while(true) { + printf("send %i\n", ok); + ret = send(fds[0], &buf[0], bufLen, MSG_DONTWAIT); + // eventually: EAGAIN (on Linux and Haiku), ENOBUFS (on macOS) + if(ret < 0) { + perror("send"); + break; + } else { + ok++; + } + } + + return 0; +}