Merge dev/socket into main (#1393)

Implement more socket APIs, refer to #1336 and below PRs:
- Implement wasi_addr_resolve function (#1319)
- Fix socket-api byte order issue when host/network order are the same (#1327)
- Enhance sock_addr_local syscall (#1320)
- Implement sock_addr_remote syscall (#1360)
- Add support for IPv6 in WAMR (#1411)
- Implement ns lookup allowlist (#1420)
- Implement sock_send_to and sock_recv_from system calls (#1457)
- Added http downloader and multicast socket options (#1467)
- Fix `bind()` calls to receive the correct size of `sockaddr` structure (#1490)
- Assert on correct parameters (#1505)
- Copy only received bytes from socket recv buffer into the app buffer (#1497)

Co-authored-by: Marcin Kolny <mkolny@amazon.com>
Co-authored-by: Marcin Kolny <marcin.kolny@gmail.com>
Co-authored-by: Callum Macmillan <callumimacmillan@gmail.com>
This commit is contained in:
Wenyong Huang
2022-09-22 21:46:14 +08:00
committed by GitHub
parent 32d2d16908
commit 78b5c5b484
31 changed files with 6345 additions and 438 deletions

View File

@ -7,29 +7,123 @@
#include "platform_api_extension.h"
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/tcp.h>
#include <netinet/in.h>
static void
textual_addr_to_sockaddr(const char *textual, int port, struct sockaddr_in *out)
static bool
textual_addr_to_sockaddr(const char *textual, int port, struct sockaddr *out,
socklen_t *out_len)
{
struct sockaddr_in *v4;
struct sockaddr_in6 *v6;
assert(textual);
out->sin_family = AF_INET;
out->sin_port = htons(port);
out->sin_addr.s_addr = inet_addr(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;
}
v6 = (struct sockaddr_in6 *)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_in6);
return true;
}
return false;
}
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_bufer.ipv4 = ntohl(addr->sin_addr.s_addr);
bh_sockaddr->is_ipv4 = true;
return BHT_OK;
}
case AF_INET6:
{
struct sockaddr_in6 *addr = (struct sockaddr_in6 *)sockaddr;
size_t i;
assert(socklen >= sizeof(struct sockaddr_in6));
bh_sockaddr->port = ntohs(addr->sin6_port);
for (i = 0; i < sizeof(bh_sockaddr->addr_bufer.ipv6)
/ sizeof(bh_sockaddr->addr_bufer.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_bufer.ipv6[i] = ntohs(part_addr);
}
bh_sockaddr->is_ipv4 = false;
return BHT_OK;
}
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_bufer.ipv4);
*socklen = sizeof(*addr);
}
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_bufer.ipv6)
/ sizeof(bh_sockaddr->addr_bufer.ipv6[0]);
i++) {
uint16 part_addr = htons(bh_sockaddr->addr_bufer.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);
}
}
int
os_socket_create(bh_socket_t *sock, int tcp_or_udp)
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 (1 == tcp_or_udp) {
*sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (is_tcp) {
*sock = socket(af, SOCK_STREAM, IPPROTO_TCP);
}
else if (0 == tcp_or_udp) {
*sock = socket(AF_INET, SOCK_DGRAM, 0);
else {
*sock = socket(af, SOCK_DGRAM, 0);
}
return (*sock == -1) ? BHT_ERROR : BHT_OK;
@ -38,7 +132,7 @@ os_socket_create(bh_socket_t *sock, int tcp_or_udp)
int
os_socket_bind(bh_socket_t socket, const char *host, int *port)
{
struct sockaddr_in addr;
struct sockaddr_storage addr = { 0 };
struct linger ling;
socklen_t socklen;
int ret;
@ -59,11 +153,12 @@ os_socket_bind(bh_socket_t socket, const char *host, int *port)
goto fail;
}
addr.sin_addr.s_addr = inet_addr(host);
addr.sin_port = htons(*port);
addr.sin_family = AF_INET;
if (!textual_addr_to_sockaddr(host, *port, (struct sockaddr *)&addr,
&socklen)) {
goto fail;
}
ret = bind(socket, (struct sockaddr *)&addr, sizeof(addr));
ret = bind(socket, (struct sockaddr *)&addr, socklen);
if (ret < 0) {
goto fail;
}
@ -73,7 +168,9 @@ os_socket_bind(bh_socket_t socket, const char *host, int *port)
goto fail;
}
*port = ntohs(addr.sin_port);
*port = ntohs(addr.ss_family == AF_INET
? ((struct sockaddr_in *)&addr)->sin_port
: ((struct sockaddr_in6 *)&addr)->sin6_port);
return BHT_OK;
@ -114,7 +211,7 @@ os_socket_accept(bh_socket_t server_sock, bh_socket_t *sock, void *addr,
struct sockaddr addr_tmp;
unsigned int len = sizeof(struct sockaddr);
*sock = accept(server_sock, (struct sockaddr *)&addr_tmp, &len);
*sock = accept(server_sock, &addr_tmp, &len);
if (*sock < 0) {
return BHT_ERROR;
@ -126,11 +223,14 @@ os_socket_accept(bh_socket_t server_sock, bh_socket_t *sock, void *addr,
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);
struct sockaddr_storage addr_in = { 0 };
socklen_t addr_len;
int ret = 0;
textual_addr_to_sockaddr(addr, port, &addr_in);
if (!textual_addr_to_sockaddr(addr, port, (struct sockaddr *)&addr_in,
&addr_len)) {
return BHT_ERROR;
}
ret = connect(socket, (struct sockaddr *)&addr_in, addr_len);
if (ret == -1) {
@ -146,12 +246,48 @@ os_socket_recv(bh_socket_t socket, void *buf, unsigned int len)
return 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 sock_addr = { 0 };
socklen_t socklen = sizeof(sock_addr);
int ret;
ret = recvfrom(socket, buf, len, flags, (struct sockaddr *)&sock_addr,
&socklen);
if (ret < 0) {
return ret;
}
if (src_addr) {
sockaddr_to_bh_sockaddr((struct sockaddr *)&sock_addr, socklen,
src_addr);
}
return ret;
}
int
os_socket_send(bh_socket_t socket, const void *buf, unsigned int len)
{
return 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 sock_addr = { 0 };
socklen_t socklen = 0;
bh_sockaddr_to_sockaddr(dest_addr, &sock_addr, &socklen);
return sendto(socket, buf, len, 0, (const struct sockaddr *)&sock_addr,
socklen);
}
int
os_socket_close(bh_socket_t socket)
{
@ -167,12 +303,668 @@ os_socket_shutdown(bh_socket_t socket)
}
int
os_socket_inet_network(const char *cp, uint32 *out)
os_socket_inet_network(bool is_ipv4, const char *cp, bh_ip_addr_buffer_t *out)
{
if (!cp)
return BHT_ERROR;
/* Note: ntohl(INADDR_NONE) == INADDR_NONE */
*out = ntohl(inet_addr(cp));
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;
}
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);
}
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 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 = 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;
}
sockaddr_to_bh_sockaddr(res->ai_addr, sizeof(struct sockaddr_in),
&addr_info[pos].sockaddr);
addr_info[pos].is_tcp = res->ai_socktype == SOCK_STREAM;
}
pos++;
res = res->ai_next;
}
*max_info_size = pos;
freeaddrinfo(result);
return BHT_OK;
}
static int
os_socket_setbooloption(bh_socket_t socket, int level, int optname,
bool is_enabled)
{
int option = (int)is_enabled;
if (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;
int optval_size = sizeof(optval);
if (getsockopt(socket, level, optname, &optval, &optval_size) != 0) {
return BHT_ERROR;
}
*is_enabled = (bool)optval;
return BHT_OK;
}
int
os_socket_set_send_buf_size(bh_socket_t socket, size_t bufsiz)
{
int buf_size_int = (int)bufsiz;
if (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 (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 (setsockopt(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 (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_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)
{
struct linger linger_opts = { .l_onoff = (int)is_enabled,
.l_linger = linger_s };
if (setsockopt(socket, SOL_SOCKET, SO_LINGER, &linger_opts,
sizeof(linger_opts))
!= 0) {
return BHT_ERROR;
}
return BHT_OK;
}
int
os_socket_get_linger(bh_socket_t socket, bool *is_enabled, int *linger_s)
{
assert(is_enabled);
assert(linger_s);
struct linger linger_opts;
socklen_t linger_opts_len = sizeof(linger_opts);
if (getsockopt(socket, SOL_SOCKET, SO_LINGER, &linger_opts,
&linger_opts_len)
!= 0) {
return BHT_ERROR;
}
*linger_s = linger_opts.l_linger;
*is_enabled = (bool)linger_opts.l_onoff;
return BHT_OK;
}
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)
{
#ifdef TCP_QUICKACK
return os_socket_setbooloption(socket, IPPROTO_TCP, TCP_QUICKACK,
is_enabled);
#else
errno = ENOSYS;
return BHT_ERROR;
#endif
}
int
os_socket_get_tcp_quick_ack(bh_socket_t socket, bool *is_enabled)
{
#ifdef TCP_QUICKACK
return os_socket_getbooloption(socket, IPPROTO_TCP, TCP_QUICKACK,
is_enabled);
#else
errno = ENOSYS;
return BHT_ERROR;
#endif
}
int
os_socket_set_tcp_keep_idle(bh_socket_t socket, uint32 time_s)
{
int time_s_int = (int)time_s;
#ifdef TCP_KEEPIDLE
if (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 (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 *time_s)
{
assert(time_s);
int time_s_int;
socklen_t time_s_len = sizeof(time_s_int);
#ifdef TCP_KEEPIDLE
if (getsockopt(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 (getsockopt(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 time_s)
{
int time_s_int = (int)time_s;
#ifdef TCP_KEEPINTVL
if (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 *time_s)
{
#ifdef TCP_KEEPINTVL
assert(time_s);
int time_s_int;
socklen_t time_s_len = sizeof(time_s_int);
if (getsockopt(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)
{
#ifdef TCP_FASTOPEN_CONNECT
return os_socket_setbooloption(socket, IPPROTO_TCP, TCP_FASTOPEN_CONNECT,
is_enabled);
#else
errno = ENOSYS;
return BHT_ERROR;
#endif
}
int
os_socket_get_tcp_fastopen_connect(bh_socket_t socket, bool *is_enabled)
{
#ifdef TCP_FASTOPEN_CONNECT
return os_socket_getbooloption(socket, IPPROTO_TCP, TCP_FASTOPEN_CONNECT,
is_enabled);
#else
errno = ENOSYS;
return BHT_ERROR;
#endif
}
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)
{
assert(imr_multiaddr);
if (is_ipv6) {
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_JOIN_GROUP, &mreq,
sizeof(mreq))
!= 0) {
return BHT_ERROR;
}
}
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) {
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_LEAVE_GROUP, &mreq,
sizeof(mreq))
!= 0) {
return BHT_ERROR;
}
}
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;
}
}
return BHT_OK;
}
int
os_socket_set_ip_ttl(bh_socket_t socket, uint8_t ttl_s)
{
if (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 (getsockopt(socket, IPPROTO_IP, IP_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 (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 (getsockopt(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)
{
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);
}
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 (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 (getsockopt(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 (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 (getsockopt(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_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 = getsockname(socket, (struct sockaddr *)&addr_storage, &addr_len);
if (ret != BHT_OK) {
return BHT_ERROR;
}
return sockaddr_to_bh_sockaddr((struct sockaddr *)&addr_storage, addr_len,
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 = getpeername(socket, (struct sockaddr *)&addr_storage, &addr_len);
if (ret != BHT_OK) {
return BHT_ERROR;
}
return sockaddr_to_bh_sockaddr((struct sockaddr *)&addr_storage, addr_len,
sockaddr);
}