Skip to content
Open
Show file tree
Hide file tree
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
1 change: 1 addition & 0 deletions lib/wasix/src/net/socket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1227,6 +1227,7 @@ impl InodeSocket {
let mut inner = self.inner.protected.write().unwrap();
let res = match &mut inner.kind {
InodeSocketKind::Icmp(socket) => socket.try_send_to(self.data, self.addr),
InodeSocketKind::TcpStream { socket, .. } => socket.try_send(self.data),
InodeSocketKind::UdpSocket { socket, .. } => {
socket.try_send_to(self.data, self.addr)
}
Expand Down
376 changes: 376 additions & 0 deletions tests/c-wasi-tests/sock_send_to.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,376 @@
#include <assert.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <wasi/api.h>
#include <wasi/api_wasix.h>

#ifndef __WASI_SOCK_RECV_INPUT_DONT_WAIT
#define __WASI_SOCK_RECV_INPUT_DONT_WAIT ((__wasi_riflags_t)(1 << 3))
#endif

static int failures = 0;

static void expect_errno(const char* name, __wasi_errno_t got,
__wasi_errno_t expect) {
if (got != expect) {
fprintf(stderr, "%s: expected %u, got %u\n", name, expect, got);
failures++;
}
}

static void set_ipv4_addr_port_le(__wasi_addr_port_t* addr, uint16_t port,
uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
memset(addr, 0, sizeof(*addr));
addr->tag = __WASI_ADDRESS_FAMILY_INET4;
unsigned char* octs = (unsigned char*)&addr->u;
octs[0] = (unsigned char)(port & 0xff);
octs[1] = (unsigned char)((port >> 8) & 0xff);
octs[2] = a;
octs[3] = b;
octs[4] = c;
octs[5] = d;
}

static uint16_t port_from_addr_be(const __wasi_addr_port_t* addr) {
const unsigned char* octs = (const unsigned char*)&addr->u;
return (uint16_t)(((uint16_t)octs[0] << 8) | (uint16_t)octs[1]);
}

static __wasi_errno_t recv_from_retry(__wasi_fd_t fd, __wasi_iovec_t* iov,
__wasi_size_t iov_len,
__wasi_riflags_t flags,
__wasi_size_t* nread,
__wasi_roflags_t* roflags,
__wasi_addr_port_t* peer) {
for (int i = 0; i < 1000; i++) {
__wasi_errno_t err = __wasi_sock_recv_from(
fd, iov, iov_len, flags | __WASI_SOCK_RECV_INPUT_DONT_WAIT, nread,
roflags, peer);
if (err == __WASI_ERRNO_AGAIN) {
continue;
}
return err;
}
return __WASI_ERRNO_AGAIN;
}

static __wasi_errno_t send_to_retry(__wasi_fd_t fd, const __wasi_ciovec_t* iov,
__wasi_size_t iov_len,
__wasi_siflags_t flags,
const __wasi_addr_port_t* addr,
__wasi_size_t* nsent) {
for (int i = 0; i < 100; i++) {
__wasi_errno_t err =
__wasi_sock_send_to(fd, iov, iov_len, flags, addr, nsent);
if (err == __WASI_ERRNO_INTR) {
continue;
}
return err;
}
return __WASI_ERRNO_INTR;
}

static void open_udp_server(__wasi_fd_t* server_fd, __wasi_addr_port_t* addr) {
__wasi_errno_t err = __wasi_sock_open(__WASI_ADDRESS_FAMILY_INET4,
__WASI_SOCK_TYPE_SOCKET_DGRAM,
__WASI_SOCK_PROTO_UDP, server_fd);
assert(err == __WASI_ERRNO_SUCCESS);

__wasi_addr_port_t bind_addr;
set_ipv4_addr_port_le(&bind_addr, 0, 127, 0, 0, 1);
err = __wasi_sock_bind(*server_fd, &bind_addr);
assert(err == __WASI_ERRNO_SUCCESS);

err = __wasi_sock_addr_local(*server_fd, addr);
assert(err == __WASI_ERRNO_SUCCESS);
assert(addr->tag == __WASI_ADDRESS_FAMILY_INET4);
assert(port_from_addr_be(addr) != 0);
}

static void open_udp_client(__wasi_fd_t* client_fd) {
__wasi_errno_t err = __wasi_sock_open(__WASI_ADDRESS_FAMILY_INET4,
__WASI_SOCK_TYPE_SOCKET_DGRAM,
__WASI_SOCK_PROTO_UDP, client_fd);
assert(err == __WASI_ERRNO_SUCCESS);
}

static void open_connected_tcp(__wasi_fd_t* client_fd, __wasi_fd_t* server_fd,
__wasi_fd_t* accepted_fd,
__wasi_addr_port_t* server_addr) {
__wasi_errno_t err = __wasi_sock_open(__WASI_ADDRESS_FAMILY_INET4,
__WASI_SOCK_TYPE_SOCKET_STREAM,
__WASI_SOCK_PROTO_TCP, server_fd);
assert(err == __WASI_ERRNO_SUCCESS);

__wasi_addr_port_t bind_addr;
set_ipv4_addr_port_le(&bind_addr, 0, 127, 0, 0, 1);
err = __wasi_sock_bind(*server_fd, &bind_addr);
assert(err == __WASI_ERRNO_SUCCESS);

err = __wasi_sock_listen(*server_fd, 1);
assert(err == __WASI_ERRNO_SUCCESS);

err = __wasi_sock_addr_local(*server_fd, server_addr);
assert(err == __WASI_ERRNO_SUCCESS);
assert(server_addr->tag == __WASI_ADDRESS_FAMILY_INET4);
uint16_t port = port_from_addr_be(server_addr);
assert(port != 0);

err = __wasi_sock_open(__WASI_ADDRESS_FAMILY_INET4,
__WASI_SOCK_TYPE_SOCKET_STREAM, __WASI_SOCK_PROTO_TCP,
client_fd);
assert(err == __WASI_ERRNO_SUCCESS);

__wasi_addr_port_t connect_addr;
set_ipv4_addr_port_le(&connect_addr, port, 127, 0, 0, 1);
err = __wasi_sock_connect(*client_fd, &connect_addr);
assert(err == __WASI_ERRNO_SUCCESS);

__wasi_addr_port_t accepted_addr;
err = __wasi_sock_accept_v2(*server_fd, 0, accepted_fd, &accepted_addr);
assert(err == __WASI_ERRNO_SUCCESS);
}

static void recv_exact(__wasi_fd_t fd, void* out, size_t len) {
uint8_t* bytes = (uint8_t*)out;
size_t offset = 0;
while (offset < len) {
__wasi_iovec_t iov = {
.buf = bytes + offset,
.buf_len = len - offset,
};
__wasi_size_t nread = 0;
__wasi_roflags_t roflags = 0;
__wasi_errno_t err = __wasi_sock_recv(fd, &iov, 1, 0, &nread, &roflags);
assert(err == __WASI_ERRNO_SUCCESS);
assert(nread > 0);
offset += nread;
}
}

static void test_invalid_fd(void) {
printf("Test 1: invalid fd\n");
const uint8_t msg[] = "x";
__wasi_ciovec_t iov = {.buf = msg, .buf_len = sizeof(msg)};
__wasi_size_t nsent = 0;
__wasi_addr_port_t addr;
set_ipv4_addr_port_le(&addr, 1234, 127, 0, 0, 1);

__wasi_errno_t err = __wasi_sock_send_to(9999, &iov, 1, 0, &addr, &nsent);
expect_errno("invalid fd", err, __WASI_ERRNO_BADF);
}

static void test_not_socket(void) {
printf("Test 2: not a socket\n");
int fd = open("sock_send_to_file", O_CREAT | O_RDWR, 0644);
assert(fd >= 0);

const uint8_t msg[] = "x";
__wasi_ciovec_t iov = {.buf = msg, .buf_len = sizeof(msg)};
__wasi_size_t nsent = 0;
__wasi_addr_port_t addr;
set_ipv4_addr_port_le(&addr, 1234, 127, 0, 0, 1);

__wasi_errno_t err =
__wasi_sock_send_to((__wasi_fd_t)fd, &iov, 1, 0, &addr, &nsent);
expect_errno("not socket", err, __WASI_ERRNO_NOTSOCK);

close(fd);
assert(unlink("sock_send_to_file") == 0);
}

static void test_invalid_iovec(void) {
printf("Test 3: invalid iovec pointer\n");
__wasi_fd_t fd = 0;
open_udp_client(&fd);

__wasi_ciovec_t* bad_iov = (__wasi_ciovec_t*)(uintptr_t)0xFFFFFFFFu;
__wasi_size_t nsent = 0;
__wasi_addr_port_t addr;
set_ipv4_addr_port_le(&addr, 1234, 127, 0, 0, 1);

__wasi_errno_t err = __wasi_sock_send_to(fd, bad_iov, 1, 0, &addr, &nsent);
expect_errno("invalid iovec", err, __WASI_ERRNO_MEMVIOLATION);

close(fd);
}

static void test_invalid_buffer(void) {
printf("Test 4: invalid buffer\n");
__wasi_fd_t fd = 0;
open_udp_client(&fd);

__wasi_ciovec_t iov = {
.buf = (const uint8_t*)0xFFFFF000u,
.buf_len = 4,
};
__wasi_size_t nsent = 0;
__wasi_addr_port_t addr;
set_ipv4_addr_port_le(&addr, 1234, 127, 0, 0, 1);

__wasi_errno_t err = __wasi_sock_send_to(fd, &iov, 1, 0, &addr, &nsent);
expect_errno("invalid buffer", err, __WASI_ERRNO_MEMVIOLATION);

close(fd);
}

static void test_invalid_addr(void) {
printf("Test 5: invalid addr pointer\n");
__wasi_fd_t fd = 0;
open_udp_client(&fd);

const uint8_t msg[] = "x";
__wasi_ciovec_t iov = {.buf = msg, .buf_len = sizeof(msg)};
__wasi_size_t nsent = 0;
__wasi_addr_port_t* bad_addr = (__wasi_addr_port_t*)(uintptr_t)0xFFFFFFFFu;

__wasi_errno_t err = __wasi_sock_send_to(fd, &iov, 1, 0, bad_addr, &nsent);
expect_errno("invalid addr", err, __WASI_ERRNO_MEMVIOLATION);

close(fd);
}

static void test_invalid_addr_family(void) {
printf("Test 6: invalid addr family\n");
__wasi_fd_t fd = 0;
open_udp_client(&fd);

const uint8_t msg[] = "x";
__wasi_ciovec_t iov = {.buf = msg, .buf_len = sizeof(msg)};
__wasi_size_t nsent = 0;
__wasi_addr_port_t addr;
memset(&addr, 0, sizeof(addr));
addr.tag = __WASI_ADDRESS_FAMILY_UNIX;

__wasi_errno_t err = __wasi_sock_send_to(fd, &iov, 1, 0, &addr, &nsent);
expect_errno("invalid addr family", err, __WASI_ERRNO_INVAL);

close(fd);
}

static void test_basic_sendto(void) {
printf("Test 7: UDP sendto + recvfrom\n");
__wasi_fd_t server_fd = 0, client_fd = 0;
__wasi_addr_port_t server_addr;
open_udp_server(&server_fd, &server_addr);
open_udp_client(&client_fd);

__wasi_addr_port_t dest_addr;
uint16_t port = port_from_addr_be(&server_addr);
set_ipv4_addr_port_le(&dest_addr, port, 127, 0, 0, 1);

const uint8_t msg[] = "hello";
__wasi_ciovec_t iov = {.buf = msg, .buf_len = sizeof(msg)};
__wasi_size_t nsent = 0;

__wasi_errno_t err = send_to_retry(client_fd, &iov, 1, 0, &dest_addr, &nsent);
assert(err == __WASI_ERRNO_SUCCESS);
assert(nsent == sizeof(msg));

uint8_t buf[8] = {0};
__wasi_iovec_t riov = {.buf = buf, .buf_len = sizeof(buf)};
__wasi_size_t nread = 0;
__wasi_roflags_t roflags = 0;
__wasi_addr_port_t peer;
err = recv_from_retry(server_fd, &riov, 1, 0, &nread, &roflags, &peer);
assert(err == __WASI_ERRNO_SUCCESS);
assert(nread == sizeof(msg));
assert(memcmp(buf, msg, sizeof(msg)) == 0);

close(server_fd);
close(client_fd);
}

static void test_udp_message_too_big(void) {
printf("Test 8: UDP message too big\n");
__wasi_fd_t server_fd = 0, client_fd = 0;
__wasi_addr_port_t server_addr;
open_udp_server(&server_fd, &server_addr);
open_udp_client(&client_fd);

__wasi_addr_port_t dest_addr;
uint16_t port = port_from_addr_be(&server_addr);
set_ipv4_addr_port_le(&dest_addr, port, 127, 0, 0, 1);

static uint8_t bigbuf[128 * 1024];
memset(bigbuf, 0x42, sizeof(bigbuf));

__wasi_ciovec_t iov = {.buf = bigbuf, .buf_len = sizeof(bigbuf)};
__wasi_size_t nsent = 0;
__wasi_errno_t err = send_to_retry(client_fd, &iov, 1, 0, &dest_addr, &nsent);
expect_errno("udp msg too big", err, __WASI_ERRNO_MSGSIZE);

close(server_fd);
close(client_fd);
}

static void test_invalid_ro_data_len(void) {
printf("Test 9: invalid ro_data_len pointer\n");
__wasi_fd_t server_fd = 0, client_fd = 0;
__wasi_addr_port_t server_addr;
open_udp_server(&server_fd, &server_addr);
open_udp_client(&client_fd);

const uint8_t msg[] = "x";
__wasi_ciovec_t iov = {.buf = msg, .buf_len = sizeof(msg)};
__wasi_size_t* bad_len = (__wasi_size_t*)(uintptr_t)0xFFFFFFFFu;

__wasi_errno_t err =
__wasi_sock_send_to(client_fd, &iov, 1, 0, &server_addr, bad_len);
expect_errno("invalid ro_data_len", err, __WASI_ERRNO_MEMVIOLATION);

close(server_fd);
close(client_fd);
}

static void test_connected_tcp_sendto(void) {
printf("Test 10: connected TCP sendto behaves like send\n");
__wasi_fd_t server_fd = 0, client_fd = 0, accepted_fd = 0;
__wasi_addr_port_t server_addr;
open_connected_tcp(&client_fd, &server_fd, &accepted_fd, &server_addr);

const uint8_t msg[] = "hello";
__wasi_ciovec_t iov = {.buf = msg, .buf_len = sizeof(msg)};
__wasi_size_t nsent = 0;

__wasi_errno_t err =
__wasi_sock_send_to(client_fd, &iov, 1, 0, &server_addr, &nsent);
expect_errno("connected tcp sendto", err, __WASI_ERRNO_SUCCESS);
if (err == __WASI_ERRNO_SUCCESS) {
assert(nsent == sizeof(msg));

uint8_t buf[sizeof(msg)] = {0};
recv_exact(accepted_fd, buf, sizeof(buf));
assert(memcmp(buf, msg, sizeof(msg)) == 0);
}

close(accepted_fd);
close(server_fd);
close(client_fd);
}

int main(void) {
printf("WASIX sock_send_to integration tests\n");

test_invalid_fd();
test_not_socket();
test_invalid_iovec();
test_invalid_buffer();
test_invalid_addr();
test_invalid_addr_family();
test_basic_sendto();
test_udp_message_too_big();
test_invalid_ro_data_len();
test_connected_tcp_sendto();

if (failures) {
fprintf(stderr, "%d test(s) failed\n", failures);
return 1;
}

printf("All tests passed!\n");
return 0;
}
Loading