Просмотр исходного кода

Implement getaddrinfo in wasi_socket_ext.c (#1413)

So getaddrinfo() can be used when compiling wasm app of C programs.
Marcin Kolny 3 лет назад
Родитель
Сommit
3c4e980c9c

+ 18 - 0
core/iwasm/libraries/lib-socket/inc/wasi_socket_ext.h

@@ -94,6 +94,17 @@ typedef struct __wasi_addr_info_hints_t {
  * <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
 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
 
 /**

+ 153 - 0
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);
+}

+ 3 - 2
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;

+ 2 - 0
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
 ############################################

+ 1 - 0
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)

+ 69 - 0
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 <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]);
+}