From 2fcd5864e9e0a57eceda08219e52979eb4551ce9 Mon Sep 17 00:00:00 2001 From: Codex Date: Fri, 25 Sep 2026 15:06:26 -0700 Subject: [PATCH 1/2] fix: support TCP keepalive socket options --- expected/wasm32-wasi-eh/predefined-macros.txt | 3 + .../wasm32-wasi-ehpic/predefined-macros.txt | 3 + .../wasm32-wasi-threads/predefined-macros.txt | 3 + expected/wasm32-wasi/predefined-macros.txt | 3 + expected/wasm64-wasi/predefined-macros.txt | 3 + .../src/libc/sys/socket/getsockopt.c | 30 +++++ .../src/libc/sys/socket/setsockopt.c | 29 +++++ .../headers/public/wasi/api_wasix.h | 18 +++ test/wasix/c_tcp_keepalive/CMakeLists.txt | 4 + test/wasix/c_tcp_keepalive/main.c | 121 ++++++++++++++++++ test/wasix/c_tcp_keepalive/test.sh | 4 + tools/wasix-headers/WASI | 2 +- 12 files changed, 222 insertions(+), 1 deletion(-) create mode 100644 test/wasix/c_tcp_keepalive/CMakeLists.txt create mode 100644 test/wasix/c_tcp_keepalive/main.c create mode 100644 test/wasix/c_tcp_keepalive/test.sh diff --git a/expected/wasm32-wasi-eh/predefined-macros.txt b/expected/wasm32-wasi-eh/predefined-macros.txt index 826a19ac0..b7db27229 100644 --- a/expected/wasm32-wasi-eh/predefined-macros.txt +++ b/expected/wasm32-wasi-eh/predefined-macros.txt @@ -6227,6 +6227,9 @@ #define __WASI_SOCK_OPTION_SEND_BUF_SIZE (UINT8_C(16)) #define __WASI_SOCK_OPTION_SEND_LOWAT (UINT8_C(18)) #define __WASI_SOCK_OPTION_SEND_TIMEOUT (UINT8_C(20)) +#define __WASI_SOCK_OPTION_TCP_KEEP_COUNT (UINT8_C(29)) +#define __WASI_SOCK_OPTION_TCP_KEEP_IDLE (UINT8_C(27)) +#define __WASI_SOCK_OPTION_TCP_KEEP_INTERVAL (UINT8_C(28)) #define __WASI_SOCK_OPTION_TTL (UINT8_C(23)) #define __WASI_SOCK_OPTION_TYPE (UINT8_C(25)) #define __WASI_SOCK_PROTO_AH (UINT16_C(51)) diff --git a/expected/wasm32-wasi-ehpic/predefined-macros.txt b/expected/wasm32-wasi-ehpic/predefined-macros.txt index 8081fbcf5..b4156fdeb 100644 --- a/expected/wasm32-wasi-ehpic/predefined-macros.txt +++ b/expected/wasm32-wasi-ehpic/predefined-macros.txt @@ -6236,6 +6236,9 @@ #define __WASI_SOCK_OPTION_SEND_BUF_SIZE (UINT8_C(16)) #define __WASI_SOCK_OPTION_SEND_LOWAT (UINT8_C(18)) #define __WASI_SOCK_OPTION_SEND_TIMEOUT (UINT8_C(20)) +#define __WASI_SOCK_OPTION_TCP_KEEP_COUNT (UINT8_C(29)) +#define __WASI_SOCK_OPTION_TCP_KEEP_IDLE (UINT8_C(27)) +#define __WASI_SOCK_OPTION_TCP_KEEP_INTERVAL (UINT8_C(28)) #define __WASI_SOCK_OPTION_TTL (UINT8_C(23)) #define __WASI_SOCK_OPTION_TYPE (UINT8_C(25)) #define __WASI_SOCK_PROTO_AH (UINT16_C(51)) diff --git a/expected/wasm32-wasi-threads/predefined-macros.txt b/expected/wasm32-wasi-threads/predefined-macros.txt index 2c80edec3..87a3cf896 100644 --- a/expected/wasm32-wasi-threads/predefined-macros.txt +++ b/expected/wasm32-wasi-threads/predefined-macros.txt @@ -6018,6 +6018,9 @@ #define __WASI_SOCK_OPTION_SEND_BUF_SIZE (UINT8_C(16)) #define __WASI_SOCK_OPTION_SEND_LOWAT (UINT8_C(18)) #define __WASI_SOCK_OPTION_SEND_TIMEOUT (UINT8_C(20)) +#define __WASI_SOCK_OPTION_TCP_KEEP_COUNT (UINT8_C(29)) +#define __WASI_SOCK_OPTION_TCP_KEEP_IDLE (UINT8_C(27)) +#define __WASI_SOCK_OPTION_TCP_KEEP_INTERVAL (UINT8_C(28)) #define __WASI_SOCK_OPTION_TTL (UINT8_C(23)) #define __WASI_SOCK_OPTION_TYPE (UINT8_C(25)) #define __WASI_SOCK_PROTO_AH (UINT16_C(51)) diff --git a/expected/wasm32-wasi/predefined-macros.txt b/expected/wasm32-wasi/predefined-macros.txt index d75a22804..d74462993 100644 --- a/expected/wasm32-wasi/predefined-macros.txt +++ b/expected/wasm32-wasi/predefined-macros.txt @@ -6235,6 +6235,9 @@ #define __WASI_SOCK_OPTION_SEND_BUF_SIZE (UINT8_C(16)) #define __WASI_SOCK_OPTION_SEND_LOWAT (UINT8_C(18)) #define __WASI_SOCK_OPTION_SEND_TIMEOUT (UINT8_C(20)) +#define __WASI_SOCK_OPTION_TCP_KEEP_COUNT (UINT8_C(29)) +#define __WASI_SOCK_OPTION_TCP_KEEP_IDLE (UINT8_C(27)) +#define __WASI_SOCK_OPTION_TCP_KEEP_INTERVAL (UINT8_C(28)) #define __WASI_SOCK_OPTION_TTL (UINT8_C(23)) #define __WASI_SOCK_OPTION_TYPE (UINT8_C(25)) #define __WASI_SOCK_PROTO_AH (UINT16_C(51)) diff --git a/expected/wasm64-wasi/predefined-macros.txt b/expected/wasm64-wasi/predefined-macros.txt index 77144a3e1..82365859e 100644 --- a/expected/wasm64-wasi/predefined-macros.txt +++ b/expected/wasm64-wasi/predefined-macros.txt @@ -6083,6 +6083,9 @@ #define __WASI_SOCK_OPTION_SEND_BUF_SIZE (UINT8_C(16)) #define __WASI_SOCK_OPTION_SEND_LOWAT (UINT8_C(18)) #define __WASI_SOCK_OPTION_SEND_TIMEOUT (UINT8_C(20)) +#define __WASI_SOCK_OPTION_TCP_KEEP_COUNT (UINT8_C(29)) +#define __WASI_SOCK_OPTION_TCP_KEEP_IDLE (UINT8_C(27)) +#define __WASI_SOCK_OPTION_TCP_KEEP_INTERVAL (UINT8_C(28)) #define __WASI_SOCK_OPTION_TTL (UINT8_C(23)) #define __WASI_SOCK_OPTION_TYPE (UINT8_C(25)) #define __WASI_SOCK_PROTO_AH (UINT16_C(51)) diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/socket/getsockopt.c b/libc-bottom-half/cloudlibc/src/libc/sys/socket/getsockopt.c index 2f7129be0..9b0a626df 100644 --- a/libc-bottom-half/cloudlibc/src/libc/sys/socket/getsockopt.c +++ b/libc-bottom-half/cloudlibc/src/libc/sys/socket/getsockopt.c @@ -3,10 +3,40 @@ #include #include +#include #include int getsockopt(int socket, int level, int option_name, void *restrict option_value, socklen_t *restrict option_len) { + if (level == IPPROTO_TCP && + (option_name == TCP_KEEPIDLE || option_name == TCP_KEEPINTVL || + option_name == TCP_KEEPCNT)) { + if (option_len == NULL || (*option_len != 0 && option_value == NULL)) { + errno = EFAULT; + return -1; + } + __wasi_sock_option_t option = option_name == TCP_KEEPIDLE + ? __WASI_SOCK_OPTION_TCP_KEEP_IDLE + : option_name == TCP_KEEPINTVL + ? __WASI_SOCK_OPTION_TCP_KEEP_INTERVAL + : __WASI_SOCK_OPTION_TCP_KEEP_COUNT; + __wasi_filesize_t size; + __wasi_errno_t error = __wasi_sock_get_opt_size(socket, option, &size); + if (error != 0) { + errno = error; + return -1; + } + if (size > INT_MAX) { + errno = EOVERFLOW; + return -1; + } + int value = size; + if (*option_len > sizeof(value)) + *option_len = sizeof(value); + if (*option_len != 0) + memcpy(option_value, &value, *option_len); + return 0; + } if (level == IPPROTO_IPV6 && option_name == IPV6_V6ONLY) { level = SOL_SOCKET; option_name = SO_ONLYV6; diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/socket/setsockopt.c b/libc-bottom-half/cloudlibc/src/libc/sys/socket/setsockopt.c index 778898fe3..89e0bb5b3 100644 --- a/libc-bottom-half/cloudlibc/src/libc/sys/socket/setsockopt.c +++ b/libc-bottom-half/cloudlibc/src/libc/sys/socket/setsockopt.c @@ -7,6 +7,35 @@ #include int setsockopt(int socket, int level, int option_name, const void *restrict option_value, socklen_t option_len) { + if (level == IPPROTO_TCP && + (option_name == TCP_KEEPIDLE || option_name == TCP_KEEPINTVL || + option_name == TCP_KEEPCNT)) { + if (option_len < sizeof(int)) { + errno = EINVAL; + return -1; + } + if (option_value == NULL) { + errno = EFAULT; + return -1; + } + int value; + memcpy(&value, option_value, sizeof(value)); + if (value <= 0) { + errno = EINVAL; + return -1; + } + __wasi_sock_option_t option = option_name == TCP_KEEPIDLE + ? __WASI_SOCK_OPTION_TCP_KEEP_IDLE + : option_name == TCP_KEEPINTVL + ? __WASI_SOCK_OPTION_TCP_KEEP_INTERVAL + : __WASI_SOCK_OPTION_TCP_KEEP_COUNT; + __wasi_errno_t error = __wasi_sock_set_opt_size(socket, option, value); + if (error != 0) { + errno = error; + return -1; + } + return 0; + } if (level == IPPROTO_IPV6 && option_name == IPV6_V6ONLY) { level = SOL_SOCKET; option_name = SO_ONLYV6; diff --git a/libc-bottom-half/headers/public/wasi/api_wasix.h b/libc-bottom-half/headers/public/wasi/api_wasix.h index e43aa20c8..cf185c753 100644 --- a/libc-bottom-half/headers/public/wasi/api_wasix.h +++ b/libc-bottom-half/headers/public/wasi/api_wasix.h @@ -2529,6 +2529,24 @@ typedef uint8_t __wasi_sock_option_t; */ #define __WASI_SOCK_OPTION_PROTO (UINT8_C(26)) +/** + * TCP idle seconds before sending keepalive probes (positive integer). + * Read and written using sock_get_opt_size and sock_set_opt_size. + */ +#define __WASI_SOCK_OPTION_TCP_KEEP_IDLE (UINT8_C(27)) + +/** + * TCP seconds between keepalive probes (positive integer). + * Read and written using sock_get_opt_size and sock_set_opt_size. + */ +#define __WASI_SOCK_OPTION_TCP_KEEP_INTERVAL (UINT8_C(28)) + +/** + * TCP unanswered keepalive probes before closing the connection (positive integer). + * Read and written using sock_get_opt_size and sock_set_opt_size. + */ +#define __WASI_SOCK_OPTION_TCP_KEEP_COUNT (UINT8_C(29)) + _Static_assert(sizeof(__wasi_sock_option_t) == 1, "witx calculated size"); _Static_assert(_Alignof(__wasi_sock_option_t) == 1, "witx calculated align"); diff --git a/test/wasix/c_tcp_keepalive/CMakeLists.txt b/test/wasix/c_tcp_keepalive/CMakeLists.txt new file mode 100644 index 000000000..0c0f178cc --- /dev/null +++ b/test/wasix/c_tcp_keepalive/CMakeLists.txt @@ -0,0 +1,4 @@ +cmake_minimum_required(VERSION 3.5.0) +project(c_tcp_keepalive C) + +add_executable(main main.c) diff --git a/test/wasix/c_tcp_keepalive/main.c b/test/wasix/c_tcp_keepalive/main.c new file mode 100644 index 000000000..f527867eb --- /dev/null +++ b/test/wasix/c_tcp_keepalive/main.c @@ -0,0 +1,121 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +_Static_assert(__WASI_SOCK_OPTION_TCP_KEEP_IDLE == 27, "keep idle ABI"); +_Static_assert(__WASI_SOCK_OPTION_TCP_KEEP_INTERVAL == 28, "keep interval ABI"); +_Static_assert(__WASI_SOCK_OPTION_TCP_KEEP_COUNT == 29, "keep count ABI"); + +static const int options[] = {TCP_KEEPIDLE, TCP_KEEPINTVL, TCP_KEEPCNT}; +static const int values[] = {31, 7, 4}; + +static void check_options(int fd) { + for (unsigned i = 0; i < sizeof(options) / sizeof(options[0]); i++) { + int value = 0; + socklen_t len = sizeof(value); + assert(getsockopt(fd, IPPROTO_TCP, options[i], &value, &len) == 0); + assert(len == sizeof(value)); + assert(value == values[i]); + } +} + +static void check_validation(int fd) { + for (unsigned i = 0; i < sizeof(options) / sizeof(options[0]); i++) { + int value = 0; + errno = 0; + assert(setsockopt(fd, IPPROTO_TCP, options[i], &value, sizeof(value)) == -1); + assert(errno == EINVAL); + value = -1; + errno = 0; + assert(setsockopt(fd, IPPROTO_TCP, options[i], &value, sizeof(value)) == -1); + assert(errno == EINVAL); + value = 1; + errno = 0; + assert(setsockopt(fd, IPPROTO_TCP, options[i], &value, sizeof(value) - 1) == -1); + assert(errno == EINVAL); + errno = 0; + assert(setsockopt(fd, IPPROTO_TCP, options[i], NULL, sizeof(value)) == -1); + assert(errno == EFAULT); + } + check_options(fd); +} + +static void check_output_lengths(int fd) { + unsigned char output[sizeof(int) + 1]; + memset(output, 0xa5, sizeof(output)); + socklen_t len = 1; + assert(getsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, output, &len) == 0); + assert(len == 1); + assert(output[0] == ((const unsigned char *)&values[0])[0]); + assert(output[1] == 0xa5); + + len = sizeof(output); + assert(getsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, output, &len) == 0); + assert(len == sizeof(int)); + assert(memcmp(output, &values[0], sizeof(int)) == 0); + assert(output[sizeof(int)] == 0xa5); + + len = 0; + assert(getsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, NULL, &len) == 0); + assert(len == 0); + len = sizeof(int); + errno = 0; + assert(getsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, NULL, &len) == -1); + assert(errno == EFAULT); + errno = 0; + assert(getsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, output, NULL) == -1); + assert(errno == EFAULT); +} + +int main(void) { + int fd = socket(AF_INET, SOCK_STREAM, 0); + assert(fd >= 0); + int enabled = 1; + assert(setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &enabled, sizeof(enabled)) == 0); + for (unsigned i = 0; i < sizeof(options) / sizeof(options[0]); i++) + assert(setsockopt(fd, IPPROTO_TCP, options[i], &values[i], sizeof(values[i])) == 0); + check_options(fd); + check_validation(fd); + check_output_lengths(fd); + + int server = socket(AF_INET, SOCK_STREAM, 0); + assert(server >= 0); + struct sockaddr_in address = {0}; + address.sin_family = AF_INET; + address.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + assert(bind(server, (struct sockaddr *)&address, sizeof(address)) == 0); + assert(listen(server, 1) == 0); + socklen_t len = sizeof(address); + assert(getsockname(server, (struct sockaddr *)&address, &len) == 0); + assert(connect(fd, (struct sockaddr *)&address, sizeof(address)) == 0); + int peer = accept(server, NULL, NULL); + assert(peer >= 0); + check_options(fd); + check_validation(fd); + + int updated = 9; + assert(setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &updated, sizeof(updated)) == 0); + int actual = 0; + len = sizeof(actual); + assert(getsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &actual, &len) == 0); + assert(actual == updated); + + assert(close(peer) == 0); + assert(close(server) == 0); + assert(close(fd) == 0); + errno = 0; + assert(setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &updated, sizeof(updated)) == -1); + assert(errno == EBADF); + errno = 0; + len = sizeof(actual); + assert(getsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &actual, &len) == -1); + assert(errno == EBADF); + puts("TCP keepalive options passed"); + return 0; +} diff --git a/test/wasix/c_tcp_keepalive/test.sh b/test/wasix/c_tcp_keepalive/test.sh new file mode 100644 index 000000000..3f93cae0d --- /dev/null +++ b/test/wasix/c_tcp_keepalive/test.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail + +wasmer run --net ./main diff --git a/tools/wasix-headers/WASI b/tools/wasix-headers/WASI index 0dfbd35a0..154c0a533 160000 --- a/tools/wasix-headers/WASI +++ b/tools/wasix-headers/WASI @@ -1 +1 @@ -Subproject commit 0dfbd35a0f30f3fe7fd3b3ab5a50dc4191d5caed +Subproject commit 154c0a53329ca1f015dbf1ae4ab866d71a6d3a7c From e3a8f12393d87f11119035f4ad175ea482e8414e Mon Sep 17 00:00:00 2001 From: Codex Date: Fri, 25 Sep 2026 15:22:10 -0700 Subject: [PATCH 2/2] refactor: reuse integer socket option handling for TCP keepalive --- .../src/libc/sys/socket/getsockopt.c | 55 ++++++--------- .../src/libc/sys/socket/setsockopt.c | 70 +++++++------------ test/wasix/c_tcp_keepalive/main.c | 21 +++--- 3 files changed, 58 insertions(+), 88 deletions(-) diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/socket/getsockopt.c b/libc-bottom-half/cloudlibc/src/libc/sys/socket/getsockopt.c index 9b0a626df..67ebb1d29 100644 --- a/libc-bottom-half/cloudlibc/src/libc/sys/socket/getsockopt.c +++ b/libc-bottom-half/cloudlibc/src/libc/sys/socket/getsockopt.c @@ -3,47 +3,23 @@ #include #include -#include #include int getsockopt(int socket, int level, int option_name, void *restrict option_value, socklen_t *restrict option_len) { - if (level == IPPROTO_TCP && - (option_name == TCP_KEEPIDLE || option_name == TCP_KEEPINTVL || - option_name == TCP_KEEPCNT)) { - if (option_len == NULL || (*option_len != 0 && option_value == NULL)) { - errno = EFAULT; - return -1; - } - __wasi_sock_option_t option = option_name == TCP_KEEPIDLE - ? __WASI_SOCK_OPTION_TCP_KEEP_IDLE - : option_name == TCP_KEEPINTVL - ? __WASI_SOCK_OPTION_TCP_KEEP_INTERVAL - : __WASI_SOCK_OPTION_TCP_KEEP_COUNT; - __wasi_filesize_t size; - __wasi_errno_t error = __wasi_sock_get_opt_size(socket, option, &size); - if (error != 0) { - errno = error; - return -1; - } - if (size > INT_MAX) { - errno = EOVERFLOW; - return -1; - } - int value = size; - if (*option_len > sizeof(value)) - *option_len = sizeof(value); - if (*option_len != 0) - memcpy(option_value, &value, *option_len); - return 0; - } if (level == IPPROTO_IPV6 && option_name == IPV6_V6ONLY) { level = SOL_SOCKET; option_name = SO_ONLYV6; } - if (level == IPPROTO_TCP && option_name == TCP_NODELAY) { + if (level == IPPROTO_TCP) { + switch (option_name) { + case TCP_NODELAY: option_name = SO_NODELAY; break; + case TCP_KEEPIDLE: option_name = __WASI_SOCK_OPTION_TCP_KEEP_IDLE; break; + case TCP_KEEPINTVL: option_name = __WASI_SOCK_OPTION_TCP_KEEP_INTERVAL; break; + case TCP_KEEPCNT: option_name = __WASI_SOCK_OPTION_TCP_KEEP_COUNT; break; + default: errno = ENOPROTOOPT; return -1; + } level = SOL_SOCKET; - option_name = SO_NODELAY; } if (level != SOL_SOCKET) { @@ -127,7 +103,14 @@ int getsockopt(int socket, int level, int option_name, case SO_RCVBUF: case SO_SNDBUF: case SO_TTL: - case SO_MCASTTTLV4: { + case SO_MCASTTTLV4: + case __WASI_SOCK_OPTION_TCP_KEEP_IDLE: + case __WASI_SOCK_OPTION_TCP_KEEP_INTERVAL: + case __WASI_SOCK_OPTION_TCP_KEEP_COUNT: { + if (option_len == NULL || (*option_len != 0 && option_value == NULL)) { + errno = EFAULT; + return -1; + } __wasi_filesize_t fs; __wasi_errno_t error = __wasi_sock_get_opt_size(socket, option_name, &fs); if (error != 0) { @@ -135,8 +118,10 @@ int getsockopt(int socket, int level, int option_name, return -1; } socklen_t len = fs; - memcpy(option_value, &len, *option_len < sizeof(socklen_t ) ? *option_len : sizeof(socklen_t )); - *option_len = sizeof(socklen_t ); + if (*option_len > sizeof(len)) + *option_len = sizeof(len); + if (*option_len != 0) + memcpy(option_value, &len, *option_len); return 0; } case SO_PROTOCOL: { diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/socket/setsockopt.c b/libc-bottom-half/cloudlibc/src/libc/sys/socket/setsockopt.c index 89e0bb5b3..7bfa059da 100644 --- a/libc-bottom-half/cloudlibc/src/libc/sys/socket/setsockopt.c +++ b/libc-bottom-half/cloudlibc/src/libc/sys/socket/setsockopt.c @@ -7,42 +7,19 @@ #include int setsockopt(int socket, int level, int option_name, const void *restrict option_value, socklen_t option_len) { - if (level == IPPROTO_TCP && - (option_name == TCP_KEEPIDLE || option_name == TCP_KEEPINTVL || - option_name == TCP_KEEPCNT)) { - if (option_len < sizeof(int)) { - errno = EINVAL; - return -1; - } - if (option_value == NULL) { - errno = EFAULT; - return -1; - } - int value; - memcpy(&value, option_value, sizeof(value)); - if (value <= 0) { - errno = EINVAL; - return -1; - } - __wasi_sock_option_t option = option_name == TCP_KEEPIDLE - ? __WASI_SOCK_OPTION_TCP_KEEP_IDLE - : option_name == TCP_KEEPINTVL - ? __WASI_SOCK_OPTION_TCP_KEEP_INTERVAL - : __WASI_SOCK_OPTION_TCP_KEEP_COUNT; - __wasi_errno_t error = __wasi_sock_set_opt_size(socket, option, value); - if (error != 0) { - errno = error; - return -1; - } - return 0; - } if (level == IPPROTO_IPV6 && option_name == IPV6_V6ONLY) { level = SOL_SOCKET; option_name = SO_ONLYV6; } - if (level == IPPROTO_TCP && option_name == TCP_NODELAY) { + if (level == IPPROTO_TCP) { + switch (option_name) { + case TCP_NODELAY: option_name = SO_NODELAY; break; + case TCP_KEEPIDLE: option_name = __WASI_SOCK_OPTION_TCP_KEEP_IDLE; break; + case TCP_KEEPINTVL: option_name = __WASI_SOCK_OPTION_TCP_KEEP_INTERVAL; break; + case TCP_KEEPCNT: option_name = __WASI_SOCK_OPTION_TCP_KEEP_COUNT; break; + default: errno = ENOSYS; return -1; + } level = SOL_SOCKET; - option_name = SO_NODELAY; } if(level!=SOL_SOCKET) { @@ -115,19 +92,24 @@ int setsockopt(int socket, int level, int option_name, const void *restrict opti return 0; } case SO_RCVBUF: - case SO_SNDBUF: - case SO_TTL: - case SO_MCASTTTLV4: { - __wasi_filesize_t fs; - if (option_len >= sizeof(socklen_t)) { - socklen_t *len = (socklen_t *)option_value; - fs = *len; - } else { - errno = EINVAL; - return -1; - } - - __wasi_errno_t error = __wasi_sock_set_opt_size(socket, option_name, fs); + case SO_SNDBUF: + case SO_TTL: + case SO_MCASTTTLV4: + case __WASI_SOCK_OPTION_TCP_KEEP_IDLE: + case __WASI_SOCK_OPTION_TCP_KEEP_INTERVAL: + case __WASI_SOCK_OPTION_TCP_KEEP_COUNT: { + socklen_t value; + if (option_len < sizeof(value)) { + errno = EINVAL; + return -1; + } + if (option_value == NULL) { + errno = EFAULT; + return -1; + } + memcpy(&value, option_value, sizeof(value)); + + __wasi_errno_t error = __wasi_sock_set_opt_size(socket, option_name, value); if (error != 0) { errno = error; return -1; diff --git a/test/wasix/c_tcp_keepalive/main.c b/test/wasix/c_tcp_keepalive/main.c index f527867eb..944e61fdc 100644 --- a/test/wasix/c_tcp_keepalive/main.c +++ b/test/wasix/c_tcp_keepalive/main.c @@ -46,30 +46,30 @@ static void check_validation(int fd) { check_options(fd); } -static void check_output_lengths(int fd) { +static void check_output_lengths(int fd, int level, int option, int expected) { unsigned char output[sizeof(int) + 1]; memset(output, 0xa5, sizeof(output)); socklen_t len = 1; - assert(getsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, output, &len) == 0); + assert(getsockopt(fd, level, option, output, &len) == 0); assert(len == 1); - assert(output[0] == ((const unsigned char *)&values[0])[0]); + assert(output[0] == ((const unsigned char *)&expected)[0]); assert(output[1] == 0xa5); len = sizeof(output); - assert(getsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, output, &len) == 0); + assert(getsockopt(fd, level, option, output, &len) == 0); assert(len == sizeof(int)); - assert(memcmp(output, &values[0], sizeof(int)) == 0); + assert(memcmp(output, &expected, sizeof(int)) == 0); assert(output[sizeof(int)] == 0xa5); len = 0; - assert(getsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, NULL, &len) == 0); + assert(getsockopt(fd, level, option, NULL, &len) == 0); assert(len == 0); len = sizeof(int); errno = 0; - assert(getsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, NULL, &len) == -1); + assert(getsockopt(fd, level, option, NULL, &len) == -1); assert(errno == EFAULT); errno = 0; - assert(getsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, output, NULL) == -1); + assert(getsockopt(fd, level, option, output, NULL) == -1); assert(errno == EFAULT); } @@ -82,7 +82,10 @@ int main(void) { assert(setsockopt(fd, IPPROTO_TCP, options[i], &values[i], sizeof(values[i])) == 0); check_options(fd); check_validation(fd); - check_output_lengths(fd); + check_output_lengths(fd, IPPROTO_TCP, TCP_KEEPIDLE, values[0]); + int buffer_size = 4096; + assert(setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &buffer_size, sizeof(buffer_size)) == 0); + check_output_lengths(fd, SOL_SOCKET, SO_RCVBUF, buffer_size); int server = socket(AF_INET, SOCK_STREAM, 0); assert(server >= 0);