From cdaab9f05c061efad8d8d4bceb0c9a9abcde8531 Mon Sep 17 00:00:00 2001 From: lucas Date: Fri, 29 Mar 2024 10:55:59 +0100 Subject: [PATCH] Import Base Code --- core/shared/platform/zephyr/zephyr_errno.h | 302 ++++++ core/shared/platform/zephyr/zephyr_socket.c | 992 ++++++++++++++++++++ 2 files changed, 1294 insertions(+) create mode 100644 core/shared/platform/zephyr/zephyr_errno.h create mode 100644 core/shared/platform/zephyr/zephyr_socket.c diff --git a/core/shared/platform/zephyr/zephyr_errno.h b/core/shared/platform/zephyr/zephyr_errno.h new file mode 100644 index 000000000..0455b539d --- /dev/null +++ b/core/shared/platform/zephyr/zephyr_errno.h @@ -0,0 +1,302 @@ +#ifndef ZEPHYR_ERRNO_H +#define ZEPHYR_ERRNO_H + +#include "platform_wasi_types.h" +#include + + // Add your custom code here +static inline __wasi_errno_t +zephyr_to_wasi_errno(int zephyr_errno) { + switch (zephyr_errno) { + case EPERM: + return __WASI_EPERM; // Operation not permitted + case ENOENT: + return __WASI_ENOENT; // No such file or directory + case ESRCH: + return __WASI_ESRCH; // No such process + case EINTR: + return __WASI_EINTR; // Interrupted system call + case EIO: + return __WASI_EIO; // I/O error + case ENXIO: + return __WASI_ENXIO; // No such device or address + case E2BIG: + return __WASI_E2BIG; // Argument list too long + case ENOEXEC: + return __WASI_ENOEXEC; // Exec format error + case EBADF: + return __WASI_EBADF; // Bad file descriptor + case ECHILD: + return __WASI_ECHILD; // No child processes + case EAGAIN: + return __WASI_EAGAIN; // Try again + case ENOMEM: + return __WASI_ENOMEM; // Out of memory + case EACCES: + return __WASI_EACCES; // Permission denied + case EFAULT: + return __WASI_EFAULT; // Bad address + case EBUSY: + return __WASI_EBUSY; // Device or resource busy + case EEXIST: + return __WASI_EEXIST; // File exists + case EXDEV: + return __WASI_EXDEV; // Cross-device link + case ENODEV: + return __WASI_ENODEV; // No such device + case ENOTDIR: + return __WASI_ENOTDIR; // Not a directory + case EISDIR: + return __WASI_EISDIR; // Is a directory + case EINVAL: + return __WASI_EINVAL; // Invalid argument + case ENFILE: + return __WASI_ENFILE; // File table overflow + case EMFILE: + return __WASI_EMFILE; // Too many open files + case ENOTTY: + return __WASI_ENOTTY; // Not a typewriter + case EFBIG: + return __WASI_EFBIG; // File too large + case ENOSPC: + return __WASI_ENOSPC; // No space left on device + case EROFS: + return __WASI_EROFS; // Read-only file system + case EMLINK: + return __WASI_EMLINK; // Too many links + case EPIPE: + return __WASI_EPIPE; // Broken pipe + case EDOM: + return __WASI_EDOM; // Math argument out of domain of func + case ERANGE: + return __WASI_ERANGE; // Math result not representable + case ENOMSG: + return __WASI_ENOMSG; // No message of desired type + case EDEADLK: + return __WASI_EDEADLK; // Resource deadlock would occur + case ENOLCK: + return __WASI_ENOLCK; // No record locks available + case ENOSYS: + return __WASI_ENOSYS; // Function not implemented + case ENOTEMPTY: + return __WASI_ENOTEMPTY; // Directory not empty + case ENAMETOOLONG: + return __WASI_ENAMETOOLONG; // File name too long + case ELOOP: + return __WASI_ELOOP; // Too many symbolic links encountered + case EOPNOTSUPP: + return __WASI_ENOTSUP; // Operation not supported on transport endpoint + case EPFNOSUPPORT: + return __WASI_EAFNOSUPPORT; // Protocol family not supported + case ECONNRESET: + return __WASI_ECONNRESET; // Connection reset by peer + case ENOBUFS: + return __WASI_ENOBUFS; // No buffer space available + case EAFNOSUPPORT: + return __WASI_EAFNOSUPPORT; // Address family not supported by protocol + case EPROTOTYPE: + return __WASI_EPROTOTYPE; // Protocol wrong type for socket + case ENOTSOCK: + return __WASI_ENOTSOCK; // Socket operation on non-socket + case ENOPROTOOPT: + return __WASI_ENOPROTOOPT; // Protocol not available + case ESHUTDOWN: + return __WASI_ECANCELED; // Cannot send after transport endpoint shutdown + case ECONNREFUSED: + return __WASI_ECONNREFUSED; // Connection refused + case EADDRINUSE: + return __WASI_EADDRINUSE; // Address already in use + case ECONNABORTED: + return __WASI_ECONNABORTED; // Connection aborted + case ENETUNREACH: + return __WASI_ENETUNREACH; // Network is unreachable + case ENETDOWN: + return __WASI_ENETDOWN; // Network is down + case ETIMEDOUT: + return __WASI_ETIMEDOUT; // Connection timed out + case EHOSTDOWN: + return __WASI_ENETDOWN; // Host is down + case EHOSTUNREACH: + return __WASI_EHOSTUNREACH; // No route to host + case EINPROGRESS: + return __WASI_EINPROGRESS; // Operation now in progress + case EALREADY: + return __WASI_EALREADY; // Operation already in progress + case EDESTADDRREQ: + return __WASI_EDESTADDRREQ; // Destination address required + case EMSGSIZE: + return __WASI_EMSGSIZE; // Message too long + case EPROTONOSUPPORT: + return __WASI_EPROTONOSUPPORT; // Protocol not supported + case ESOCKTNOSUPPORT: + // return __WASI_ESOCKTNOSUPPORT; // Socket type not supported + case EADDRNOTAVAIL: + return __WASI_EADDRNOTAVAIL; // Cannot assign requested address + case ENETRESET: + return __WASI_ENETRESET; // Network dropped connection because of reset + case EISCONN: + return __WASI_EISCONN; // Transport endpoint is already connected + case ENOTCONN: + return __WASI_ENOTCONN; // Transport endpoint is not connected + case ETOOMANYREFS: + return __WASI_ENOTRECOVERABLE; // Too many references: cannot splice + case ENOTSUP: + return __WASI_ENOTSUP; // Operation not supported + case EILSEQ: + return __WASI_EILSEQ; // Illegal byte sequence + case EOVERFLOW: + return __WASI_EOVERFLOW; // Value too large for defined data type + case ECANCELED: + return __WASI_ECANCELED; // Operation canceled + default: + return __WASI_ENOSYS; // Function not implemented + } +} + + +static inline int +wasi_to_zephyr_errno(__wasi_errno_t wasi_errno) { + switch (wasi_errno) { + case __WASI_EPERM: + return EPERM; + case __WASI_ENOENT: + return ENOENT; + case __WASI_ESRCH: + return ESRCH; + case __WASI_EINTR: + return EINTR; + case __WASI_EIO: + return EIO; + case __WASI_ENXIO: + return ENXIO; + case __WASI_E2BIG: + return E2BIG; + case __WASI_ENOEXEC: + return ENOEXEC; + case __WASI_EBADF: + return EBADF; + case __WASI_ECHILD: + return ECHILD; + case __WASI_EAGAIN: + return EAGAIN; + case __WASI_ENOMEM: + return ENOMEM; + case __WASI_EACCES: + return EACCES; + case __WASI_EFAULT: + return EFAULT; + case __WASI_EBUSY: + return EBUSY; + case __WASI_EEXIST: + return EEXIST; + case __WASI_EXDEV: + return EXDEV; + case __WASI_ENODEV: + return ENODEV; + case __WASI_ENOTDIR: + return ENOTDIR; + case __WASI_EISDIR: + return EISDIR; + case __WASI_EINVAL: + return EINVAL; + case __WASI_ENFILE: + return ENFILE; + case __WASI_EMFILE: + return EMFILE; + case __WASI_ENOTTY: + return ENOTTY; + case __WASI_EFBIG: + return EFBIG; + case __WASI_ENOSPC: + return ENOSPC; + case __WASI_EROFS: + return EROFS; + case __WASI_EMLINK: + return EMLINK; + case __WASI_EPIPE: + return EPIPE; + case __WASI_EDOM: + return EDOM; + case __WASI_ERANGE: + return ERANGE; + case __WASI_ENOMSG: + return ENOMSG; + case __WASI_EDEADLK: + return EDEADLK; + case __WASI_ENOLCK: + return ENOLCK; + case __WASI_ENOSYS: + return ENOSYS; + case __WASI_ENOTEMPTY: + return ENOTEMPTY; + case __WASI_ENAMETOOLONG: + return ENAMETOOLONG; + case __WASI_ELOOP: + return ELOOP; + //case __WASI_ENOTSUPP : + // return EOPNOTSUPP; + case __WASI_EAFNOSUPPORT: + return EPFNOSUPPORT; + case __WASI_ECONNRESET: + return ECONNRESET; + case __WASI_ENOBUFS: + return ENOBUFS; + case __WASI_EPROTOTYPE: + return EPROTOTYPE; + case __WASI_ENOTSOCK: + return ENOTSOCK; + case __WASI_ENOPROTOOPT: + return ENOPROTOOPT; + //case __WASI_ECANCELED: + // return ESHUTDOWN; + case __WASI_ECONNREFUSED: + return ECONNREFUSED; + case __WASI_EADDRINUSE: + return EADDRINUSE; + case __WASI_ECONNABORTED: + return ECONNABORTED; + case __WASI_ENETUNREACH: + return ENETUNREACH; + case __WASI_ENETDOWN: + return ENETDOWN; + case __WASI_ETIMEDOUT: + return ETIMEDOUT; + // case __WASI_EHOSTDOWN: + // return EHOSTDOWN; + case __WASI_EHOSTUNREACH: + return EHOSTUNREACH; + case __WASI_EINPROGRESS: + return EINPROGRESS; + case __WASI_EALREADY: + return EALREADY; + case __WASI_EDESTADDRREQ: + return EDESTADDRREQ; + case __WASI_EMSGSIZE: + return EMSGSIZE; + case __WASI_EPROTONOSUPPORT: + return EPROTONOSUPPORT; + // case __WASI_ESOCKTNOSUPPORT: + // return ESOCKTNOSUPPORT; + case __WASI_EADDRNOTAVAIL: + return EADDRNOTAVAIL; + case __WASI_ENETRESET: + return ENETRESET; + case __WASI_EISCONN: + return EISCONN; + case __WASI_ENOTCONN: + return ENOTCONN; + case __WASI_ENOTRECOVERABLE: + return ETOOMANYREFS; + case __WASI_ENOTSUP: + return ENOTSUP; + case __WASI_EILSEQ: + return EILSEQ; + case __WASI_EOVERFLOW: + return EOVERFLOW; + case __WASI_ECANCELED: + return ECANCELED; + default: + return ENOSYS; + } +} +#endif /* ZEPHYR_ERRNO_H */ diff --git a/core/shared/platform/zephyr/zephyr_socket.c b/core/shared/platform/zephyr/zephyr_socket.c new file mode 100644 index 000000000..2739ecba7 --- /dev/null +++ b/core/shared/platform/zephyr/zephyr_socket.c @@ -0,0 +1,992 @@ +#include "platform_api_extension.h" +//#include "platform_wasi_types.h" + +#include +#include +#include +#include "zephyr_errno.h" + +// Static functions +static bool +textual_addr_to_sockaddr(const char *textual, int port, struct sockaddr *out, + socklen_t *out_len) +{ + struct sockaddr_in *v4; +#ifdef IPPROTO_IPV6 + struct sockaddr_in *v6; +#endif + + assert(textual); + + v4 = (struct sockaddr_in *)out; + if (inet_pton(AF_INET, textual, &v4->sin_addr.s_addr) == 1) { + v4->sin_family = AF_INET; + v4->sin_port = htons(port); + *out_len = sizeof(struct sockaddr_in); + return true; + } + +#ifdef IPPROTO_IPV6 + v6 = (struct sockaddr_in *)out; + if (inet_pton(AF_INET6, textual, &v6->sin6_addr.s6_addr) == 1) { + v6->sin6_family = AF_INET6; + v6->sin6_port = htons(port); + *out_len = sizeof(struct sockaddr_in); + return true; + } +#endif + + return false; +} + +static int +sockaddr_to_bh_sockaddr(const struct sockaddr *sockaddr, + bh_sockaddr_t *bh_sockaddr) +{ + switch (sockaddr->sa_family) { + case AF_INET: + { + struct sockaddr_in *addr = (struct sockaddr_in *)sockaddr; + + bh_sockaddr->port = ntohs(addr->sin_port); + bh_sockaddr->addr_buffer.ipv4 = ntohl(addr->sin_addr.s_addr); + bh_sockaddr->is_ipv4 = true; + return BHT_OK; + } +#ifdef IPPROTO_IPV6 + case AF_INET6: + { + struct sockaddr_in *addr = (struct sockaddr_in *)sockaddr; + size_t i; + + bh_sockaddr->port = ntohs(addr->sin6_port); + + for (i = 0; i < sizeof(bh_sockaddr->addr_buffer.ipv6) + / sizeof(bh_sockaddr->addr_buffer.ipv6[0]); + i++) { + uint16 part_addr = addr->sin6_addr.s6_addr[i * 2] + | (addr->sin6_addr.s6_addr[i * 2 + 1] << 8); + bh_sockaddr->addr_buffer.ipv6[i] = ntohs(part_addr); + } + + bh_sockaddr->is_ipv4 = false; + return BHT_OK; + } +#endif + default: + errno = EAFNOSUPPORT; + return BHT_ERROR; + } +} + +static void +bh_sockaddr_to_sockaddr(const bh_sockaddr_t *bh_sockaddr, + struct sockaddr_storage *sockaddr, socklen_t *socklen) +{ + if (bh_sockaddr->is_ipv4) { + struct sockaddr_in *addr = (struct sockaddr_in *)sockaddr; + addr->sin_port = htons(bh_sockaddr->port); + addr->sin_family = AF_INET; + addr->sin_addr.s_addr = htonl(bh_sockaddr->addr_buffer.ipv4); + *socklen = sizeof(*addr); + } +#ifdef IPPROTO_IPV6 + else { + struct sockaddr_in6 *addr = (struct sockaddr_in6 *)sockaddr; + size_t i; + addr->sin6_port = htons(bh_sockaddr->port); + addr->sin6_family = AF_INET6; + + for (i = 0; i < sizeof(bh_sockaddr->addr_buffer.ipv6) + / sizeof(bh_sockaddr->addr_buffer.ipv6[0]); + i++) { + uint16 part_addr = htons(bh_sockaddr->addr_buffer.ipv6[i]); + addr->sin6_addr.s6_addr[i * 2] = 0xff & part_addr; + addr->sin6_addr.s6_addr[i * 2 + 1] = (0xff00 & part_addr) >> 8; + } + + *socklen = sizeof(*addr); + } +#endif +} + +static int +getaddrinfo_error_to_errno(int error) +{ + switch (error) { + case EAI_AGAIN: + return EAGAIN; + case EAI_FAIL: + return EFAULT; + case EAI_MEMORY: + return ENOMEM; + case EAI_SYSTEM: + return errno; + default: + return EINVAL; + } +} + +static int +is_addrinfo_supported(struct addrinfo *info) +{ + return + // Allow only IPv4 and IPv6 + (info->ai_family == AF_INET || info->ai_family == AF_INET6) + // Allow only UDP and TCP + && (info->ai_socktype == SOCK_DGRAM || info->ai_socktype == SOCK_STREAM) + && (info->ai_protocol == IPPROTO_TCP + || info->ai_protocol == IPPROTO_UDP); +} + +static int +os_socket_setbooloption(bh_socket_t socket, int level, int optname, + bool is_enabled) +{ + int option = (int)is_enabled; + + if (zsock_setsockopt(socket, level, optname, &option, sizeof(option)) != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +static int +os_socket_getbooloption(bh_socket_t socket, int level, int optname, + bool *is_enabled) +{ + assert(is_enabled); + + int optval; + socklen_t optval_size = sizeof(optval); + + if (zsock_setsockopt(socket, level, optname, &optval, &optval_size) != 0) { + return BHT_ERROR; + } + *is_enabled = (bool)optval; + return BHT_OK; +} + +// Platform API implementation +int +os_socket_create(bh_socket_t *sock, bool is_ipv4, bool is_tcp) +{ + int af = is_ipv4 ? AF_INET : AF_INET6; + + if (!sock) { + return BHT_ERROR; + } + + if (is_tcp) { + *sock = zsock_socket(af, SOCK_STREAM, IPPROTO_TCP); + } + else { + *sock = zsock_socket(af, SOCK_DGRAM, IPPROTO_UDP); // IPPROTO_UDP or 0 ? + } + + return (*sock == -1) ? BHT_ERROR : BHT_OK; +} + +int +os_socket_bind(bh_socket_t socket, const char *host, int *port) +{ + struct sockaddr_storage addr = { 0 }; + socklen_t socklen; + int ret; + + assert(host); + assert(port); + + if (!textual_addr_to_sockaddr(host, *port, (struct sockaddr *)&addr, + &socklen)) { + goto fail; + } + + // F_SETF_SETFD and FD_CLOEXEC are not defined in zephyr. + // SO_LINGER: Socket lingers on close (ignored, for compatibility) + + ret = zsock_bind(socket, (struct sockaddr *)&addr, socklen); + if (ret < 0) { + goto fail; + } + + socklen = sizeof(addr); + if (zsock_getsockname(socket, (void *)&addr, &socklen) == -1) { + goto fail; + } + + if (addr.sin_family == AF_INET) { + *port = ntohs((&addr)->sin_port); + } + else { +#ifdef IPPROTO_IPV6 + *port = ntohs((&addr)->sin6_port); +#else + goto fail; +#endif + } + + return BHT_OK; + +fail: + // Close the fd because FD_CLOEXEC isn't implemented. + os_socket_close(socket); + return BHT_ERROR; +} + +int +os_socket_settimeout(bh_socket_t socket, uint64 timeout_us) +{ + struct timeval timeout = { 0 }; + + timeout.tv_sec = timeout_us / 1000000; + timeout.tv_usec = timeout_us % 1000000; + + return zsock_setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, + sizeof(timeout)); +} + +int +os_socket_listen(bh_socket_t socket, int max_client) +{ + return zsock_listen(socket, max_client) != 0 ? BHT_ERROR : BHT_OK; +} + +int +os_socket_accept(bh_socket_t server_sock, bh_socket_t *sock, void *addr, + unsigned int *addrlen) +{ + *sock = zsock_accept(server_sock, addr, addrlen); + + if (*sock < 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + + +int +os_socket_connect(bh_socket_t socket, const char *addr, int port) +{ + struct sockaddr_storage addr = { 0 }; + socklen_t socklen; + int ret; + + assert(addr); + + if (!textual_addr_to_sockaddr(addr, port, (struct sockaddr *)&addr, + &socklen)) { + return BHT_ERROR; + } + + ret = zsock_connect(socket, (struct sockaddr *)&addr, socklen); + if (ret < 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_recv(bh_socket_t socket, void *buf, unsigned int len) +{ + return zsock_recv(socket, buf, len, 0); +} + +int +os_socket_recv_from(bh_socket_t socket, void *buf, unsigned int len, int flags, + bh_sockaddr_t *src_addr) +{ + struct sockaddr_storage addr = { 0 }; + socklen_t socklen = sizeof(addr); + int ret; + + ret = zsock_recvfrom(socket, buf, len, flags, (struct sockaddr *)&addr, + &socklen); + if (ret < 0) { + return BHT_ERROR; + } + + if (src_addr && socklen > 0) { + if (sockaddr_to_bh_sockaddr((struct sockaddr *)&addr, src_addr) != BHT_OK) { + return BHT_ERROR; + } + } + + return ret; + +} + +int +os_socket_send(bh_socket_t socket, const void *buf, unsigned int len) +{ + return zsock_send(socket, buf, len, 0); +} + +int +os_socket_send_to(bh_socket_t socket, const void *buf, unsigned int len, + int flags, const bh_sockaddr_t *dest_addr) +{ + struct sockaddr_storage addr = { 0 }; + socklen_t socklen; + int ret; + + if (bh_sockaddr_to_sockaddr(dest_addr, (struct sockaddr *)&addr, &socklen) + == BHT_ERROR) { + return -1; + } + + return zsock_sendto(socket, buf, len, flags, (struct sockaddr *)&addr, + socklen); +} + +int +os_socket_close(bh_socket_t socket) +{ + return zsock_close(socket) == -1 ? BHT_ERROR : BHT_OK; +} + +__wasi_errno_t +os_socket_shutdown(bh_socket_t socket) +{ + if (zsock_shutdown(socket, ZSOCK_SHUT_RDWR) == -1) { + return zephyr_to_wasi_errno(errno); + } + return __WASI_ESUCCESS;; +} + +int +os_socket_inet_network(bool is_ipv4, const char *cp, bh_ip_addr_buffer_t *out) +{ + if (!cp) + return BHT_ERROR; + + if (is_ipv4) { + if (zsock_inet_pton(AF_INET, cp, &out->ipv4) != 1) { + return BHT_ERROR; + } + /* Note: ntohl(INADDR_NONE) == INADDR_NONE */ + out->ipv4 = ntohl(out->ipv4); + } + else { +#ifdef IPPROTO_IPV6 + if (zsock_inet_pton(AF_INET6, cp, out->ipv6) != 1) { + return BHT_ERROR; + } + for (int i = 0; i < 8; i++) { + out->ipv6[i] = ntohs(out->ipv6[i]); + } +#else + errno = EAFNOSUPPORT; + return BHT_ERROR; +#endif + } + + return BHT_OK; +} + +int +os_socket_addr_resolve(const char *host, const char *service, + uint8_t *hint_is_tcp, uint8_t *hint_is_ipv4, + bh_addr_info_t *addr_info, size_t addr_info_size, + size_t *max_info_size) +{ + struct zsock_addrinfo hints = { 0 }, *res, *result; + int hints_enabled = hint_is_tcp || hint_is_ipv4; + int ret; + size_t pos = 0; + + if (hints_enabled) { + if (hint_is_ipv4) { + hints.ai_family = *hint_is_ipv4 ? AF_INET : AF_INET6; + } + if (hint_is_tcp) { + hints.ai_socktype = *hint_is_tcp ? SOCK_STREAM : SOCK_DGRAM; + } + } + + ret = zsock_getaddrinfo(host, strlen(service) == 0 ? NULL : service, + hints_enabled ? &hints : NULL, &result); + if (ret != BHT_OK) { + errno = getaddrinfo_error_to_errno(ret); + return BHT_ERROR; + } + + res = result; + + while (res) { + if (addr_info_size > pos) { + if (!is_addrinfo_supported(res)) { + res = res->ai_next; + continue; + } + + ret = + sockaddr_to_bh_sockaddr(res->ai_addr, &addr_info[pos].sockaddr); + + if (ret == BHT_ERROR) { + zsock_freeaddrinfo(result); + return BHT_ERROR; + } + + addr_info[pos].is_tcp = res->ai_socktype == SOCK_STREAM; + } + + pos++; + res = res->ai_next; + } + + *max_info_size = pos; + zsock_freeaddrinfo(result); + + return BHT_OK; +} + +int +os_socket_addr_local(bh_socket_t socket, bh_sockaddr_t *sockaddr) +{ + struct sockaddr_storage addr_storage = { 0 }; + socklen_t addr_len = sizeof(addr_storage); + int ret; + + ret = zsock_getsockname(socket, (struct sockaddr *)&addr_storage, &addr_len); + + if (ret != BHT_OK) { + return BHT_ERROR; + } + + return sockaddr_to_bh_sockaddr((struct sockaddr *)&addr_storage, sockaddr); +} + +int +os_socket_addr_remote(bh_socket_t socket, bh_sockaddr_t *sockaddr) +{ + struct sockaddr_storage addr_storage = { 0 }; + socklen_t addr_len = sizeof(addr_storage); + int ret; + + ret = zsock_getpeername(socket, (struct sockaddr *)&addr_storage, &addr_len); + + if (ret != BHT_OK) { + return BHT_ERROR; + } + + return sockaddr_to_bh_sockaddr((struct sockaddr *)&addr_storage, sockaddr); +} + +int +os_socket_set_send_buf_size(bh_socket_t socket, size_t bufsiz) +{ + int buf_size_int = (int)bufsiz; + + if (zsock_setsockopt(socket, SOL_SOCKET, SO_SNDBUF, &buf_size_int, + sizeof(buf_size_int)) + != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_get_send_buf_size(bh_socket_t socket, size_t *bufsiz) +{ + assert(bufsiz); + + int buf_size_int; + socklen_t bufsiz_len = sizeof(buf_size_int); + + if (zsock_getsockopt(socket, SOL_SOCKET, SO_SNDBUF, &buf_size_int, + &bufsiz_len) + != 0) { + return BHT_ERROR; + } + + *bufsiz = (size_t)buf_size_int; + return BHT_OK; +} + +int +os_socket_set_recv_buf_size(bh_socket_t socket, size_t bufsiz) +{ + int buf_size_int = (int)bufsiz; + + if (zsock_getsockopt(socket, SOL_SOCKET, SO_RCVBUF, &buf_size_int, + sizeof(buf_size_int)) + != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_get_recv_buf_size(bh_socket_t socket, size_t *bufsiz) +{ + assert(bufsiz); + + int buf_size_int; + socklen_t bufsiz_len = sizeof(buf_size_int); + + if (zsock_getsockopt(socket, SOL_SOCKET, SO_RCVBUF, &buf_size_int, &bufsiz_len) + != 0) { + return BHT_ERROR; + } + *bufsiz = (size_t)buf_size_int; + + return BHT_OK; +} + +int +os_socket_set_keep_alive(bh_socket_t socket, bool is_enabled) +{ + return os_socket_setbooloption(socket, SOL_SOCKET, SO_KEEPALIVE, + is_enabled) +} + +int +os_socket_get_keep_alive(bh_socket_t socket, bool *is_enabled) +{ + return os_socket_getbooloption(socket, SOL_SOCKET, SO_KEEPALIVE, + is_enabled) +} + +int +os_socket_set_send_timeout(bh_socket_t socket, uint64 timeout_us) +{ + struct timeval tv; + tv.tv_sec = timeout_us / 1000000UL; + tv.tv_usec = timeout_us % 1000000UL; + + if (zsock_setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) != 0) { + return BHT_ERROR; + } + return BHT_OK; +} + +int +os_socket_get_send_timeout(bh_socket_t socket, uint64 *timeout_us) +{ + struct timeval tv; + socklen_t tv_len = sizeof(tv); + + if (zsock_setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, &tv, &tv_len) != 0) { + return BHT_ERROR; + } + *timeout_us = (tv.tv_sec * 1000000UL) + tv.tv_usec; + + return BHT_OK; +} + +int +os_socket_set_recv_timeout(bh_socket_t socket, uint64 timeout_us) +{ + struct timeval tv; + tv.tv_sec = timeout_us / 1000000UL; + tv.tv_usec = timeout_us % 1000000UL; + + if (zsock_setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_get_recv_timeout(bh_socket_t socket, uint64 *timeout_us) +{ + struct timeval tv; + socklen_t tv_len = sizeof(tv); + + if (zsock_setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &tv, &tv_len) != 0) { + return BHT_ERROR; + } + *timeout_us = (tv.tv_sec * 1000000UL) + tv.tv_usec; + + return BHT_OK; +} + +int +os_socket_set_reuse_addr(bh_socket_t socket, bool is_enabled) +{ + return os_socket_setbooloption(socket, SOL_SOCKET, SO_REUSEADDR, + is_enabled); +} + +int +os_socket_get_reuse_addr(bh_socket_t socket, bool *is_enabled) +{ + return os_socket_getbooloption(socket, SOL_SOCKET, SO_REUSEADDR, + is_enabled); +} + +int +os_socket_set_reuse_port(bh_socket_t socket, bool is_enabled) +{ + return os_socket_setbooloption(socket, SOL_SOCKET, SO_REUSEPORT, + is_enabled); +} + +int +os_socket_get_reuse_port(bh_socket_t socket, bool *is_enabled) +{ + return os_socket_getbooloption(socket, SOL_SOCKET, SO_REUSEPORT, + is_enabled); +} + +// SO_LINGER Socket lingers on close (ignored, for compatibility) +int +os_socket_set_linger(bh_socket_t socket, bool is_enabled, int linger_s) +{ + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_get_linger(bh_socket_t socket, bool *is_enabled, int *linger_s) +{ + errno = ENOSYS; + + return BHT_ERROR; +} + +// TCP_NODELAY Disable TCP buffering (ignored, for compatibility) +int +os_socket_set_tcp_no_delay(bh_socket_t socket, bool is_enabled) +{ + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_get_tcp_no_delay(bh_socket_t socket, bool *is_enabled) +{ + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_set_tcp_quick_ack(bh_socket_t socket, bool is_enabled) +{ + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_get_tcp_quick_ack(bh_socket_t socket, bool *is_enabled) +{ + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_set_tcp_keep_idle(bh_socket_t socket, uint32_t time_s) +{ + int time_s_int = (int)time_s; + +#ifdef TCP_KEEPIDLE + if (zsock_setsockopt(socket, IPPROTO_TCP, TCP_KEEPIDLE, &time_s_int, + sizeof(time_s_int)) + != 0) { + return BHT_ERROR; + } + return BHT_OK; +#elif defined(TCP_KEEPALIVE) + if (zsock_setsockopt(socket, IPPROTO_TCP, TCP_KEEPALIVE, &time_s_int, + sizeof(time_s_int)) + != 0) { + return BHT_ERROR; + } + return BHT_OK; +#else + errno = ENOSYS; + + return BHT_ERROR; +#endif +} + +int +os_socket_get_tcp_keep_idle(bh_socket_t socket, uint32_t *time_s) +{ + assert(time_s); + int time_s_int; + socklen_t time_s_len = sizeof(time_s_int); + +#ifdef TCP_KEEPIDLE + if (getsozsock_setsockoptkopt(socket, IPPROTO_TCP, TCP_KEEPIDLE, &time_s_int, &time_s_len) + != 0) { + return BHT_ERROR; + } + *time_s = (uint32)time_s_int; + + return BHT_OK; +#elif defined(TCP_KEEPALIVE) + if (zsock_setsockopt(socket, IPPROTO_TCP, TCP_KEEPALIVE, &time_s_int, &time_s_len) + != 0) { + return BHT_ERROR; + } + *time_s = (uint32)time_s_int; + + return BHT_OK; +#else + errno = ENOSYS; + + return BHT_ERROR; +#endif +} + +int +os_socket_set_tcp_keep_intvl(bh_socket_t socket, uint32_t time_s) +{ + int time_s_int = (int)time_s; + +#ifdef TCP_KEEPINTVL + if (zsock_setsockopt(socket, IPPROTO_TCP, TCP_KEEPINTVL, &time_s_int, + sizeof(time_s_int)) + != 0) { + return BHT_ERROR; + } + + return BHT_OK; +#else + errno = ENOSYS; + + return BHT_ERROR; +#endif +} + +int +os_socket_get_tcp_keep_intvl(bh_socket_t socket, uint32_t *time_s) +{ +#ifdef TCP_KEEPINTVL + assert(time_s); + int time_s_int; + socklen_t time_s_len = sizeof(time_s_int); + + if (zsock_setsockopt(socket, IPPROTO_TCP, TCP_KEEPINTVL, &time_s_int, &time_s_len) + != 0) { + return BHT_ERROR; + } + *time_s = (uint32)time_s_int; + + return BHT_OK; +#else + errno = ENOSYS; + + return BHT_ERROR; +#endif +} + +int +os_socket_set_tcp_fastopen_connect(bh_socket_t socket, bool is_enabled) +{ + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_get_tcp_fastopen_connect(bh_socket_t socket, bool *is_enabled) +{ + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_set_ip_multicast_loop(bh_socket_t socket, bool ipv6, bool is_enabled) +{ + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_get_ip_multicast_loop(bh_socket_t socket, bool ipv6, + bool *is_enabled) +{ + errno = ENOSYS; + + return BHT_ERROR; + + +} + +int +os_socket_set_ip_add_membership(bh_socket_t socket, + bh_ip_addr_buffer_t *imr_multiaddr, + uint32_t imr_interface, bool is_ipv6) +{ + assert(imr_multiaddr); + + if(is_ipv6){ +#if defined(IPPROTO_IPV6) && !defined(BH_PLATFORM_COSMOPOLITAN) + struct ipv6_mreq mreq; + + for (int i = 0; i < 8; i++) { + ((uint16_t *)mreq.ipv6mr_multiaddr.s6_addr)[i] = + imr_multiaddr->ipv6[i]; + } + mreq.ipv6mr_interface = imr_interface; + + if (setsockopt(socket, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, + sizeof(mreq)) + != 0) { + return BHT_ERROR; + } +#else + errno = EAFNOSUPPORT; + return BHT_ERROR; +#endif + } + else{ + struct ip_mreq mreq; + mreq.imr_multiaddr.s_addr = imr_multiaddr->ipv4; + mreq.imr_interface.s_addr = imr_interface; + + if (setsockopt(socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, + sizeof(mreq)) + != 0) { + return BHT_ERROR; + } + } + return BHT_OK; +} + + +int +os_socket_set_ip_drop_membership(bh_socket_t socket, + bh_ip_addr_buffer_t *imr_multiaddr, + uint32_t imr_interface, bool is_ipv6) +{ + assert(imr_multiaddr); + + if(is_ipv6){ +#if defined(IPPROTO_IPV6) && !defined(BH_PLATFORM_COSMOPOLITAN) + struct ipv6_mreq mreq; + + for (int i = 0; i < 8; i++) { + ((uint16_t *)mreq.ipv6mr_multiaddr.s6_addr)[i] = + imr_multiaddr->ipv6[i]; + } + mreq.ipv6mr_interface = imr_interface; + + if (setsockopt(socket, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq, + sizeof(mreq)) + != 0) { + return BHT_ERROR; + } +#else + errno = EAFNOSUPPORT; + return BHT_ERROR; +#endif + } + else { + struct ip_mreq mreq; + mreq.imr_multiaddr.s_addr = imr_multiaddr->ipv4; + mreq.imr_interface.s_addr = imr_interface; + + if (setsockopt(socket, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, + sizeof(mreq)) + != 0) { + return BHT_ERROR; + } + } +} + +int +os_socket_set_ip_ttl(bh_socket_t socket, uint8_t ttl_s) +{ + if (zsock_setsockopt(socket, IPPROTO_IP, IP_TTL, &ttl_s, sizeof(ttl_s)) != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_get_ip_ttl(bh_socket_t socket, uint8_t *ttl_s) +{ + socklen_t opt_len = sizeof(*ttl_s); + + if (zsock_setsockopt(socket, IPPROTO_IP, IP_MULTICAST_TTL, ttl_s, &opt_len) + != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_set_ip_multicast_ttl(bh_socket_t socket, uint8_t ttl_s) +{ + if (zsock_setsockopt(socket, IPPROTO_IP, IP_MULTICAST_TTL, &ttl_s, sizeof(ttl_s)) != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + + +int +os_socket_get_ip_multicast_ttl(bh_socket_t socket, uint8_t *ttl_s) +{ + socklen_t opt_len = sizeof(*ttl_s); + + if (zsock_setsockopt(socket, IPPROTO_IP, IP_MULTICAST_TTL, ttl_s, &opt_len) + != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + + +int +os_socket_set_ipv6_only(bh_socket_t socket, bool is_enabled) +{ +#ifdef IPPROTO_IPV6 + return os_socket_setbooloption(socket, IPPROTO_IPV6, IPV6_V6ONLY, + is_enabled); +#else + errno = EAFNOSUPPORT; + return BHT_ERROR; +#endif +} + + +int +os_socket_get_ipv6_only(bh_socket_t socket, bool *is_enabled) +{ +#ifdef IPPROTO_IPV6 + return os_socket_getbooloption(socket, IPPROTO_IPV6, IPV6_V6ONLY, + is_enabled); +#else + errno = EAFNOSUPPORT; + return BHT_ERROR; +#endif +} + + +int +os_socket_set_broadcast(bh_socket_t socket, bool is_enabled) +{ + return os_socket_setbooloption(socket, SOL_SOCKET, SO_BROADCAST, + is_enabled); +} + + +int +os_socket_get_broadcast(bh_socket_t socket, bool *is_enabled) +{ + return os_socket_getbooloption(socket, SOL_SOCKET, SO_BROADCAST, + is_enabled); +} +