From 5ddc335a7f286f136b77f3636c245bccacd948c0 Mon Sep 17 00:00:00 2001 From: Callum Macmillan Date: Fri, 7 Oct 2022 03:31:21 +0100 Subject: [PATCH] Add timeout send/recv and multicast client/server socket examples (#1519) Add a couple of socket examples that can be used with WAMR: - The `timeout_client` and `timeout_server` examples demonstrate socket send and receive timeouts using the socket options - The `multicast_client` and `multicast_server` examples demonstrate receiving multicast packets in WASM And add several macro controls for `socket_opts` example. --- .../lib-socket/src/wasi/wasi_socket_ext.c | 8 +- samples/socket-api/CMakeLists.txt | 12 ++ samples/socket-api/README.md | 63 +++++++- samples/socket-api/wasm-src/CMakeLists.txt | 4 + .../socket-api/wasm-src/multicast_client.c | 135 ++++++++++++++++++ .../socket-api/wasm-src/multicast_server.c | 120 ++++++++++++++++ samples/socket-api/wasm-src/socket_opts.c | 51 +++---- samples/socket-api/wasm-src/timeout_client.c | 111 ++++++++++++++ samples/socket-api/wasm-src/timeout_server.c | 69 +++++++++ 9 files changed, 534 insertions(+), 39 deletions(-) create mode 100644 samples/socket-api/wasm-src/multicast_client.c create mode 100644 samples/socket-api/wasm-src/multicast_server.c create mode 100644 samples/socket-api/wasm-src/timeout_client.c create mode 100644 samples/socket-api/wasm-src/timeout_server.c diff --git a/core/iwasm/libraries/lib-socket/src/wasi/wasi_socket_ext.c b/core/iwasm/libraries/lib-socket/src/wasi/wasi_socket_ext.c index 22d51a7b..4609bbca 100644 --- a/core/iwasm/libraries/lib-socket/src/wasi/wasi_socket_ext.c +++ b/core/iwasm/libraries/lib-socket/src/wasi/wasi_socket_ext.c @@ -692,12 +692,12 @@ get_ipproto_ip_option(int sockfd, int optname, void *__restrict optval, HANDLE_ERROR(error); return error; case IP_TTL: - assert(*optlen == sizeof(uint8_t)); + assert(*optlen == sizeof(int)); error = __wasi_sock_get_ip_ttl(sockfd, (uint8_t *)optval); HANDLE_ERROR(error); return error; case IP_MULTICAST_TTL: - assert(*optlen == sizeof(uint8_t)); + assert(*optlen == sizeof(int)); error = __wasi_sock_get_ip_multicast_ttl(sockfd, (uint8_t *)optval); HANDLE_ERROR(error); return error; @@ -915,12 +915,12 @@ set_ipproto_ip_option(int sockfd, int optname, const void *optval, HANDLE_ERROR(error); return error; case IP_TTL: - assert(optlen == sizeof(uint8_t)); + assert(optlen == sizeof(int)); error = __wasi_sock_set_ip_ttl(sockfd, *(uint8_t *)optval); HANDLE_ERROR(error); return error; case IP_MULTICAST_TTL: - assert(optlen == sizeof(uint8_t)); + assert(optlen == sizeof(int)); error = __wasi_sock_set_ip_multicast_ttl(sockfd, *(uint8_t *)optval); HANDLE_ERROR(error); diff --git a/samples/socket-api/CMakeLists.txt b/samples/socket-api/CMakeLists.txt index fcf40cc5..fc8bdda7 100644 --- a/samples/socket-api/CMakeLists.txt +++ b/samples/socket-api/CMakeLists.txt @@ -94,6 +94,10 @@ ExternalProject_Add(wasm-app socket_opts.wasm ${CMAKE_BINARY_DIR} udp_client.wasm ${CMAKE_BINARY_DIR} udp_server.wasm ${CMAKE_BINARY_DIR} + multicast_client.wasm ${CMAKE_BINARY_DIR} + multicast_server.wasm ${CMAKE_BINARY_DIR} + timeout_client.wasm ${CMAKE_BINARY_DIR} + timeout_server.wasm ${CMAKE_BINARY_DIR} ) add_executable(tcp_server ${CMAKE_CURRENT_SOURCE_DIR}/wasm-src/tcp_server.c) @@ -112,6 +116,14 @@ add_executable(udp_client ${CMAKE_CURRENT_SOURCE_DIR}/wasm-src/udp_client.c) add_executable(udp_server ${CMAKE_CURRENT_SOURCE_DIR}/wasm-src/udp_server.c) +add_executable(multicast_client ${CMAKE_CURRENT_SOURCE_DIR}/wasm-src/multicast_client.c) + +add_executable(multicast_server ${CMAKE_CURRENT_SOURCE_DIR}/wasm-src/multicast_server.c) + +add_executable(timeout_client ${CMAKE_CURRENT_SOURCE_DIR}/wasm-src/timeout_client.c) + +add_executable(timeout_server ${CMAKE_CURRENT_SOURCE_DIR}/wasm-src/timeout_server.c) + ############################################ ## Build iwasm with wasi and pthread support ############################################ diff --git a/samples/socket-api/README.md b/samples/socket-api/README.md index 2d22e3b2..a838c8a6 100644 --- a/samples/socket-api/README.md +++ b/samples/socket-api/README.md @@ -93,20 +93,73 @@ Data: `socket_opts.wasm` shows an example of getting and setting various supported socket options ```bash -$ ./iwasm ./socket_opts.wasm +$ ./iwasm socket_opts.wasm ``` - -The output describes the different socket options that are set & retrieved, like so: +The output is: ```bash [Client] Create TCP socket [Client] Create UDP socket [Client] Create UDP IPv6 socket -SO_RCVTIMEO tv_sec is expected -SO_RCVTIMEO tv_usec is expected +setsockopt SO_RCVTIMEO result is expected +getsockopt SO_RCVTIMEO result is expected ... [Client] Close sockets ``` +The `timeout_client.wasm` and `timeout_server.wasm` examples demonstrate socket send and receive timeouts using the socket options. Start the server, then start the client. + +```bash +$ ./iwasm --addr-pool=0.0.0.0/15 timeout_server.wasm +``` + +The output is: + +```bash +Wait for client to connect +Client connected, sleeping for 10s +Shuting down +``` + +```bash +$ ./iwasm --addr-pool=127.0.0.1/15 --heap-size=10000000 timeout_client.wasm +``` + +The output is: + +```bash +Waiting on recv, which should timeout +Waiting on send, which should timeout +Success. Closing socket +``` + +The `multicast_client` and `multicast_server` examples demonstrate receiving multicast packets in WASM. Start the client and then the server with a multicast IP address and port. + +```bash +$ ./iwasm --addr-pool=0.0.0.0/0,::/0 multicast_client.wasm +$ ./iwasm --addr-pool=0.0.0.0/0,::/0 multicast_client.wasm 224.0.0.1 +$ ./iwasm --addr-pool=0.0.0.0/0,::/0 multicast_client.wasm FF02:113D:6FDD:2C17:A643:FFE2:1BD1:3CD2 +``` + +The output should be + +```bash +Joined multicast group. Waiting for datagram... +Reading datagram message...OK. +The message from multicast server is: "Test message" +``` + +```bash +$ ./multicast_server +$ ./multicast_server 224.0.0.1 +$ ./multicast_server FF02:113D:6FDD:2C17:A643:FFE2:1BD1:3CD2 +``` + +The output should be + +```bash +Datagram sent +``` + ### Domain name server resolution `addr_resolve.wasm` demonstrates the usage of resolving a domain name diff --git a/samples/socket-api/wasm-src/CMakeLists.txt b/samples/socket-api/wasm-src/CMakeLists.txt index 32cd7276..fda5d308 100644 --- a/samples/socket-api/wasm-src/CMakeLists.txt +++ b/samples/socket-api/wasm-src/CMakeLists.txt @@ -83,3 +83,7 @@ compile_with_clang(addr_resolve.c) compile_with_clang(socket_opts.c) compile_with_clang(udp_client.c) compile_with_clang(udp_server.c) +compile_with_clang(multicast_client.c) +compile_with_clang(multicast_server.c) +compile_with_clang(timeout_client.c) +compile_with_clang(timeout_server.c) diff --git a/samples/socket-api/wasm-src/multicast_client.c b/samples/socket-api/wasm-src/multicast_client.c new file mode 100644 index 00000000..0f0fa062 --- /dev/null +++ b/samples/socket-api/wasm-src/multicast_client.c @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2022 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include +#include +#include +#include +#ifdef __wasi__ +#include +#endif + +static void +init_sockaddr_inet(struct sockaddr_in *addr) +{ + addr->sin_family = AF_INET; + addr->sin_port = htons(1234); +} + +static void +init_sockaddr_inet6(struct sockaddr_in6 *addr) +{ + addr->sin6_family = AF_INET6; + addr->sin6_port = htons(1234); +} + +static int +get_ip_addr_type(char *addr, char *buf) +{ + if (inet_pton(AF_INET6, addr, buf)) { + return AF_INET6; + } + if (inet_pton(AF_INET, addr, buf)) { + return AF_INET; + } + return -1; +} + +static int +is_valid_addr_type(int addr_type) +{ + return !(addr_type == -1 + || (addr_type != AF_INET && addr_type != AF_INET6)); +} + +int +main(int argc, char *argv[]) +{ + struct ipv6_mreq ipv6_group; + struct ip_mreq ipv4_group; + int sd; + int datalen; + char databuf[1024]; + char multicast_addr_buffer[16]; + struct sockaddr_storage local_address = { 0 }; + int addr_type = -1; + int read_result; + int bool_opt = 1; + + if (argc < 2) { + printf("Usage is \n"); + return EXIT_FAILURE; + } + + addr_type = get_ip_addr_type(argv[1], multicast_addr_buffer); + + if (!is_valid_addr_type(addr_type)) { + printf("Not a valid ipv4 or ipv6 address\n"); + return EXIT_FAILURE; + } + + if ((sd = socket(addr_type, SOCK_DGRAM, 0)) == -1) { + perror("Failed opening socket"); + return EXIT_FAILURE; + } + + if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &bool_opt, sizeof(bool_opt)) + == -1) { + perror("Failed setting SO_REUSEADDR"); + goto fail; + } + + if (addr_type == AF_INET) { + init_sockaddr_inet((struct sockaddr_in *)&local_address); + memcpy(&(ipv4_group.imr_multiaddr), multicast_addr_buffer, 4); + ipv4_group.imr_interface.s_addr = htonl(INADDR_ANY); + + if (setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &ipv4_group, + sizeof(ipv4_group)) + == -1) { + perror("Failed joining IPv4 multicast group"); + goto fail; + } + } + else { + init_sockaddr_inet6((struct sockaddr_in6 *)&local_address); + memcpy(&(ipv6_group.ipv6mr_multiaddr), multicast_addr_buffer, 16); + ipv6_group.ipv6mr_interface = 0; + + if (setsockopt(sd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &ipv6_group, + sizeof(ipv6_group)) + == -1) { + perror("Failed joining IPv6 multicast group"); + goto fail; + } + } + + if (bind(sd, (struct sockaddr *)&local_address, sizeof(local_address)) + == -1) { + perror("Failed binding socket"); + goto fail; + } + + printf("Joined multicast group. Waiting for datagram...\n"); + + datalen = sizeof(databuf); + read_result = read(sd, databuf, datalen); + + if (read_result < 0) { + perror("Failed binding socket"); + goto fail; + } + + printf("Reading datagram message...OK.\n"); + printf("The message from multicast server is: \"%s\"\n", databuf); + close(sd); + return EXIT_SUCCESS; + +fail: + close(sd); + return EXIT_FAILURE; +} \ No newline at end of file diff --git a/samples/socket-api/wasm-src/multicast_server.c b/samples/socket-api/wasm-src/multicast_server.c new file mode 100644 index 00000000..cd33557b --- /dev/null +++ b/samples/socket-api/wasm-src/multicast_server.c @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2022 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include +#include +#include +#include +#ifdef __wasi__ +#include +#endif + +static int +get_ip_addr_type(char *addr, char *buf) +{ + if (inet_pton(AF_INET6, addr, buf)) { + return AF_INET6; + } + if (inet_pton(AF_INET, addr, buf)) { + return AF_INET; + } + return -1; +} + +static int +is_valid_addr_type(int addr_type) +{ + return !(addr_type == -1 + || (addr_type != AF_INET && addr_type != AF_INET6)); +} + +static void +init_sockaddr_inet(struct sockaddr_in *addr, char *addr_buffer) +{ + addr->sin_family = AF_INET; + addr->sin_port = htons(1234); + memcpy(&(addr->sin_addr), addr_buffer, 4); +} + +static void +init_sockaddr_inet6(struct sockaddr_in6 *addr, char *addr_buffer) +{ + addr->sin6_family = AF_INET6; + addr->sin6_port = htons(1234); + memcpy(&(addr->sin6_addr), addr_buffer, 16); +} + +int +main(int argc, char *argv[]) +{ + struct sockaddr_storage addr = { 0 }; + int sd; + char *databuf = "Test message"; + int datalen = strlen(databuf) + 1; + char multicast_addr_buffer[16]; + int addr_type = -1; + int multicast_interface; + int bool_opt = 1; + + if (argc < 2) { + printf("Usage is \n"); + return EXIT_FAILURE; + } + + addr_type = get_ip_addr_type(argv[1], multicast_addr_buffer); + + if (!is_valid_addr_type(addr_type)) { + printf("Not a valid ipv4 or ipv6 address\n"); + return EXIT_FAILURE; + } + + if ((sd = socket(addr_type, SOCK_DGRAM, 0)) == -1) { + return EXIT_FAILURE; + } + + if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &bool_opt, sizeof(bool_opt)) + == -1) { + perror("Failed setting SO_REUSEADDR"); + goto fail; + } + + if (addr_type == AF_INET) { + multicast_interface = htonl(INADDR_ANY); + if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, + (char *)&multicast_interface, + sizeof(multicast_interface))) { + perror("Failed setting local interface"); + goto fail; + } + init_sockaddr_inet((struct sockaddr_in *)&addr, multicast_addr_buffer); + } + else { + multicast_interface = 0; + if (setsockopt(sd, IPPROTO_IPV6, IPV6_MULTICAST_IF, + (char *)&multicast_interface, + sizeof(multicast_interface))) { + perror("Failed setting local interface"); + goto fail; + } + init_sockaddr_inet6((struct sockaddr_in6 *)&addr, + multicast_addr_buffer); + } + + if (sendto(sd, databuf, datalen, 0, (struct sockaddr *)&addr, sizeof(addr)) + == -1) { + perror("Failed sending datagram"); + goto fail; + } + + printf("Datagram sent\n"); + close(sd); + return EXIT_SUCCESS; + +fail: + close(sd); + return EXIT_FAILURE; +} \ No newline at end of file diff --git a/samples/socket-api/wasm-src/socket_opts.c b/samples/socket-api/wasm-src/socket_opts.c index aad82184..890cc0cc 100644 --- a/samples/socket-api/wasm-src/socket_opts.c +++ b/samples/socket-api/wasm-src/socket_opts.c @@ -1,15 +1,22 @@ +/* + * Copyright (C) 2022 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + #include #include #include -#include #include #include +#include #include +#include #include #ifdef __wasi__ #include #endif +#define MULTICAST_ADDR 16777440 #define OPTION_ASSERT(A, B, OPTION) \ if (A == B) { \ printf("%s is expected\n", OPTION); \ @@ -20,14 +27,14 @@ return EXIT_FAILURE; \ } -struct timeval +static struct timeval to_timeval(time_t tv_sec, suseconds_t tv_usec) { struct timeval tv = { tv_sec, tv_usec }; return tv; } -int +static int set_and_get_bool_opt(int socket_fd, int level, int optname, int val) { int bool_opt = val; @@ -58,9 +65,7 @@ main(int argc, char *argv[]) int result; struct linger linger_opt; uint32_t time_s; - struct ip_mreq mcast; - struct ipv6_mreq mcast_ipv6; - unsigned char ttl; + int ttl; printf("[Client] Create TCP socket\n"); tcp_socket_fd = socket(AF_INET, SOCK_STREAM, 0); @@ -120,7 +125,8 @@ main(int argc, char *argv[]) result = getsockopt(tcp_socket_fd, SOL_SOCKET, SO_SNDBUF, &buf_len, &opt_len); OPTION_ASSERT(result, 0, "getsockopt SO_SNDBUF result") - OPTION_ASSERT(buf_len, 16384, "SO_SNDBUF buf_len"); + OPTION_ASSERT((buf_len == 16384 || buf_len == 8192), 1, + "SO_SNDBUF buf_len"); // SO_RCVBUF buf_len = 4096; @@ -133,7 +139,7 @@ main(int argc, char *argv[]) result = getsockopt(tcp_socket_fd, SOL_SOCKET, SO_RCVBUF, &buf_len, &opt_len); OPTION_ASSERT(result, 0, "getsockopt SO_RCVBUF result") - OPTION_ASSERT(buf_len, 8192, "SO_RCVBUF buf_len"); + OPTION_ASSERT((buf_len == 8192 || buf_len == 4096), 1, "SO_SNDBUF buf_len"); // SO_KEEPALIVE OPTION_ASSERT( @@ -184,6 +190,7 @@ main(int argc, char *argv[]) "SO_BROADCAST disabled"); // TCP_KEEPIDLE +#ifdef TCP_KEEPIDLE time_s = 16; result = setsockopt(tcp_socket_fd, IPPROTO_TCP, TCP_KEEPIDLE, &time_s, sizeof(time_s)); @@ -195,6 +202,7 @@ main(int argc, char *argv[]) getsockopt(tcp_socket_fd, IPPROTO_TCP, TCP_KEEPIDLE, &time_s, &opt_len); OPTION_ASSERT(result, 0, "getsockopt TCP_KEEPIDLE result") OPTION_ASSERT(time_s, 16, "TCP_KEEPIDLE"); +#endif // TCP_KEEPINTVL time_s = 8; @@ -210,12 +218,14 @@ main(int argc, char *argv[]) OPTION_ASSERT(time_s, 8, "TCP_KEEPINTVL"); // TCP_FASTOPEN_CONNECT +#ifdef TCP_FASTOPEN_CONNECT OPTION_ASSERT(set_and_get_bool_opt(tcp_socket_fd, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, 1), 1, "TCP_FASTOPEN_CONNECT enabled"); OPTION_ASSERT(set_and_get_bool_opt(tcp_socket_fd, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, 0), 0, "TCP_FASTOPEN_CONNECT disabled"); +#endif // TCP_NODELAY OPTION_ASSERT( @@ -226,12 +236,14 @@ main(int argc, char *argv[]) "TCP_NODELAY disabled"); // TCP_QUICKACK +#ifdef TCP_QUICKACK OPTION_ASSERT( set_and_get_bool_opt(tcp_socket_fd, IPPROTO_TCP, TCP_QUICKACK, 1), 1, "TCP_QUICKACK enabled"); OPTION_ASSERT( set_and_get_bool_opt(tcp_socket_fd, IPPROTO_TCP, TCP_QUICKACK, 0), 0, "TCP_QUICKACK disabled"); +#endif // IP_TTL ttl = 8; @@ -259,18 +271,6 @@ main(int argc, char *argv[]) set_and_get_bool_opt(udp_socket_fd, IPPROTO_IP, IP_MULTICAST_LOOP, 0), 0, "IP_MULTICAST_LOOP disabled"); - // IP_ADD_MEMBERSHIP - mcast.imr_multiaddr.s_addr = 16777440; - mcast.imr_interface.s_addr = htonl(INADDR_ANY); - result = setsockopt(udp_socket_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mcast, - sizeof(mcast)); - OPTION_ASSERT(result, 0, "IP_ADD_MEMBERSHIP"); - - // IP_DROP_MEMBERSHIP - result = setsockopt(udp_socket_fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mcast, - sizeof(mcast)); - OPTION_ASSERT(result, 0, "IP_DROP_MEMBERSHIP"); - // IP_MULTICAST_TTL ttl = 8; result = setsockopt(udp_socket_fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, @@ -291,18 +291,9 @@ main(int argc, char *argv[]) IPV6_MULTICAST_LOOP, 0), 0, "IPV6_MULTICAST_LOOP disabled"); - // IPV6_JOIN_GROUP - result = setsockopt(udp_ipv6_socket_fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, - &mcast_ipv6, sizeof(mcast_ipv6)); - // OPTION_ASSERT(result, 0, "IPV6_JOIN_GROUP"); - - // IPV6_LEAVE_GROUP - result = setsockopt(udp_ipv6_socket_fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, - &mcast_ipv6, sizeof(mcast_ipv6)); - // OPTION_ASSERT(result, 0, "IPV6_LEAVE_GROUP"); - printf("[Client] Close sockets\n"); close(tcp_socket_fd); close(udp_socket_fd); + close(udp_ipv6_socket_fd); return EXIT_SUCCESS; } diff --git a/samples/socket-api/wasm-src/timeout_client.c b/samples/socket-api/wasm-src/timeout_client.c new file mode 100644 index 00000000..652021b5 --- /dev/null +++ b/samples/socket-api/wasm-src/timeout_client.c @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2022 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include +#include +#include +#include +#include +#ifdef __wasi__ +#include +#endif + +int +main(int argc, char *argv[]) +{ + int socket_fd; + struct sockaddr_in addr; + struct timeval tv = { 0, 1 }; + const int snd_buf_len = 8; + const int data_buf_len = 1000000; + char *buffer = (char *)malloc(sizeof(char) * data_buf_len); + int result; + socklen_t opt_len = sizeof(snd_buf_len); + struct timeval snd_start_time, snd_end_time; + int bool_opt = 1; + + if (buffer == NULL) { + perror("Allocation failed, please re-run with larger heap size"); + return EXIT_FAILURE; + } + + /* 127.0.0.1:1234 */ + addr.sin_family = AF_INET; + addr.sin_port = htons(1234); + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + if ((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { + perror("Create socket failed"); + goto fail1; + } + + if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &bool_opt, + sizeof(bool_opt)) + == -1) { + perror("Failed setting SO_REUSEADDR"); + goto fail2; + } + + if (setsockopt(socket_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) { + perror("Failed setting SO_RCVTIMEO"); + goto fail2; + } + + if (setsockopt(socket_fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) == -1) { + perror("Failed setting SO_SNDTIMEO"); + goto fail2; + } + + if (setsockopt(socket_fd, SOL_SOCKET, SO_SNDBUF, &data_buf_len, + sizeof(data_buf_len)) + == -1) { + perror("Failed setting SO_SNDBUF"); + goto fail2; + } + + if (connect(socket_fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + perror("Connect failed"); + goto fail2; + } + + if (getsockopt(socket_fd, SOL_SOCKET, SO_SNDBUF, (void *)&data_buf_len, + &opt_len) + == -1) { + perror("Failed getting SO_SNDBUF"); + goto fail2; + } + + printf("Waiting on recv, which should timeout\n"); + result = recv(socket_fd, buffer, 1, 0); + + if (result != -1 || errno != EAGAIN) { + perror("Recv did not timeout as expected"); + goto fail2; + } + + printf("Waiting on send, which should timeout\n"); + gettimeofday(&snd_start_time, NULL); + result = send(socket_fd, buffer, data_buf_len, 0); + gettimeofday(&snd_end_time, NULL); + + if (result >= data_buf_len + || snd_start_time.tv_sec != snd_end_time.tv_sec) { + perror("Send did not timeout as expected"); + goto fail2; + } + + printf("Success. Closing socket \n"); + close(socket_fd); + free(buffer); + return EXIT_SUCCESS; + +fail2: + close(socket_fd); +fail1: + free(buffer); + return EXIT_FAILURE; +} diff --git a/samples/socket-api/wasm-src/timeout_server.c b/samples/socket-api/wasm-src/timeout_server.c new file mode 100644 index 00000000..c71cf455 --- /dev/null +++ b/samples/socket-api/wasm-src/timeout_server.c @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2022 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include +#include +#include +#ifdef __wasi__ +#include +#endif + +int +main(int argc, char *argv[]) +{ + int socket_fd; + int client_socket_fd; + struct sockaddr_in addr = { 0 }; + int addrlen = sizeof(addr); + int bool_opt = 1; + + addr.sin_family = AF_INET; + addr.sin_port = htons(1234); + addr.sin_addr.s_addr = htonl(INADDR_ANY); + if ((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { + perror("Create socket failed"); + return EXIT_FAILURE; + } + + if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &bool_opt, + sizeof(bool_opt)) + == -1) { + perror("Failed setting SO_REUSEADDR"); + goto fail; + } + + if (bind(socket_fd, (struct sockaddr *)&addr, addrlen) == -1) { + perror("Bind socket failed"); + goto fail; + } + + if (listen(socket_fd, 1) == -1) { + perror("Listen failed"); + goto fail; + } + + if ((client_socket_fd = + accept(socket_fd, (struct sockaddr *)&addr, (socklen_t *)&addrlen)) + == -1) { + perror("Accept failed"); + goto fail; + } + + printf("Client connected, sleeping for 10s\n"); + sleep(10); + + printf("Shuting down\n"); + shutdown(client_socket_fd, SHUT_RDWR); + close(client_socket_fd); + shutdown(socket_fd, SHUT_RDWR); + close(socket_fd); + return EXIT_SUCCESS; + +fail: + close(socket_fd); + return EXIT_FAILURE; +}