diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-26 16:08:04 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-26 16:08:04 +0000 |
commit | 41927c28dd5030318be5cf71f515449d55b2802b (patch) | |
tree | 41f7cef3a417fee1ee171cdb4f2709f4372ae4f3 /libc-bottom-half/sources/send.c | |
parent | Releasing progress-linux version 0.0~git20230821.ec4566b-2~progress7.99u1. (diff) | |
download | wasi-libc-41927c28dd5030318be5cf71f515449d55b2802b.tar.xz wasi-libc-41927c28dd5030318be5cf71f515449d55b2802b.zip |
Merging upstream version 0.0~git20240411.9e8c542.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'libc-bottom-half/sources/send.c')
-rw-r--r-- | libc-bottom-half/sources/send.c | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/libc-bottom-half/sources/send.c b/libc-bottom-half/sources/send.c new file mode 100644 index 0000000..42f653b --- /dev/null +++ b/libc-bottom-half/sources/send.c @@ -0,0 +1,249 @@ +#include <sys/socket.h> + +#include <errno.h> + +#include <wasi/api.h> +#include <wasi/descriptor_table.h> +#include <wasi/sockets_utils.h> + +static ssize_t tcp_sendto(tcp_socket_t *socket, const uint8_t *buffer, + size_t length, int flags, const struct sockaddr *addr, + socklen_t addrlen) +{ + const int supported_flags = MSG_DONTWAIT | MSG_NOSIGNAL; + if ((flags & supported_flags) != flags) { + errno = EOPNOTSUPP; + return -1; + } + + if (addr != NULL || addrlen != 0) { + errno = EISCONN; + return -1; + } + + tcp_socket_state_connected_t connection; + if (socket->state.tag == TCP_SOCKET_STATE_CONNECTED) { + connection = socket->state.connected; + } else { + errno = ENOTCONN; + return -1; + } + + bool should_block = socket->blocking; + if ((flags & MSG_DONTWAIT) != 0) { + should_block = false; + } + + if ((flags & MSG_NOSIGNAL) != 0) { + // Ignore it. WASI has no Unix-style signals. So effectively, + // MSG_NOSIGNAL is always the case, whether it was explicitly + // requested or not. + } + + streams_borrow_output_stream_t tx_borrow = + streams_borrow_output_stream(connection.output); + while (true) { + streams_stream_error_t error; + uint64_t count; + if (!streams_method_output_stream_check_write(tx_borrow, &count, + &error)) { + // TODO wasi-sockets: wasi-sockets has no way to recover stream errors yet. + errno = EPIPE; + return -1; + } + + if (count) { + count = count < length ? count : length; + wasip2_list_u8_t list = { .ptr = (uint8_t *)buffer, + .len = count }; + if (!streams_method_output_stream_write( + tx_borrow, &list, &error)) { + // TODO wasi-sockets: wasi-sockets has no way to recover TCP stream errors yet. + errno = EPIPE; + return -1; + } else { + return count; + } + } else if (should_block) { + poll_borrow_pollable_t pollable_borrow = + poll_borrow_pollable( + connection.output_pollable); + poll_method_pollable_block(pollable_borrow); + } else { + errno = EWOULDBLOCK; + return -1; + } + } +} + +static ssize_t udp_sendto(udp_socket_t *socket, const uint8_t *buffer, + size_t length, int flags, const struct sockaddr *addr, + socklen_t addrlen) +{ + const int supported_flags = MSG_DONTWAIT; + if ((flags & supported_flags) != flags) { + errno = EOPNOTSUPP; + return -1; + } + + network_ip_socket_address_t remote_address; + bool has_remote_address = (addr != NULL); + + if (has_remote_address) { + if (socket->state.tag == UDP_SOCKET_STATE_CONNECTED) { + errno = EISCONN; + return -1; + } + + int parse_err; + if (!__wasi_sockets_utils__parse_address( + socket->family, addr, addrlen, &remote_address, + &parse_err)) { + errno = parse_err; + return -1; + } + } else { + if (addrlen != 0) { + errno = EINVAL; + return -1; + } + + if (socket->state.tag != UDP_SOCKET_STATE_CONNECTED) { + errno = EDESTADDRREQ; + return -1; + } + } + + network_error_code_t error; + udp_borrow_udp_socket_t socket_borrow = + udp_borrow_udp_socket(socket->socket); + + udp_socket_streams_t streams; + switch (socket->state.tag) { + case UDP_SOCKET_STATE_UNBOUND: { + // Socket is not explicitly bound by the user. We'll do it for them: + + network_ip_socket_address_t any = + __wasi_sockets_utils__any_addr(socket->family); + int result = __wasi_sockets_utils__udp_bind(socket, &any); + if (result != 0) { + return result; + } + + if (!__wasi_sockets_utils__stream(socket, NULL, &streams, + &error)) { + errno = __wasi_sockets_utils__map_error(error); + return -1; + } + break; + } + case UDP_SOCKET_STATE_BOUND_NOSTREAMS: { + if (!__wasi_sockets_utils__stream(socket, NULL, &streams, + &error)) { + errno = __wasi_sockets_utils__map_error(error); + return -1; + } + break; + } + case UDP_SOCKET_STATE_BOUND_STREAMING: + streams = socket->state.bound_streaming.streams; + break; + + case UDP_SOCKET_STATE_CONNECTED: + streams = socket->state.connected.streams; + break; + + default: /* unreachable */ + abort(); + } + + bool should_block = socket->blocking; + if ((flags & MSG_DONTWAIT) != 0) { + should_block = false; + } + + udp_outgoing_datagram_t datagrams[1] = {{ + .remote_address = { + .is_some = has_remote_address, + .val = remote_address, + }, + .data = { + .len = length, + .ptr = (uint8_t*)buffer, + }, + }}; + udp_list_outgoing_datagram_t list = { + .len = 1, + .ptr = datagrams, + }; + + udp_borrow_outgoing_datagram_stream_t outgoing_borrow = + udp_borrow_outgoing_datagram_stream(streams.outgoing); + while (true) { + uint64_t allowed; + if (!udp_method_outgoing_datagram_stream_check_send( + outgoing_borrow, &allowed, &error)) { + errno = __wasi_sockets_utils__map_error(error); + return -1; + } + + if (allowed) { + uint64_t datagrams_sent; + if (!udp_method_outgoing_datagram_stream_send( + outgoing_borrow, &list, &datagrams_sent, + &error)) { + errno = __wasi_sockets_utils__map_error(error); + return -1; + } + + if (datagrams_sent != 0 && datagrams_sent != 1) { + abort(); + } + + if (datagrams_sent == 1) { + return length; + } + } + + if (should_block) { + poll_borrow_pollable_t pollable_borrow = + poll_borrow_pollable(streams.outgoing_pollable); + poll_method_pollable_block(pollable_borrow); + } else { + errno = EWOULDBLOCK; + return -1; + } + } +} + +ssize_t send(int socket, const void *buffer, size_t length, int flags) +{ + return sendto(socket, buffer, length, flags, NULL, 0); +} + +ssize_t sendto(int socket, const void *buffer, size_t length, int flags, + const struct sockaddr *addr, socklen_t addrlen) +{ + descriptor_table_entry_t *entry; + if (!descriptor_table_get_ref(socket, &entry)) { + errno = EBADF; + return -1; + } + + if (buffer == NULL) { + errno = EINVAL; + return -1; + } + + switch (entry->tag) { + case DESCRIPTOR_TABLE_ENTRY_TCP_SOCKET: + return tcp_sendto(&entry->tcp_socket, buffer, length, flags, + addr, addrlen); + case DESCRIPTOR_TABLE_ENTRY_UDP_SOCKET: + return udp_sendto(&entry->udp_socket, buffer, length, flags, + addr, addrlen); + default: + errno = EOPNOTSUPP; + return -1; + } +} |