/* * Copyright (C) 2019 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #include #include #include #include #include #include #include #include #define HANDLE_ERROR(error) \ if (error != __WASI_ERRNO_SUCCESS) { \ errno = error; \ return -1; \ } static void ipv4_addr_to_wasi_ip4_addr(uint32_t addr_num, __wasi_addr_ip4_t *out) { addr_num = ntohl(addr_num); out->n0 = (addr_num & 0xFF000000) >> 24; out->n1 = (addr_num & 0x00FF0000) >> 16; out->n2 = (addr_num & 0x0000FF00) >> 8; out->n3 = (addr_num & 0x000000FF); } /* addr_num and port are in network order */ static void ipv4_addr_to_wasi_addr(uint32_t addr_num, uint16_t port, __wasi_addr_t *out) { out->kind = IPv4; out->addr.ip4.port = ntohs(port); ipv4_addr_to_wasi_ip4_addr(addr_num, &(out->addr.ip4.addr)); } static void ipv6_addr_to_wasi_ipv6_addr(uint16_t *addr, __wasi_addr_ip6_t *out) { out->n0 = ntohs(addr[0]); out->n1 = ntohs(addr[1]); out->n2 = ntohs(addr[2]); out->n3 = ntohs(addr[3]); out->h0 = ntohs(addr[4]); out->h1 = ntohs(addr[5]); out->h2 = ntohs(addr[6]); out->h3 = ntohs(addr[7]); } static void ipv6_addr_to_wasi_addr(uint16_t *addr, uint16_t port, __wasi_addr_t *out) { out->kind = IPv6; out->addr.ip6.port = ntohs(port); ipv6_addr_to_wasi_ipv6_addr(addr, &(out->addr.ip6.addr)); } static __wasi_errno_t sockaddr_to_wasi_addr(const struct sockaddr *sock_addr, socklen_t addrlen, __wasi_addr_t *wasi_addr) { __wasi_errno_t ret = __WASI_ERRNO_SUCCESS; if (AF_INET == sock_addr->sa_family) { assert(sizeof(struct sockaddr_in) <= addrlen); ipv4_addr_to_wasi_addr( ((struct sockaddr_in *)sock_addr)->sin_addr.s_addr, ((struct sockaddr_in *)sock_addr)->sin_port, wasi_addr); } else if (AF_INET6 == sock_addr->sa_family) { assert(sizeof(struct sockaddr_in6) <= addrlen); ipv6_addr_to_wasi_addr( (uint16_t *)((struct sockaddr_in6 *)sock_addr)->sin6_addr.s6_addr, ((struct sockaddr_in6 *)sock_addr)->sin6_port, wasi_addr); } else { ret = __WASI_ERRNO_AFNOSUPPORT; } return ret; } static __wasi_errno_t wasi_addr_to_sockaddr(const __wasi_addr_t *wasi_addr, struct sockaddr *sock_addr, socklen_t *addrlen) { switch (wasi_addr->kind) { case IPv4: { struct sockaddr_in sock_addr_in; uint32_t s_addr; memset(&sock_addr_in, 0, sizeof(sock_addr_in)); s_addr = (wasi_addr->addr.ip4.addr.n0 << 24) | (wasi_addr->addr.ip4.addr.n1 << 16) | (wasi_addr->addr.ip4.addr.n2 << 8) | wasi_addr->addr.ip4.addr.n3; sock_addr_in.sin_family = AF_INET; sock_addr_in.sin_addr.s_addr = htonl(s_addr); sock_addr_in.sin_port = htons(wasi_addr->addr.ip4.port); memcpy(sock_addr, &sock_addr_in, sizeof(sock_addr_in)); *addrlen = sizeof(sock_addr_in); break; } case IPv6: { struct sockaddr_in6 sock_addr_in6; memset(&sock_addr_in6, 0, sizeof(sock_addr_in6)); uint16_t *addr_buf = (uint16_t *)sock_addr_in6.sin6_addr.s6_addr; addr_buf[0] = htons(wasi_addr->addr.ip6.addr.n0); addr_buf[1] = htons(wasi_addr->addr.ip6.addr.n1); addr_buf[2] = htons(wasi_addr->addr.ip6.addr.n2); addr_buf[3] = htons(wasi_addr->addr.ip6.addr.n3); addr_buf[4] = htons(wasi_addr->addr.ip6.addr.h0); addr_buf[5] = htons(wasi_addr->addr.ip6.addr.h1); addr_buf[6] = htons(wasi_addr->addr.ip6.addr.h2); addr_buf[7] = htons(wasi_addr->addr.ip6.addr.h3); sock_addr_in6.sin6_family = AF_INET6; sock_addr_in6.sin6_port = htons(wasi_addr->addr.ip6.port); memcpy(sock_addr, &sock_addr_in6, sizeof(sock_addr_in6)); *addrlen = sizeof(sock_addr_in6); break; } default: return __WASI_ERRNO_AFNOSUPPORT; } return __WASI_ERRNO_SUCCESS; } int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) { __wasi_addr_t wasi_addr; __wasi_fd_t new_sockfd; __wasi_errno_t error; memset(&wasi_addr, 0, sizeof(wasi_addr)); error = __wasi_sock_accept(sockfd, 0, &new_sockfd); HANDLE_ERROR(error) if (getpeername(new_sockfd, addr, addrlen) == -1) { return -1; } return new_sockfd; } int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { __wasi_addr_t wasi_addr; __wasi_errno_t error; memset(&wasi_addr, 0, sizeof(wasi_addr)); error = sockaddr_to_wasi_addr(addr, addrlen, &wasi_addr); HANDLE_ERROR(error) error = __wasi_sock_bind(sockfd, &wasi_addr); HANDLE_ERROR(error) return __WASI_ERRNO_SUCCESS; } int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { __wasi_addr_t wasi_addr; __wasi_errno_t error; memset(&wasi_addr, 0, sizeof(wasi_addr)); if (NULL == addr) { HANDLE_ERROR(__WASI_ERRNO_INVAL) } error = sockaddr_to_wasi_addr(addr, addrlen, &wasi_addr); HANDLE_ERROR(error) error = __wasi_sock_connect(sockfd, &wasi_addr); HANDLE_ERROR(error) return __WASI_ERRNO_SUCCESS; } int listen(int sockfd, int backlog) { __wasi_errno_t error = __wasi_sock_listen(sockfd, backlog); HANDLE_ERROR(error) return __WASI_ERRNO_SUCCESS; } ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags) { // Prepare input parameters. __wasi_iovec_t *ri_data = NULL; size_t i = 0; size_t ro_datalen = 0; __wasi_roflags_t ro_flags = 0; if (NULL == msg) { HANDLE_ERROR(__WASI_ERRNO_INVAL) } // Validate flags. if (flags != 0) { HANDLE_ERROR(__WASI_ERRNO_NOPROTOOPT) } // __wasi_ciovec_t -> struct iovec if (!(ri_data = (__wasi_iovec_t *)malloc(sizeof(__wasi_iovec_t) * msg->msg_iovlen))) { HANDLE_ERROR(__WASI_ERRNO_NOMEM) } for (i = 0; i < msg->msg_iovlen; i++) { ri_data[i].buf = (uint8_t *)msg->msg_iov[i].iov_base; ri_data[i].buf_len = msg->msg_iov[i].iov_len; } // Perform system call. __wasi_errno_t error = __wasi_sock_recv(sockfd, ri_data, msg->msg_iovlen, 0, &ro_datalen, &ro_flags); free(ri_data); HANDLE_ERROR(error) return ro_datalen; } ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags) { // Prepare input parameters. __wasi_ciovec_t *si_data = NULL; size_t so_datalen = 0; size_t i = 0; if (NULL == msg) { HANDLE_ERROR(__WASI_ERRNO_INVAL) } // This implementation does not support any flags. if (flags != 0) { HANDLE_ERROR(__WASI_ERRNO_NOPROTOOPT) } // struct iovec -> __wasi_ciovec_t if (!(si_data = (__wasi_ciovec_t *)malloc(sizeof(__wasi_ciovec_t) * msg->msg_iovlen))) { HANDLE_ERROR(__WASI_ERRNO_NOMEM) } for (i = 0; i < msg->msg_iovlen; i++) { si_data[i].buf = (uint8_t *)msg->msg_iov[i].iov_base; si_data[i].buf_len = msg->msg_iov[i].iov_len; } // Perform system call. __wasi_errno_t error = __wasi_sock_send(sockfd, si_data, msg->msg_iovlen, 0, &so_datalen); free(si_data); HANDLE_ERROR(error) return so_datalen; } ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen) { // Prepare input parameters. __wasi_ciovec_t iov = { .buf = (uint8_t *)buf, .buf_len = len }; uint32_t so_datalen = 0; __wasi_addr_t wasi_addr; __wasi_errno_t error; size_t si_data_len = 1; __wasi_siflags_t si_flags = 0; // This implementation does not support any flags. if (flags != 0) { HANDLE_ERROR(__WASI_ERRNO_NOPROTOOPT) } error = sockaddr_to_wasi_addr(dest_addr, addrlen, &wasi_addr); HANDLE_ERROR(error); // Perform system call. error = __wasi_sock_send_to(sockfd, &iov, si_data_len, si_flags, &wasi_addr, &so_datalen); HANDLE_ERROR(error) return so_datalen; } ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen) { // Prepare input parameters. __wasi_ciovec_t iov = { .buf = (uint8_t *)buf, .buf_len = len }; uint32_t so_datalen = 0; __wasi_addr_t wasi_addr; __wasi_errno_t error; size_t si_data_len = 1; __wasi_siflags_t si_flags = 0; // This implementation does not support any flags. if (flags != 0) { HANDLE_ERROR(__WASI_ERRNO_NOPROTOOPT) } if (!src_addr) { return recv(sockfd, buf, len, flags); } // Perform system call. error = __wasi_sock_recv_from(sockfd, &iov, si_data_len, si_flags, &wasi_addr, &so_datalen); HANDLE_ERROR(error); error = wasi_addr_to_sockaddr(&wasi_addr, src_addr, addrlen); HANDLE_ERROR(error); return so_datalen; } int socket(int domain, int type, int protocol) { // the stub of address pool fd __wasi_fd_t poolfd = -1; __wasi_fd_t sockfd; __wasi_errno_t error; __wasi_address_family_t af; __wasi_sock_type_t socktype; if (AF_INET == domain) { af = INET4; } else if (AF_INET6 == domain) { af = INET6; } else { return __WASI_ERRNO_NOPROTOOPT; } if (SOCK_DGRAM == type) { socktype = SOCKET_DGRAM; } else if (SOCK_STREAM == type) { socktype = SOCKET_STREAM; } else { return __WASI_ERRNO_NOPROTOOPT; } error = __wasi_sock_open(poolfd, af, socktype, &sockfd); HANDLE_ERROR(error) return sockfd; } int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen) { __wasi_addr_t wasi_addr; __wasi_errno_t error; memset(&wasi_addr, 0, sizeof(wasi_addr)); error = __wasi_sock_addr_local(sockfd, &wasi_addr); HANDLE_ERROR(error) error = wasi_addr_to_sockaddr(&wasi_addr, addr, addrlen); HANDLE_ERROR(error) return __WASI_ERRNO_SUCCESS; } int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen) { __wasi_addr_t wasi_addr; __wasi_errno_t error; memset(&wasi_addr, 0, sizeof(wasi_addr)); error = __wasi_sock_addr_remote(sockfd, &wasi_addr); HANDLE_ERROR(error) error = wasi_addr_to_sockaddr(&wasi_addr, addr, addrlen); HANDLE_ERROR(error) return __WASI_ERRNO_SUCCESS; } struct aibuf { struct addrinfo ai; union sa { struct sockaddr_in sin; struct sockaddr_in6 sin6; } sa; }; static __wasi_errno_t addrinfo_hints_to_wasi_hints(const struct addrinfo *hints, __wasi_addr_info_hints_t *wasi_hints) { if (hints) { wasi_hints->hints_enabled = 1; switch (hints->ai_family) { case AF_INET: wasi_hints->family = INET4; break; case AF_INET6: wasi_hints->family = INET6; break; default: return __WASI_ERRNO_AFNOSUPPORT; } switch (hints->ai_socktype) { case SOCK_STREAM: wasi_hints->type = SOCKET_STREAM; break; case SOCK_DGRAM: wasi_hints->type = SOCKET_DGRAM; break; default: return __WASI_ERRNO_NOTSUP; } if (hints->ai_protocol != 0) { return __WASI_ERRNO_NOTSUP; } if (hints->ai_flags != 0) { return __WASI_ERRNO_NOTSUP; } } else { wasi_hints->hints_enabled = 0; } return __WASI_ERRNO_SUCCESS; } static __wasi_errno_t wasi_addr_info_to_addr_info(const __wasi_addr_info_t *addr_info, struct addrinfo *ai) { ai->ai_socktype = addr_info->type == SOCKET_DGRAM ? SOCK_DGRAM : SOCK_STREAM; ai->ai_protocol = 0; ai->ai_canonname = NULL; if (addr_info->addr.kind == IPv4) { ai->ai_family = AF_INET; ai->ai_addrlen = sizeof(struct sockaddr_in); } else { ai->ai_family = AF_INET6; ai->ai_addrlen = sizeof(struct sockaddr_in6); } return wasi_addr_to_sockaddr(&addr_info->addr, ai->ai_addr, &ai->ai_addrlen); // TODO err handling } int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res) { __wasi_addr_info_hints_t wasi_hints; __wasi_addr_info_t *addr_info = NULL; __wasi_size_t addr_info_size, i; __wasi_size_t max_info_size = 16; __wasi_errno_t error; struct aibuf *aibuf_res; error = addrinfo_hints_to_wasi_hints(hints, &wasi_hints); HANDLE_ERROR(error) do { if (addr_info) free(addr_info); addr_info_size = max_info_size; addr_info = (__wasi_addr_info_t *)malloc(addr_info_size * sizeof(__wasi_addr_info_t)); if (!addr_info) { HANDLE_ERROR(__WASI_ERRNO_NOMEM) } error = __wasi_sock_addr_resolve(node, service == NULL ? "" : service, &wasi_hints, addr_info, addr_info_size, &max_info_size); if (error != __WASI_ERRNO_SUCCESS) { free(addr_info); HANDLE_ERROR(error); } } while (max_info_size > addr_info_size); if (addr_info_size == 0) { free(addr_info); *res = NULL; return __WASI_ERRNO_SUCCESS; } aibuf_res = (struct aibuf *)calloc(1, addr_info_size * sizeof(struct aibuf)); if (!aibuf_res) { free(addr_info); HANDLE_ERROR(__WASI_ERRNO_NOMEM) } *res = &aibuf_res[0].ai; if (addr_info_size) { addr_info_size = max_info_size; } for (i = 0; i < addr_info_size; i++) { struct addrinfo *ai = &aibuf_res[i].ai; ai->ai_addr = (struct sockaddr *)&aibuf_res[i].sa; error = wasi_addr_info_to_addr_info(&addr_info[i], ai); if (error != __WASI_ERRNO_SUCCESS) { free(addr_info); free(aibuf_res); HANDLE_ERROR(error) } ai->ai_next = i == addr_info_size - 1 ? NULL : &aibuf_res[i + 1].ai; } free(addr_info); return __WASI_ERRNO_SUCCESS; } void freeaddrinfo(struct addrinfo *res) { /* res is a pointer to a first field in the first element * of aibuf array allocated in getaddrinfo, therefore this call * frees the memory of the entire array. */ free(res); } static struct timeval time_us_to_timeval(uint64_t time_us) { struct timeval tv; tv.tv_sec = time_us / 1000000UL; tv.tv_usec = time_us % 1000000UL; return tv; } static uint64_t timeval_to_time_us(struct timeval tv) { return (tv.tv_sec * 1000000UL) + tv.tv_usec; } static int get_sol_socket_option(int sockfd, int optname, void *__restrict optval, socklen_t *__restrict optlen) { __wasi_errno_t error; uint64_t timeout_us; bool is_linger_enabled; int linger_s; switch (optname) { case SO_RCVTIMEO: assert(*optlen == sizeof(struct timeval)); error = __wasi_sock_get_recv_timeout(sockfd, &timeout_us); HANDLE_ERROR(error); *(struct timeval *)optval = time_us_to_timeval(timeout_us); return error; case SO_SNDTIMEO: assert(*optlen == sizeof(struct timeval)); error = __wasi_sock_get_send_timeout(sockfd, &timeout_us); HANDLE_ERROR(error); *(struct timeval *)optval = time_us_to_timeval(timeout_us); return error; case SO_SNDBUF: assert(*optlen == sizeof(int)); error = __wasi_sock_get_send_buf_size(sockfd, (size_t *)optval); HANDLE_ERROR(error); return error; case SO_RCVBUF: assert(*optlen == sizeof(int)); error = __wasi_sock_get_recv_buf_size(sockfd, (size_t *)optval); HANDLE_ERROR(error); return error; case SO_KEEPALIVE: assert(*optlen == sizeof(int)); error = __wasi_sock_get_keep_alive(sockfd, (bool *)optval); HANDLE_ERROR(error); return error; case SO_REUSEADDR: assert(*optlen == sizeof(int)); error = __wasi_sock_get_reuse_addr(sockfd, (bool *)optval); HANDLE_ERROR(error); return error; case SO_REUSEPORT: assert(*optlen == sizeof(int)); error = __wasi_sock_get_reuse_port(sockfd, (bool *)optval); HANDLE_ERROR(error); return error; case SO_LINGER: assert(*optlen == sizeof(struct linger)); error = __wasi_sock_get_linger(sockfd, &is_linger_enabled, &linger_s); HANDLE_ERROR(error); ((struct linger *)optval)->l_onoff = (int)is_linger_enabled; ((struct linger *)optval)->l_linger = linger_s; return error; case SO_BROADCAST: assert(*optlen == sizeof(int)); error = __wasi_sock_get_broadcast(sockfd, (bool *)optval); HANDLE_ERROR(error); return error; default: error = __WASI_ERRNO_NOTSUP; HANDLE_ERROR(error); return error; } } static int get_ipproto_tcp_option(int sockfd, int optname, void *__restrict optval, socklen_t *__restrict optlen) { __wasi_errno_t error; switch (optname) { case TCP_KEEPIDLE: assert(*optlen == sizeof(uint32_t)); error = __wasi_sock_get_tcp_keep_idle(sockfd, (uint32_t *)optval); HANDLE_ERROR(error); return error; case TCP_KEEPINTVL: assert(*optlen == sizeof(uint32_t)); error = __wasi_sock_get_tcp_keep_intvl(sockfd, (uint32_t *)optval); HANDLE_ERROR(error); return error; case TCP_FASTOPEN_CONNECT: assert(*optlen == sizeof(int)); error = __wasi_sock_get_tcp_fastopen_connect(sockfd, (bool *)optval); HANDLE_ERROR(error); return error; case TCP_NODELAY: assert(*optlen == sizeof(int)); error = __wasi_sock_get_tcp_no_delay(sockfd, (bool *)optval); HANDLE_ERROR(error); return error; case TCP_QUICKACK: assert(*optlen == sizeof(int)); error = __wasi_sock_get_tcp_quick_ack(sockfd, (bool *)optval); HANDLE_ERROR(error); return error; default: error = __WASI_ERRNO_NOTSUP; HANDLE_ERROR(error); return error; } } static int get_ipproto_ip_option(int sockfd, int optname, void *__restrict optval, socklen_t *__restrict optlen) { __wasi_errno_t error; switch (optname) { case IP_MULTICAST_LOOP: assert(*optlen == sizeof(int)); error = __wasi_sock_get_ip_multicast_loop(sockfd, false, (bool *)optval); HANDLE_ERROR(error); return error; case IP_TTL: 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(int)); error = __wasi_sock_get_ip_multicast_ttl(sockfd, (uint8_t *)optval); HANDLE_ERROR(error); return error; default: error = __WASI_ERRNO_NOTSUP; HANDLE_ERROR(error); return error; } } static int get_ipproto_ipv6_option(int sockfd, int optname, void *__restrict optval, socklen_t *__restrict optlen) { __wasi_errno_t error; switch (optname) { case IPV6_V6ONLY: assert(*optlen == sizeof(int)); error = __wasi_sock_get_ipv6_only(sockfd, (bool *)optval); HANDLE_ERROR(error); return error; case IPV6_MULTICAST_LOOP: assert(*optlen == sizeof(int)); error = __wasi_sock_get_ip_multicast_loop(sockfd, true, (bool *)optval); HANDLE_ERROR(error); return error; default: error = __WASI_ERRNO_NOTSUP; HANDLE_ERROR(error); return error; } } int getsockopt(int sockfd, int level, int optname, void *__restrict optval, socklen_t *__restrict optlen) { __wasi_errno_t error; switch (level) { case SOL_SOCKET: return get_sol_socket_option(sockfd, optname, optval, optlen); case IPPROTO_TCP: return get_ipproto_tcp_option(sockfd, optname, optval, optlen); case IPPROTO_IP: return get_ipproto_ip_option(sockfd, optname, optval, optlen); case IPPROTO_IPV6: return get_ipproto_ipv6_option(sockfd, optname, optval, optlen); default: error = __WASI_ERRNO_NOTSUP; HANDLE_ERROR(error); return error; } } static int set_sol_socket_option(int sockfd, int optname, const void *optval, socklen_t optlen) { __wasi_errno_t error; uint64_t timeout_us; switch (optname) { case SO_RCVTIMEO: { assert(optlen == sizeof(struct timeval)); timeout_us = timeval_to_time_us(*(struct timeval *)optval); error = __wasi_sock_set_recv_timeout(sockfd, timeout_us); HANDLE_ERROR(error); return error; } case SO_SNDTIMEO: { assert(optlen == sizeof(struct timeval)); timeout_us = timeval_to_time_us(*(struct timeval *)optval); error = __wasi_sock_set_send_timeout(sockfd, timeout_us); HANDLE_ERROR(error); return error; } case SO_SNDBUF: { assert(optlen == sizeof(int)); error = __wasi_sock_set_send_buf_size(sockfd, *(size_t *)optval); HANDLE_ERROR(error); return error; } case SO_RCVBUF: { assert(optlen == sizeof(int)); error = __wasi_sock_set_recv_buf_size(sockfd, *(size_t *)optval); HANDLE_ERROR(error); return error; } case SO_KEEPALIVE: { assert(optlen == sizeof(int)); error = __wasi_sock_set_keep_alive(sockfd, *(bool *)optval); HANDLE_ERROR(error); return error; } case SO_REUSEADDR: { assert(optlen == sizeof(int)); error = __wasi_sock_set_reuse_addr(sockfd, *(bool *)optval); HANDLE_ERROR(error); return error; } case SO_REUSEPORT: { assert(optlen == sizeof(int)); error = __wasi_sock_set_reuse_port(sockfd, *(bool *)optval); HANDLE_ERROR(error); return error; } case SO_LINGER: { assert(optlen == sizeof(struct linger)); struct linger *linger_opt = ((struct linger *)optval); error = __wasi_sock_set_linger(sockfd, (bool)linger_opt->l_onoff, linger_opt->l_linger); HANDLE_ERROR(error); return error; } case SO_BROADCAST: { assert(optlen == sizeof(int)); error = __wasi_sock_set_broadcast(sockfd, *(bool *)optval); HANDLE_ERROR(error); return error; } default: { error = __WASI_ERRNO_NOTSUP; HANDLE_ERROR(error); return error; } } } static int set_ipproto_tcp_option(int sockfd, int optname, const void *optval, socklen_t optlen) { __wasi_errno_t error; switch (optname) { case TCP_NODELAY: assert(optlen == sizeof(int)); error = __wasi_sock_set_tcp_no_delay(sockfd, *(bool *)optval); HANDLE_ERROR(error); return error; case TCP_KEEPIDLE: assert(optlen == sizeof(uint32_t)); error = __wasi_sock_set_tcp_keep_idle(sockfd, *(uint32_t *)optval); HANDLE_ERROR(error); return error; case TCP_KEEPINTVL: assert(optlen == sizeof(uint32_t)); error = __wasi_sock_set_tcp_keep_intvl(sockfd, *(uint32_t *)optval); HANDLE_ERROR(error); return error; case TCP_FASTOPEN_CONNECT: assert(optlen == sizeof(int)); error = __wasi_sock_set_tcp_fastopen_connect(sockfd, *(bool *)optval); HANDLE_ERROR(error); return error; case TCP_QUICKACK: assert(optlen == sizeof(int)); error = __wasi_sock_set_tcp_quick_ack(sockfd, *(bool *)optval); HANDLE_ERROR(error); return error; default: error = __WASI_ERRNO_NOTSUP; HANDLE_ERROR(error); return error; } } static int set_ipproto_ip_option(int sockfd, int optname, const void *optval, socklen_t optlen) { __wasi_errno_t error; __wasi_addr_ip_t imr_multiaddr; struct ip_mreq *ip_mreq_opt; switch (optname) { case IP_MULTICAST_LOOP: assert(optlen == sizeof(int)); error = __wasi_sock_set_ip_multicast_loop(sockfd, false, *(bool *)optval); HANDLE_ERROR(error); return error; case IP_ADD_MEMBERSHIP: assert(optlen == sizeof(struct ip_mreq)); ip_mreq_opt = (struct ip_mreq *)optval; imr_multiaddr.kind = IPv4; ipv4_addr_to_wasi_ip4_addr(ip_mreq_opt->imr_multiaddr.s_addr, &imr_multiaddr.addr.ip4); error = __wasi_sock_set_ip_add_membership( sockfd, &imr_multiaddr, ip_mreq_opt->imr_interface.s_addr); HANDLE_ERROR(error); return error; case IP_DROP_MEMBERSHIP: assert(optlen == sizeof(struct ip_mreq)); ip_mreq_opt = (struct ip_mreq *)optval; imr_multiaddr.kind = IPv4; ipv4_addr_to_wasi_ip4_addr(ip_mreq_opt->imr_multiaddr.s_addr, &imr_multiaddr.addr.ip4); error = __wasi_sock_set_ip_drop_membership( sockfd, &imr_multiaddr, ip_mreq_opt->imr_interface.s_addr); HANDLE_ERROR(error); return error; case IP_TTL: 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(int)); error = __wasi_sock_set_ip_multicast_ttl(sockfd, *(uint8_t *)optval); HANDLE_ERROR(error); return error; default: error = __WASI_ERRNO_NOTSUP; HANDLE_ERROR(error); return error; } } static int set_ipproto_ipv6_option(int sockfd, int optname, const void *optval, socklen_t optlen) { __wasi_errno_t error; struct ipv6_mreq *ipv6_mreq_opt; __wasi_addr_ip_t imr_multiaddr; switch (optname) { case IPV6_V6ONLY: assert(optlen == sizeof(int)); error = __wasi_sock_set_ipv6_only(sockfd, *(bool *)optval); HANDLE_ERROR(error); return error; case IPV6_MULTICAST_LOOP: assert(optlen == sizeof(int)); error = __wasi_sock_set_ip_multicast_loop(sockfd, true, *(bool *)optval); HANDLE_ERROR(error); return error; case IPV6_JOIN_GROUP: assert(optlen == sizeof(struct ipv6_mreq)); ipv6_mreq_opt = (struct ipv6_mreq *)optval; imr_multiaddr.kind = IPv6; ipv6_addr_to_wasi_ipv6_addr( (uint16_t *)ipv6_mreq_opt->ipv6mr_multiaddr.s6_addr, &imr_multiaddr.addr.ip6); error = __wasi_sock_set_ip_add_membership( sockfd, &imr_multiaddr, ipv6_mreq_opt->ipv6mr_interface); HANDLE_ERROR(error); return error; case IPV6_LEAVE_GROUP: assert(optlen == sizeof(struct ipv6_mreq)); ipv6_mreq_opt = (struct ipv6_mreq *)optval; imr_multiaddr.kind = IPv6; ipv6_addr_to_wasi_ipv6_addr( (uint16_t *)ipv6_mreq_opt->ipv6mr_multiaddr.s6_addr, &imr_multiaddr.addr.ip6); error = __wasi_sock_set_ip_drop_membership( sockfd, &imr_multiaddr, ipv6_mreq_opt->ipv6mr_interface); HANDLE_ERROR(error); return error; default: error = __WASI_ERRNO_NOTSUP; HANDLE_ERROR(error); return error; } } int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen) { __wasi_errno_t error; switch (level) { case SOL_SOCKET: return set_sol_socket_option(sockfd, optname, optval, optlen); case IPPROTO_TCP: return set_ipproto_tcp_option(sockfd, optname, optval, optlen); case IPPROTO_IP: return set_ipproto_ip_option(sockfd, optname, optval, optlen); case IPPROTO_IPV6: return set_ipproto_ipv6_option(sockfd, optname, optval, optlen); default: error = __WASI_ERRNO_NOTSUP; HANDLE_ERROR(error); return error; } }