Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
226 changes: 223 additions & 3 deletions libc-bottom-half/sources/wasip3_tcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,232 @@
#include <wasi/api.h>

#ifdef __wasip3__
#include <netinet/tcp.h>
#include <stdlib.h>
#include <wasi/descriptor_table.h>
#include <wasi/file_utils.h>
#include <wasi/sockets_utils.h>
#include <wasi/wasip3_block.h>

typedef struct {
sockets_own_tcp_socket_t socket;
sockets_ip_address_family_t family;
bool blocking;
sockets_stream_own_tcp_socket_t incoming;

sockets_tuple2_stream_u8_future_result_void_error_code_t receive;
wasip3_write_t send;
} tcp_socket_t;

static int tcp_connect(void *data, const struct sockaddr *addr,
socklen_t addrlen) {
tcp_socket_t *socket = (tcp_socket_t *)data;
sockets_method_tcp_socket_connect_args_t args;
// sockets_ip_socket_address_t remote_address;
int parse_err;
if (!__wasi_sockets_utils__parse_address(socket->family, addr, addrlen,
&args.remote_address, &parse_err)) {
errno = parse_err;
return -1;
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Around here, could this return -1 + ENOTSUPP if blocking is set to false? I think that'll need special handling here to ensure that case works.

args.self = sockets_borrow_tcp_socket(socket->socket);
sockets_result_void_error_code_t error;

wasip3_subtask_status_t status =
sockets_method_tcp_socket_connect(&args, &error);
wasip3_subtask_block_on(status);

if (error.is_err) {
errno = __wasi_sockets_utils__map_error(error.val.err);
return -1;
}

return 0;
}

static int tcp_do_bind(tcp_socket_t *socket,
sockets_ip_socket_address_t *address) {
sockets_error_code_t error;
sockets_borrow_tcp_socket_t socket_borrow =
sockets_borrow_tcp_socket(socket->socket);

if (!sockets_method_tcp_socket_bind(socket_borrow, address, &error)) {
errno = __wasi_sockets_utils__map_error(error);
return -1;
}
return 0;
}

static int tcp_bind(void *data, const struct sockaddr *addr,
socklen_t addrlen) {
tcp_socket_t *socket = (tcp_socket_t *)data;
sockets_ip_socket_address_t local_address;
int parse_err;
if (!__wasi_sockets_utils__parse_address(socket->family, addr, addrlen,
&local_address, &parse_err)) {
errno = parse_err;
return -1;
}
return tcp_do_bind(socket, &local_address);
}

static int tcp_getsockname(void *data, struct sockaddr *addr,
socklen_t *addrlen) {
tcp_socket_t *socket = (tcp_socket_t *)data;
output_sockaddr_t output_addr;
if (!__wasi_sockets_utils__output_addr_validate(socket->family, addr, addrlen,
&output_addr)) {
errno = EINVAL;
return -1;
}

if (output_addr.tag == OUTPUT_SOCKADDR_NULL) {
errno = EINVAL;
return -1;
}

sockets_error_code_t error;
sockets_ip_socket_address_t result;
sockets_borrow_tcp_socket_t socket_borrow =
sockets_borrow_tcp_socket(socket->socket);
if (!sockets_method_tcp_socket_get_local_address(socket_borrow, &result,
&error)) {
errno = __wasi_sockets_utils__map_error(error);
return -1;
}

__wasi_sockets_utils__output_addr_write(result, &output_addr);
return 0;
}

static int tcp_listen(void *data, int backlog) {
tcp_socket_t *socket = (tcp_socket_t *)data;
sockets_error_code_t error;
sockets_borrow_tcp_socket_t socket_borrow =
sockets_borrow_tcp_socket(socket->socket);

if (!sockets_method_tcp_socket_set_listen_backlog_size(socket_borrow, backlog,
&error)) {
errno = __wasi_sockets_utils__map_error(error);
return -1;
}

// sockets_stream_own_tcp_socket_t ret;
if (!sockets_method_tcp_socket_listen(socket_borrow, &socket->incoming,
&error)) {
errno = __wasi_sockets_utils__map_error(error);
return -1;
}

return 0;
}

static int tcp_setsockopt(void *data, int level, int optname,
const void *optval, socklen_t optlen) {
tcp_socket_t *socket = (tcp_socket_t *)data;
int intval = *(int *)optval;

switch (level) {
case SOL_TCP:
switch (optname) {
case TCP_NODELAY: {
// is this right?
socket->blocking = false;
return 0;
}
default:
errno = ENOPROTOOPT;
return -1;
}
break;

default:
errno = ENOPROTOOPT;
return -1;
}
}

static int tcp_read_stream(
void *data,
filesystem_tuple2_stream_u8_future_result_void_error_code_t **out,
off_t **offs) {
tcp_socket_t *file = (tcp_socket_t *)data;
if (file->receive.f0 == 0) {
sockets_method_tcp_socket_receive(sockets_borrow_tcp_socket(file->socket),
&file->receive);
}
*out = (filesystem_tuple2_stream_u8_future_result_void_error_code_t *)&file
->receive;
*offs = 0;
return 0;
}

static int tcp_write_stream(void *data, wasip3_write_t **out, off_t **offs) {
tcp_socket_t *file = (tcp_socket_t *)data;
if (file->send.output == 0) {
sockets_stream_u8_t send_read = sockets_stream_u8_new(&file->send.output);
file->send.subtask = sockets_method_tcp_socket_send(
sockets_borrow_tcp_socket(file->socket), send_read,
(sockets_result_void_error_code_t *)&file->send.pending_result);
}
*out = &file->send;
*offs = 0;
return 0;
}

static void tcp_free(void *data) {
tcp_socket_t *tcp = (tcp_socket_t *)data;
sockets_stream_u8_drop_readable(tcp->receive.f0);
sockets_future_result_void_error_code_drop_readable(tcp->receive.f1);
if (tcp->incoming != 0)
sockets_stream_own_tcp_socket_drop_readable(tcp->incoming);
if (tcp->receive.f0 != 0) {
sockets_stream_u8_drop_readable(tcp->receive.f0);
tcp->receive.f0 = 0;
sockets_future_result_void_error_code_drop_readable(tcp->receive.f1);
tcp->receive.f1 = 0;
}
if (tcp->send.output != 0) {
sockets_stream_u8_drop_writable(tcp->send.output);
tcp->send.output = 0;
wasip3_subtask_block_on(tcp->send.subtask);
tcp->send.subtask = WASIP3_SUBTASK_RETURNED;
if (tcp->send.pending_result.is_err) {
errno = __wasi_sockets_utils__map_error(tcp->send.pending_result.val.err);
}
}
free(data);
}

static descriptor_vtable_t tcp_vtable = {
.bind = tcp_bind,
.connect = tcp_connect,
.getsockname = tcp_getsockname,
.listen = tcp_listen,
.free = tcp_free,
.setsockopt = tcp_setsockopt,
.get_read_stream3 = tcp_read_stream,
.get_write_stream3 = tcp_write_stream,
};

int __wasilibc_add_tcp_socket(sockets_own_tcp_socket_t socket,
sockets_ip_address_family_t family,
bool blocking) {
// TODO(wasip3)
errno = ENOTSUP;
return -1;
tcp_socket_t *tcp = calloc(1, sizeof(tcp_socket_t));
if (!tcp) {
sockets_tcp_socket_drop_own(socket);
errno = ENOMEM;
return -1;
}
tcp->socket = socket;
tcp->family = family;
tcp->blocking = blocking;
tcp->incoming = 0;

descriptor_table_entry_t entry;
entry.vtable = &tcp_vtable;
entry.data = tcp;
return descriptor_table_insert(entry);
}
#endif // __wasip3__