Implement getaddrinfo in wasi_socket_ext.c (#1413)

So getaddrinfo() can be used when compiling wasm app of C programs.
This commit is contained in:
Marcin Kolny 2022-09-02 15:11:58 +02:00 committed by GitHub
parent 9a04c21075
commit 3c4e980c9c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 246 additions and 2 deletions

View File

@ -94,6 +94,17 @@ typedef struct __wasi_addr_info_hints_t {
* <sys/types.h> * <sys/types.h>
*/ */
struct addrinfo {
int ai_flags; /* Input flags. */
int ai_family; /* Protocol family for socket. */
int ai_socktype; /* Socket type. */
int ai_protocol; /* Protocol for socket. */
socklen_t ai_addrlen; /* Length of socket address. */
struct sockaddr *ai_addr; /* Socket address for socket. */
char *ai_canonname; /* Canonical name for service location. */
struct addrinfo *ai_next; /* Pointer to next in list. */
};
int int
accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
@ -120,6 +131,13 @@ getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
int int
getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen); getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
int
getaddrinfo(const char *node, const char *service, const struct addrinfo *hints,
struct addrinfo **res);
void
freeaddrinfo(struct addrinfo *res);
#endif #endif
/** /**

View File

@ -317,3 +317,156 @@ getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
return __WASI_ERRNO_SUCCESS; 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 = 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);
}

View File

@ -256,14 +256,15 @@ os_socket_addr_resolve(const char *host, const char *service,
if (hints_enabled) { if (hints_enabled) {
if (hint_is_ipv4) { if (hint_is_ipv4) {
hints.ai_family = *hint_is_ipv4 ? PF_INET : PF_INET6; hints.ai_family = *hint_is_ipv4 ? AF_INET : AF_INET6;
} }
if (hint_is_tcp) { if (hint_is_tcp) {
hints.ai_socktype = *hint_is_tcp ? SOCK_STREAM : SOCK_DGRAM; hints.ai_socktype = *hint_is_tcp ? SOCK_STREAM : SOCK_DGRAM;
} }
} }
ret = getaddrinfo(host, service, hints_enabled ? &hints : NULL, &result); ret = getaddrinfo(host, strlen(service) == 0 ? NULL : service,
hints_enabled ? &hints : NULL, &result);
if (ret != BHT_OK) { if (ret != BHT_OK) {
errno = getaddrinfo_error_to_errno(ret); errno = getaddrinfo_error_to_errno(ret);
return BHT_ERROR; return BHT_ERROR;

View File

@ -100,6 +100,8 @@ add_executable(tcp_client ${CMAKE_CURRENT_SOURCE_DIR}/wasm-src/tcp_client.c)
add_executable(send_recv ${CMAKE_CURRENT_SOURCE_DIR}/wasm-src/send_recv.c) add_executable(send_recv ${CMAKE_CURRENT_SOURCE_DIR}/wasm-src/send_recv.c)
target_link_libraries(send_recv pthread) target_link_libraries(send_recv pthread)
add_executable(addr_resolve ${CMAKE_CURRENT_SOURCE_DIR}/wasm-src/addr_resolve.c)
############################################ ############################################
## Build iwasm with wasi and pthread support ## Build iwasm with wasi and pthread support
############################################ ############################################

View File

@ -79,3 +79,4 @@ endfunction()
compile_with_clang(tcp_server.c) compile_with_clang(tcp_server.c)
compile_with_clang(tcp_client.c) compile_with_clang(tcp_client.c)
compile_with_clang(send_recv.c) compile_with_clang(send_recv.c)
compile_with_clang(addr_resolve.c)

View File

@ -0,0 +1,69 @@
/*
* Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef __wasi__
#include <wasi_socket_ext.h>
#else
#include <netdb.h>
#endif
int
lookup_host(const char *host)
{
struct addrinfo hints, *res, *result;
int errcode;
char addrstr[100];
void *ptr;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
errcode = getaddrinfo(host, NULL, &hints, &result);
if (errcode != 0) {
perror("getaddrinfo");
return -1;
}
res = result;
printf("Host: %s\n", host);
while (res) {
switch (res->ai_family) {
case AF_INET:
ptr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
break;
case AF_INET6:
ptr = &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
break;
default:
printf("Unsupported address family: %d\n", res->ai_family);
continue;
}
inet_ntop(res->ai_family, ptr, addrstr, 100);
printf("IPv%d address: %s (%s)\n", res->ai_family == AF_INET6 ? 6 : 4,
addrstr, res->ai_socktype == SOCK_STREAM ? "TCP" : "UDP");
res = res->ai_next;
}
freeaddrinfo(result);
return EXIT_SUCCESS;
}
int
main(int argc, char *argv[])
{
if (argc < 2) {
printf("Usage: %s DOMAIN\n", argv[0]);
return EXIT_FAILURE;
}
return lookup_host(argv[1]);
}