diff --git a/core/iwasm/libraries/lib-socket/inc/wasi_socket_ext.h b/core/iwasm/libraries/lib-socket/inc/wasi_socket_ext.h index 947ee7ef..0cc41864 100644 --- a/core/iwasm/libraries/lib-socket/inc/wasi_socket_ext.h +++ b/core/iwasm/libraries/lib-socket/inc/wasi_socket_ext.h @@ -94,6 +94,17 @@ typedef struct __wasi_addr_info_hints_t { * */ +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 accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); @@ -120,6 +131,13 @@ getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen); int 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 /** diff --git a/core/iwasm/libraries/lib-socket/src/wasi/wasi_socket_ext.c b/core/iwasm/libraries/lib-socket/src/wasi/wasi_socket_ext.c index bd008bbd..bcc2a3ea 100644 --- a/core/iwasm/libraries/lib-socket/src/wasi/wasi_socket_ext.c +++ b/core/iwasm/libraries/lib-socket/src/wasi/wasi_socket_ext.c @@ -317,3 +317,156 @@ getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen) 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); +} \ No newline at end of file diff --git a/core/shared/platform/common/posix/posix_socket.c b/core/shared/platform/common/posix/posix_socket.c index 27609b1b..6ed0b988 100644 --- a/core/shared/platform/common/posix/posix_socket.c +++ b/core/shared/platform/common/posix/posix_socket.c @@ -256,14 +256,15 @@ os_socket_addr_resolve(const char *host, const char *service, if (hints_enabled) { 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) { 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) { errno = getaddrinfo_error_to_errno(ret); return BHT_ERROR; diff --git a/samples/socket-api/CMakeLists.txt b/samples/socket-api/CMakeLists.txt index 5ce2543b..c7298b14 100644 --- a/samples/socket-api/CMakeLists.txt +++ b/samples/socket-api/CMakeLists.txt @@ -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) 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 ############################################ diff --git a/samples/socket-api/wasm-src/CMakeLists.txt b/samples/socket-api/wasm-src/CMakeLists.txt index 5c0ed7e2..19aba5f0 100644 --- a/samples/socket-api/wasm-src/CMakeLists.txt +++ b/samples/socket-api/wasm-src/CMakeLists.txt @@ -79,3 +79,4 @@ endfunction() compile_with_clang(tcp_server.c) compile_with_clang(tcp_client.c) compile_with_clang(send_recv.c) +compile_with_clang(addr_resolve.c) diff --git a/samples/socket-api/wasm-src/addr_resolve.c b/samples/socket-api/wasm-src/addr_resolve.c new file mode 100644 index 00000000..87734dea --- /dev/null +++ b/samples/socket-api/wasm-src/addr_resolve.c @@ -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 +#include +#include +#include +#ifdef __wasi__ +#include +#else +#include +#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]); +}