/* * Copyright (C) 2019 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #include "platform_api_vmcore.h" #include "platform_api_extension.h" #ifndef SGX_DISABLE_WASI #include "libc_errno.h" #define TRACE_OCALL_FAIL() os_printf("ocall %s failed!\n", __FUNCTION__) /** OCALLs prototypes **/ int ocall_accept(int *p_ret, int sockfd, void *addr, uint32_t *addrlen, uint32_t addr_size); int ocall_bind(int *p_ret, int sockfd, const void *addr, uint32_t addrlen); int ocall_close(int *p_ret, int fd); int ocall_connect(int *p_ret, int sockfd, void *addr, uint32_t addrlen); int ocall_fcntl_long(int *p_ret, int fd, int cmd, long arg); int ocall_getsockname(int *p_ret, int sockfd, void *addr, uint32_t *addrlen, uint32_t addr_size); int ocall_getpeername(int *p_ret, int sockfd, void *addr, uint32_t *addrlen, uint32_t addr_size); int ocall_getsockopt(int *p_ret, int sockfd, int level, int optname, void *val_buf, unsigned int val_buf_size, void *len_buf); int ocall_listen(int *p_ret, int sockfd, int backlog); int ocall_recv(int *p_ret, int sockfd, void *buf, size_t len, int flags); int ocall_recvfrom(ssize_t *p_ret, int sockfd, void *buf, size_t len, int flags, void *src_addr, uint32_t *addrlen, uint32_t addr_size); int ocall_recvmsg(ssize_t *p_ret, int sockfd, void *msg_buf, unsigned int msg_buf_size, int flags); int ocall_send(int *p_ret, int sockfd, const void *buf, size_t len, int flags); int ocall_sendto(ssize_t *p_ret, int sockfd, const void *buf, size_t len, int flags, void *dest_addr, uint32_t addrlen); int ocall_sendmsg(ssize_t *p_ret, int sockfd, void *msg_buf, unsigned int msg_buf_size, int flags); int ocall_setsockopt(int *p_ret, int sockfd, int level, int optname, void *optval, unsigned int optlen); int ocall_shutdown(int *p_ret, int sockfd, int how); int ocall_socket(int *p_ret, int domain, int type, int protocol); /** OCALLs prototypes end **/ /** In-enclave implementation of POSIX functions **/ static bool is_little_endian() { long i = 0x01020304; unsigned char *c = (unsigned char *)&i; return (*c == 0x04) ? true : false; } static void swap32(uint8 *pData) { uint8 value = *pData; *pData = *(pData + 3); *(pData + 3) = value; value = *(pData + 1); *(pData + 1) = *(pData + 2); *(pData + 2) = value; } static void swap16(uint8 *pData) { uint8 value = *pData; *(pData) = *(pData + 1); *(pData + 1) = value; } uint32 htonl(uint32 value) { uint32 ret; if (is_little_endian()) { ret = value; swap32((uint8 *)&ret); return ret; } return value; } uint32 ntohl(uint32 value) { return htonl(value); } uint16 htons(uint16 value) { uint16 ret; if (is_little_endian()) { ret = value; swap16((uint8 *)&ret); return ret; } return value; } static uint16 ntohs(uint16 value) { return htons(value); } /* Coming from musl, under MIT license */ static int hexval(unsigned c) { if (c - '0' < 10) return c - '0'; c |= 32; if (c - 'a' < 6) return c - 'a' + 10; return -1; } /* Coming from musl, under MIT license */ static int inet_pton(int af, const char *restrict s, void *restrict a0) { uint16_t ip[8]; unsigned char *a = a0; int i, j, v, d, brk = -1, need_v4 = 0; if (af == AF_INET) { for (i = 0; i < 4; i++) { for (v = j = 0; j < 3 && isdigit(s[j]); j++) v = 10 * v + s[j] - '0'; if (j == 0 || (j > 1 && s[0] == '0') || v > 255) return 0; a[i] = v; if (s[j] == 0 && i == 3) return 1; if (s[j] != '.') return 0; s += j + 1; } return 0; } else if (af != AF_INET6) { errno = EAFNOSUPPORT; return -1; } if (*s == ':' && *++s != ':') return 0; for (i = 0;; i++) { if (s[0] == ':' && brk < 0) { brk = i; ip[i & 7] = 0; if (!*++s) break; if (i == 7) return 0; continue; } for (v = j = 0; j < 4 && (d = hexval(s[j])) >= 0; j++) v = 16 * v + d; if (j == 0) return 0; ip[i & 7] = v; if (!s[j] && (brk >= 0 || i == 7)) break; if (i == 7) return 0; if (s[j] != ':') { if (s[j] != '.' || (i < 6 && brk < 0)) return 0; need_v4 = 1; i++; break; } s += j + 1; } if (brk >= 0) { memmove(ip + brk + 7 - i, ip + brk, 2 * (i + 1 - brk)); for (j = 0; j < 7 - i; j++) ip[brk + j] = 0; } for (j = 0; j < 8; j++) { *a++ = ip[j] >> 8; *a++ = ip[j]; } if (need_v4 && inet_pton(AF_INET, (void *)s, a - 4) <= 0) return 0; return 1; } static int inet_addr(const char *p) { struct in_addr a; if (!inet_pton(AF_INET, p, &a)) return -1; return a.s_addr; } /** In-enclave implementation of POSIX functions end **/ static int textual_addr_to_sockaddr(const char *textual, int port, struct sockaddr_in *out) { assert(textual); out->sin_family = AF_INET; out->sin_port = htons(port); out->sin_addr.s_addr = inet_addr(textual); return BHT_OK; } static int sockaddr_to_bh_sockaddr(const struct sockaddr *sockaddr, socklen_t socklen, bh_sockaddr_t *bh_sockaddr) { switch (sockaddr->sa_family) { case AF_INET: { struct sockaddr_in *addr = (struct sockaddr_in *)sockaddr; assert(socklen >= sizeof(struct sockaddr_in)); 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; } default: errno = EAFNOSUPPORT; return BHT_ERROR; } } static int bh_sockaddr_to_sockaddr(const bh_sockaddr_t *bh_sockaddr, struct sockaddr *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); return BHT_OK; } else { errno = EAFNOSUPPORT; return BHT_ERROR; } } static int os_socket_setbooloption(bh_socket_t socket, int level, int optname, bool is_enabled) { int option = (int)is_enabled; int ret; if (ocall_setsockopt(&ret, socket, level, optname, &option, sizeof(option)) != SGX_SUCCESS) { TRACE_OCALL_FAIL(); return BHT_ERROR; } if (ret != 0) { errno = get_errno(); 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); int ret; if (ocall_getsockopt(&ret, socket, level, optname, &optval, optval_size, &optval_size) != SGX_SUCCESS) { TRACE_OCALL_FAIL(); return BHT_ERROR; } if (ret != 0) { errno = get_errno(); return BHT_ERROR; } *is_enabled = (bool)optval; return BHT_OK; } int socket(int domain, int type, int protocol) { int ret; if (ocall_socket(&ret, domain, type, protocol) != SGX_SUCCESS) { TRACE_OCALL_FAIL(); return -1; } if (ret == -1) errno = get_errno(); return ret; } int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen) { int ret; unsigned int val_buf_size = *optlen; if (ocall_getsockopt(&ret, sockfd, level, optname, optval, val_buf_size, (void *)optlen) != SGX_SUCCESS) { TRACE_OCALL_FAIL(); return -1; } if (ret == -1) errno = get_errno(); return ret; } int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen) { int ret; if (ocall_setsockopt(&ret, sockfd, level, optname, (void *)optval, optlen) != SGX_SUCCESS) { TRACE_OCALL_FAIL(); return -1; } if (ret == -1) errno = get_errno(); return ret; } ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags) { ssize_t ret; int i; char *p; struct msghdr *msg1; uint64 total_size = sizeof(struct msghdr) + (uint64)msg->msg_namelen + (uint64)msg->msg_controllen; total_size += sizeof(struct iovec) * (msg->msg_iovlen); for (i = 0; i < msg->msg_iovlen; i++) { total_size += msg->msg_iov[i].iov_len; } if (total_size >= UINT32_MAX) return -1; msg1 = BH_MALLOC((uint32)total_size); if (msg1 == NULL) return -1; p = (char *)(uintptr_t)sizeof(struct msghdr); if (msg->msg_name != NULL) { msg1->msg_name = p; memcpy((uintptr_t)p + (char *)msg1, msg->msg_name, (size_t)msg->msg_namelen); p += msg->msg_namelen; } if (msg->msg_control != NULL) { msg1->msg_control = p; memcpy((uintptr_t)p + (char *)msg1, msg->msg_control, (size_t)msg->msg_control); p += msg->msg_controllen; } if (msg->msg_iov != NULL) { msg1->msg_iov = (struct iovec *)p; p += (uintptr_t)(sizeof(struct iovec) * (msg->msg_iovlen)); for (i = 0; i < msg->msg_iovlen; i++) { msg1->msg_iov[i].iov_base = p; msg1->msg_iov[i].iov_len = msg->msg_iov[i].iov_len; memcpy((uintptr_t)p + (char *)msg1, msg->msg_iov[i].iov_base, (size_t)(msg->msg_iov[i].iov_len)); p += msg->msg_iov[i].iov_len; } } if (ocall_sendmsg(&ret, sockfd, (void *)msg1, (uint32)total_size, flags) != SGX_SUCCESS) { TRACE_OCALL_FAIL(); return -1; } if (ret == -1) errno = get_errno(); return ret; } ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags) { ssize_t ret; int i; char *p; struct msghdr *msg1; uint64 total_size = sizeof(struct msghdr) + (uint64)msg->msg_namelen + (uint64)msg->msg_controllen; total_size += sizeof(struct iovec) * (msg->msg_iovlen); for (i = 0; i < msg->msg_iovlen; i++) { total_size += msg->msg_iov[i].iov_len; } if (total_size >= UINT32_MAX) return -1; msg1 = BH_MALLOC((uint32)total_size); if (msg1 == NULL) return -1; memset(msg1, 0, total_size); p = (char *)(uintptr_t)sizeof(struct msghdr); if (msg->msg_name != NULL) { msg1->msg_name = p; p += msg->msg_namelen; } if (msg->msg_control != NULL) { msg1->msg_control = p; p += msg->msg_controllen; } if (msg->msg_iov != NULL) { msg1->msg_iov = (struct iovec *)p; p += (uintptr_t)(sizeof(struct iovec) * (msg->msg_iovlen)); for (i = 0; i < msg->msg_iovlen; i++) { msg1->msg_iov[i].iov_base = p; msg1->msg_iov[i].iov_len = msg->msg_iov[i].iov_len; p += msg->msg_iov[i].iov_len; } } if (ocall_recvmsg(&ret, sockfd, (void *)msg1, (uint32)total_size, flags) != SGX_SUCCESS) { TRACE_OCALL_FAIL(); return -1; } p = (char *)(uintptr_t)(sizeof(struct msghdr)); if (msg1->msg_name != NULL) { memcpy(msg->msg_name, (uintptr_t)p + (char *)msg1, (size_t)msg1->msg_namelen); p += msg1->msg_namelen; } if (msg1->msg_control != NULL) { memcpy(msg->msg_control, (uintptr_t)p + (char *)msg1, (size_t)msg1->msg_control); p += msg->msg_controllen; } if (msg1->msg_iov != NULL) { p += (uintptr_t)(sizeof(struct iovec) * (msg1->msg_iovlen)); for (i = 0; i < msg1->msg_iovlen; i++) { memcpy(msg->msg_iov[i].iov_base, (uintptr_t)p + (char *)msg1, (size_t)(msg1->msg_iov[i].iov_len)); p += msg1->msg_iov[i].iov_len; } } if (ret == -1) errno = get_errno(); return ret; } int shutdown(int sockfd, int how) { int ret; if (ocall_shutdown(&ret, sockfd, how) != SGX_SUCCESS) { TRACE_OCALL_FAIL(); return -1; } if (ret == -1) errno = get_errno(); return ret; } int os_socket_accept(bh_socket_t server_sock, bh_socket_t *sock, void *addr, unsigned int *addrlen) { struct sockaddr addr_tmp; unsigned int len = sizeof(struct sockaddr); if (ocall_accept(sock, server_sock, &addr_tmp, &len, len) != SGX_SUCCESS) { TRACE_OCALL_FAIL(); return -1; } if (*sock < 0) { errno = get_errno(); return BHT_ERROR; } return BHT_OK; } int os_socket_bind(bh_socket_t socket, const char *host, int *port) { struct sockaddr_in addr; struct linger ling; unsigned int socklen; int ret; assert(host); assert(port); ling.l_onoff = 1; ling.l_linger = 0; if (ocall_fcntl_long(&ret, socket, F_SETFD, FD_CLOEXEC) != SGX_SUCCESS) { TRACE_OCALL_FAIL(); return -1; } if (ret < 0) { goto fail; } if (ocall_setsockopt(&ret, socket, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling)) != SGX_SUCCESS) { TRACE_OCALL_FAIL(); return -1; } if (ret < 0) { goto fail; } addr.sin_addr.s_addr = inet_addr(host); addr.sin_port = htons(*port); addr.sin_family = AF_INET; if (ocall_bind(&ret, socket, &addr, sizeof(addr)) != SGX_SUCCESS) { TRACE_OCALL_FAIL(); return -1; } if (ret < 0) { goto fail; } socklen = sizeof(addr); if (ocall_getsockname(&ret, socket, (void *)&addr, &socklen, socklen) != SGX_SUCCESS) { TRACE_OCALL_FAIL(); return -1; } if (ret == -1) { goto fail; } *port = ntohs(addr.sin_port); return BHT_OK; fail: errno = get_errno(); return BHT_ERROR; } int os_socket_close(bh_socket_t socket) { int ret; if (ocall_close(&ret, socket) != SGX_SUCCESS) { TRACE_OCALL_FAIL(); return -1; } if (ret == -1) errno = get_errno(); return ret; } int os_socket_connect(bh_socket_t socket, const char *addr, int port) { struct sockaddr_in addr_in = { 0 }; socklen_t addr_len = sizeof(struct sockaddr_in); int ret = 0; if ((ret = textual_addr_to_sockaddr(addr, port, &addr_in)) < 0) { return ret; } if (ocall_connect(&ret, socket, &addr_in, addr_len) != SGX_SUCCESS) { TRACE_OCALL_FAIL(); return -1; } if (ret == -1) errno = get_errno(); return ret; } int os_socket_create(bh_socket_t *sock, bool is_ipv4, bool is_tcp) { int af; if (!sock) { return BHT_ERROR; } if (is_ipv4) { af = AF_INET; } else { errno = ENOSYS; return BHT_ERROR; } if (is_tcp) { if (ocall_socket(sock, af, SOCK_STREAM, IPPROTO_TCP) != SGX_SUCCESS) { TRACE_OCALL_FAIL(); return -1; } } else { if (ocall_socket(sock, af, SOCK_DGRAM, 0) != SGX_SUCCESS) { TRACE_OCALL_FAIL(); return -1; } } if (*sock == -1) { errno = get_errno(); return BHT_ERROR; } return BHT_OK; } 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 (inet_pton(AF_INET, cp, &out->ipv4) != 1) { return BHT_ERROR; } /* Note: ntohl(INADDR_NONE) == INADDR_NONE */ out->ipv4 = ntohl(out->ipv4); } else { if (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]); } } return BHT_OK; } int os_socket_listen(bh_socket_t socket, int max_client) { int ret; if (ocall_listen(&ret, socket, max_client) != SGX_SUCCESS) { TRACE_OCALL_FAIL(); return -1; } if (ret == -1) errno = get_errno(); return ret; } int os_socket_recv(bh_socket_t socket, void *buf, unsigned int len) { int ret; if (ocall_recv(&ret, socket, buf, len, 0) != SGX_SUCCESS) { TRACE_OCALL_FAIL(); errno = ENOSYS; return -1; } if (ret == -1) errno = get_errno(); return ret; } int os_socket_recv_from(bh_socket_t socket, void *buf, unsigned int len, int flags, bh_sockaddr_t *src_addr) { struct sockaddr_in addr; socklen_t addr_len = sizeof(addr); ssize_t ret; if (ocall_recvfrom(&ret, socket, buf, len, flags, &addr, &addr_len, addr_len) != SGX_SUCCESS) { TRACE_OCALL_FAIL(); errno = ENOSYS; return -1; } if (ret < 0) { errno = get_errno(); return ret; } if (src_addr && addr_len > 0) { if (sockaddr_to_bh_sockaddr((struct sockaddr *)&addr, addr_len, src_addr) == BHT_ERROR) { return -1; } } return ret; } int os_socket_send(bh_socket_t socket, const void *buf, unsigned int len) { int ret; if (ocall_send(&ret, socket, buf, len, 0) != SGX_SUCCESS) { TRACE_OCALL_FAIL(); errno = ENOSYS; return -1; } if (ret == -1) errno = get_errno(); return ret; } 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_in addr; socklen_t addr_len; ssize_t ret; if (bh_sockaddr_to_sockaddr(dest_addr, (struct sockaddr *)&addr, &addr_len) == BHT_ERROR) { return -1; } if (ocall_sendto(&ret, socket, buf, len, flags, (struct sockaddr *)&addr, addr_len) != SGX_SUCCESS) { TRACE_OCALL_FAIL(); errno = ENOSYS; return -1; } if (ret == -1) { errno = get_errno(); } return ret; } __wasi_errno_t os_socket_shutdown(bh_socket_t socket) { if (shutdown(socket, O_RDWR) != 0) { return convert_errno(errno); } return __WASI_ESUCCESS; } 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) { errno = ENOSYS; return BHT_ERROR; } int os_socket_addr_local(bh_socket_t socket, bh_sockaddr_t *sockaddr) { struct sockaddr_in addr; socklen_t addr_len = sizeof(addr); int ret; if (ocall_getsockname(&ret, socket, (struct sockaddr *)&addr, &addr_len, addr_len) != SGX_SUCCESS) { TRACE_OCALL_FAIL(); return BHT_ERROR; } if (ret != BHT_OK) { errno = get_errno(); return BHT_ERROR; } return sockaddr_to_bh_sockaddr((struct sockaddr *)&addr, addr_len, sockaddr); } int os_socket_addr_remote(bh_socket_t socket, bh_sockaddr_t *sockaddr) { struct sockaddr_in addr; socklen_t addr_len = sizeof(addr); int ret; if (ocall_getpeername(&ret, socket, (void *)&addr, &addr_len, addr_len) != SGX_SUCCESS) { TRACE_OCALL_FAIL(); return -1; } if (ret != BHT_OK) { errno = get_errno(); return BHT_ERROR; } return sockaddr_to_bh_sockaddr((struct sockaddr *)&addr, addr_len, sockaddr); } int os_socket_set_send_timeout(bh_socket_t socket, uint64 timeout_us) { errno = ENOSYS; return BHT_ERROR; } int os_socket_get_send_timeout(bh_socket_t socket, uint64 *timeout_us) { errno = ENOSYS; return BHT_ERROR; } int os_socket_set_recv_timeout(bh_socket_t socket, uint64 timeout_us) { errno = ENOSYS; return BHT_ERROR; } int os_socket_get_recv_timeout(bh_socket_t socket, uint64 *timeout_us) { errno = ENOSYS; return BHT_ERROR; } int os_socket_set_send_buf_size(bh_socket_t socket, size_t bufsiz) { errno = ENOSYS; return BHT_ERROR; } int os_socket_get_send_buf_size(bh_socket_t socket, size_t *bufsiz) { errno = ENOSYS; return BHT_ERROR; } int os_socket_set_recv_buf_size(bh_socket_t socket, size_t bufsiz) { errno = ENOSYS; return BHT_ERROR; } int os_socket_get_recv_buf_size(bh_socket_t socket, size_t *bufsiz) { errno = ENOSYS; return BHT_ERROR; } 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_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); } 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; } int os_socket_set_tcp_no_delay(bh_socket_t socket, bool is_enabled) { return os_socket_setbooloption(socket, IPPROTO_TCP, TCP_NODELAY, is_enabled); } int os_socket_get_tcp_no_delay(bh_socket_t socket, bool *is_enabled) { return os_socket_getbooloption(socket, IPPROTO_TCP, TCP_NODELAY, is_enabled); } int os_socket_set_tcp_quick_ack(bh_socket_t socket, bool is_enabled) { return os_socket_setbooloption(socket, IPPROTO_TCP, TCP_QUICKACK, is_enabled); } int os_socket_get_tcp_quick_ack(bh_socket_t socket, bool *is_enabled) { return os_socket_getbooloption(socket, IPPROTO_TCP, TCP_QUICKACK, is_enabled); } int os_socket_set_tcp_keep_idle(bh_socket_t socket, uint32 time_s) { errno = ENOSYS; return BHT_ERROR; } int os_socket_get_tcp_keep_idle(bh_socket_t socket, uint32 *time_s) { errno = ENOSYS; return BHT_ERROR; } int os_socket_set_tcp_keep_intvl(bh_socket_t socket, uint32 time_s) { errno = ENOSYS; return BHT_ERROR; } int os_socket_get_tcp_keep_intvl(bh_socket_t socket, uint32 *time_s) { errno = ENOSYS; return BHT_ERROR; } int os_socket_set_tcp_fastopen_connect(bh_socket_t socket, bool is_enabled) { return os_socket_setbooloption(socket, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, is_enabled); } int os_socket_get_tcp_fastopen_connect(bh_socket_t socket, bool *is_enabled) { return os_socket_getbooloption(socket, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, is_enabled); } int os_socket_set_ip_multicast_loop(bh_socket_t socket, bool ipv6, bool is_enabled) { if (ipv6) { return os_socket_setbooloption(socket, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, is_enabled); } else { return os_socket_setbooloption(socket, IPPROTO_IP, IP_MULTICAST_LOOP, is_enabled); } } int os_socket_get_ip_multicast_loop(bh_socket_t socket, bool ipv6, bool *is_enabled) { if (ipv6) { return os_socket_getbooloption(socket, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, is_enabled); } else { return os_socket_getbooloption(socket, IPPROTO_IP, IP_MULTICAST_LOOP, is_enabled); } } 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) { errno = ENOSYS; return BHT_ERROR; } 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) { errno = ENOSYS; return BHT_ERROR; } int os_socket_set_ip_ttl(bh_socket_t socket, uint8_t ttl_s) { errno = ENOSYS; return BHT_ERROR; } int os_socket_get_ip_ttl(bh_socket_t socket, uint8_t *ttl_s) { errno = ENOSYS; return BHT_ERROR; } int os_socket_set_ip_multicast_ttl(bh_socket_t socket, uint8_t ttl_s) { errno = ENOSYS; return BHT_ERROR; } int os_socket_get_ip_multicast_ttl(bh_socket_t socket, uint8_t *ttl_s) { errno = ENOSYS; return BHT_ERROR; } int os_socket_set_ipv6_only(bh_socket_t socket, bool is_enabled) { return os_socket_setbooloption(socket, IPPROTO_IPV6, IPV6_V6ONLY, is_enabled); } int os_socket_get_ipv6_only(bh_socket_t socket, bool *is_enabled) { return os_socket_getbooloption(socket, IPPROTO_IPV6, IPV6_V6ONLY, is_enabled); } 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); } #endif