Procházet zdrojové kódy

Implement wasi_addr_resolve function (#1319)

Implement wasi_addr_resolve function.
    
Also slightly modify the interface to make it more accessible for the end user:
- replace port argument with the service - so the user can actually get the port for a given service if unknown
- introduce __wasi_addr_info_t and use it as a buffer for addresses, instead of generic buffer
- introduce __wasi_addr_info_hints_t so user can enable filtering on the syscall level (and therefore use smaller buffers for addresses)
- add max_size parameter for the API as an output - in case the number of addresses is bigger than the buffer size, user can repeat the call with bigger buffer

This change is very minimalistic, and it doesn't include the followings:
 1. implementation of getaddrinfo in the lib-socket
 2. sample application
Which are to be added in the following change #1336
Marcin Kolny před 3 roky
rodič
revize
f6bbeade2a

+ 32 - 13
core/iwasm/libraries/lib-socket/inc/wasi_socket_ext.h

@@ -59,6 +59,18 @@ typedef struct __wasi_addr_t {
 
 typedef enum { INET4 = 0, INET6 } __wasi_address_family_t;
 
+typedef struct __wasi_addr_info_t {
+    __wasi_addr_t addr;
+    __wasi_sock_type_t type;
+} __wasi_addr_info_t;
+
+typedef struct __wasi_addr_info_hints_t {
+    __wasi_sock_type_t type;
+    __wasi_address_family_t family;
+    // this is to workaround lack of optional parameters
+    uint8_t hints_enabled;
+} __wasi_addr_info_hints_t;
+
 #ifdef __wasi__
 /**
  * Reimplement below POSIX APIs with __wasi_sock_XXX functions.
@@ -149,30 +161,37 @@ __wasi_sock_addr_remote(__wasi_fd_t fd, uint8_t *buf, __wasi_size_t buf_len)
 }
 
 /**
- * Resolves a hostname and a port to one or more IP addresses. Port is optional
- * and you can pass 0 (zero) in most cases, it is used a hint for protocol.
+ * Resolve a hostname and a service to one or more IP addresses. Service is
+ * optional and you can pass empty string in most cases, it is used as a hint
+ * for protocol.
  *
  * Note: This is similar to `getaddrinfo` in POSIX
  *
  * When successful, the contents of the output buffer consist of a sequence of
- * IPv4 and/or IPv6 addresses. Each address entry consists of a addr_t object.
+ * IPv4 and/or IPv6 addresses. Each address entry consists of a wasi_addr_t
+ * object.
  *
- * This function fills the output buffer as much as possible, potentially
- * truncating the last address entry. It is advisable that the buffer is
+ * This function fills the output buffer as much as possible, truncating the
+ * entries that didn't fit into the buffer. A number of available addresses
+ * will be returned through the last parameter.
  */
 int32_t
-__imported_wasi_snapshot_preview1_addr_resolve(int32_t arg0, int32_t arg1,
-                                               int32_t arg2, int32_t arg3,
-                                               int32_t arg4)
+__imported_wasi_snapshot_preview1_sock_addr_resolve(int32_t arg0, int32_t arg1,
+                                                    int32_t arg2, int32_t arg3,
+                                                    int32_t arg4, int32_t arg5)
     __attribute__((__import_module__("wasi_snapshot_preview1"),
-                   __import_name__("addr_resolve")));
+                   __import_name__("sock_addr_resolve")));
 
 static inline __wasi_errno_t
-__wasi_addr_resolve(__wasi_fd_t fd, const char *host, __wasi_ip_port_t port,
-                    uint8_t *buf, __wasi_size_t size)
+__wasi_sock_addr_resolve(const char *host, const char *service,
+                         __wasi_addr_info_hints_t *hints,
+                         __wasi_addr_info_t *addr_info,
+                         __wasi_size_t addr_info_size,
+                         __wasi_size_t *max_info_size)
 {
-    return (__wasi_errno_t)__imported_wasi_snapshot_preview1_addr_resolve(
-        (int32_t)fd, (int32_t)host, (int32_t)port, (int32_t)buf, (int32_t)size);
+    return (__wasi_errno_t)__imported_wasi_snapshot_preview1_sock_addr_resolve(
+        (int32_t)host, (int32_t)service, (int32_t)hints, (int32_t)addr_info,
+        (int32_t)addr_info_size, (int32_t)max_info_size);
 }
 
 /**

+ 17 - 4
core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.c

@@ -1041,10 +1041,23 @@ wasi_sock_addr_remote(wasm_exec_env_t exec_env, wasi_fd_t fd, uint8 *buf,
 }
 
 static wasi_errno_t
-wasi_sock_addr_resolve(wasm_exec_env_t exec_env, wasi_fd_t fd, const char *host,
-                       wasi_ip_port_t port, uint8 *buf, wasi_size_t size)
+wasi_sock_addr_resolve(wasm_exec_env_t exec_env, const char *host,
+                       const char *service, __wasi_addr_info_hints_t *hints,
+                       __wasi_addr_info_t *addr_info,
+                       __wasi_size_t addr_info_size,
+                       __wasi_size_t *max_info_size)
 {
-    return __WASI_ENOSYS;
+    wasm_module_inst_t module_inst = get_module_inst(exec_env);
+    wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst);
+    struct fd_table *curfds = NULL;
+
+    if (!wasi_ctx)
+        return __WASI_EACCES;
+
+    curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx);
+
+    return wasi_ssp_sock_addr_resolve(curfds, host, service, hints, addr_info,
+                                      addr_info_size, max_info_size);
 }
 
 static wasi_errno_t
@@ -1390,7 +1403,7 @@ static NativeSymbol native_symbols_libc_wasi[] = {
     REG_NATIVE_FUNC(sock_accept, "(i*)i"),
     REG_NATIVE_FUNC(sock_addr_local, "(i*i)i"),
     REG_NATIVE_FUNC(sock_addr_remote, "(i*i)i"),
-    REG_NATIVE_FUNC(sock_addr_resolve, "(i*i*i)i"),
+    REG_NATIVE_FUNC(sock_addr_resolve, "($$**i*)i"),
     REG_NATIVE_FUNC(sock_bind, "(i*)i"),
     REG_NATIVE_FUNC(sock_close, "(i)i"),
     REG_NATIVE_FUNC(sock_connect, "(i*)i"),

+ 22 - 0
core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/include/wasmtime_ssp.h

@@ -598,6 +598,18 @@ typedef struct __wasi_addr_t {
 
 typedef enum { INET4 = 0, INET6 } __wasi_address_family_t;
 
+typedef struct __wasi_addr_info_t {
+    __wasi_addr_t addr;
+    __wasi_sock_type_t type;
+} __wasi_addr_info_t;
+
+typedef struct __wasi_addr_info_hints_t {
+   __wasi_sock_type_t type;
+   __wasi_address_family_t family;
+   // this is to workaround lack of optional parameters
+   uint8_t hints_enabled;
+} __wasi_addr_info_hints_t;
+
 #if defined(WASMTIME_SSP_WASI_API)
 #define WASMTIME_SSP_SYSCALL_NAME(name) \
     asm("__wasi_" #name)
@@ -1023,6 +1035,16 @@ wasi_ssp_sock_bind(
     __wasi_fd_t fd, __wasi_addr_t *addr
 ) __attribute__((__warn_unused_result__));
 
+__wasi_errno_t
+wasi_ssp_sock_addr_resolve(
+#if !defined(WASMTIME_SSP_STATIC_CURFDS)
+    struct fd_table *curfds,
+#endif
+    const char *host, const char* service,
+    __wasi_addr_info_hints_t *hints, __wasi_addr_info_t *addr_info,
+    __wasi_size_t addr_info_size, __wasi_size_t *max_info_size
+) __attribute__((__warn_unused_result__));
+
 __wasi_errno_t
 wasi_ssp_sock_connect(
 #if !defined(WASMTIME_SSP_STATIC_CURFDS)

+ 79 - 0
core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/posix.c

@@ -199,6 +199,35 @@ convert_clockid(__wasi_clockid_t in, clockid_t *out)
     }
 }
 
+// Converts an IPv4 binary address object to WASI address.
+static void
+ipv4_addr_to_wasi_addr(uint32_t addr, __wasi_ip_port_t port, __wasi_addr_t *out)
+{
+    out->kind = IPv4;
+    out->addr.ip4.port = port;
+    out->addr.ip4.addr.n3 = (addr & 0xFF000000) >> 24;
+    out->addr.ip4.addr.n2 = (addr & 0x00FF0000) >> 16;
+    out->addr.ip4.addr.n1 = (addr & 0x0000FF00) >> 8;
+    out->addr.ip4.addr.n0 = (addr & 0x000000FF);
+}
+
+// Converts an IPv6 binary address object to WASI address object.
+static void
+ipv6_addr_to_wasi_addr(uint16_t addr[8], __wasi_ip_port_t port,
+                       __wasi_addr_t *out)
+{
+    out->kind = IPv6;
+    out->addr.ip6.port = port;
+    out->addr.ip6.addr.n0 = addr[0];
+    out->addr.ip6.addr.n1 = addr[1];
+    out->addr.ip6.addr.n2 = addr[2];
+    out->addr.ip6.addr.n3 = addr[3];
+    out->addr.ip6.addr.h0 = addr[4];
+    out->addr.ip6.addr.h1 = addr[5];
+    out->addr.ip6.addr.h2 = addr[6];
+    out->addr.ip6.addr.h3 = addr[7];
+}
+
 __wasi_errno_t
 wasmtime_ssp_clock_res_get(__wasi_clockid_t clock_id,
                            __wasi_timestamp_t *resolution)
@@ -2915,6 +2944,56 @@ wasi_ssp_sock_bind(
     return __WASI_ESUCCESS;
 }
 
+__wasi_errno_t
+wasi_ssp_sock_addr_resolve(
+#if !defined(WASMTIME_SSP_STATIC_CURFDS)
+    struct fd_table *curfds,
+#endif
+    const char *host, const char *service, __wasi_addr_info_hints_t *hints,
+    __wasi_addr_info_t *addr_info, __wasi_size_t addr_info_size,
+    __wasi_size_t *max_info_size)
+{
+    bh_addr_info_t *wamr_addr_info =
+        wasm_runtime_malloc(addr_info_size * sizeof(bh_addr_info_t));
+    uint8_t hints_is_ipv4 = hints->family == INET4;
+    uint8_t hints_is_tcp = hints->type == SOCKET_STREAM;
+    size_t _max_info_size;
+    size_t actual_info_size;
+
+    if (!wamr_addr_info) {
+        return __WASI_ENOMEM;
+    }
+
+    int ret = os_socket_addr_resolve(
+        host, service, hints->hints_enabled ? &hints_is_tcp : NULL,
+        hints->hints_enabled ? &hints_is_ipv4 : NULL, wamr_addr_info,
+        addr_info_size, &_max_info_size);
+
+    if (ret != BHT_OK) {
+        wasm_runtime_free(wamr_addr_info);
+        return convert_errno(errno);
+    }
+
+    *max_info_size = _max_info_size;
+    actual_info_size =
+        addr_info_size < *max_info_size ? addr_info_size : *max_info_size;
+
+    for (size_t i = 0; i < actual_info_size; i++) {
+        addr_info[i].type = wamr_addr_info[i].is_tcp ? SOCK_STREAM : SOCK_DGRAM;
+        if (wamr_addr_info[i].is_ipv4) {
+            ipv4_addr_to_wasi_addr(*(uint32_t *)wamr_addr_info[i].addr,
+                                   wamr_addr_info[i].port, &addr_info[i].addr);
+        }
+        else {
+            ipv6_addr_to_wasi_addr((uint16_t *)wamr_addr_info[i].addr,
+                                   wamr_addr_info[i].port, &addr_info[i].addr);
+        }
+    }
+
+    wasm_runtime_free(wamr_addr_info);
+    return __WASI_ESUCCESS;
+}
+
 __wasi_errno_t
 wasi_ssp_sock_connect(
 #if !defined(WASMTIME_SSP_STATIC_CURFDS)

+ 98 - 0
core/shared/platform/common/posix/posix_socket.c

@@ -7,6 +7,7 @@
 #include "platform_api_extension.h"
 
 #include <arpa/inet.h>
+#include <netdb.h>
 
 static void
 textual_addr_to_sockaddr(const char *textual, int port, struct sockaddr_in *out)
@@ -176,3 +177,100 @@ os_socket_inet_network(const char *cp, uint32 *out)
     *out = ntohl(inet_addr(cp));
     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 ? PF_INET : PF_INET6;
+        }
+        if (hint_is_tcp) {
+            hints.ai_socktype = *hint_is_tcp ? SOCK_STREAM : SOCK_DGRAM;
+        }
+    }
+
+    ret = getaddrinfo(host, 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;
+            }
+
+            if (res->ai_family == AF_INET) {
+                struct sockaddr_in *addr_in =
+                    (struct sockaddr_in *)res->ai_addr;
+
+                addr_info[pos].port = addr_in->sin_port;
+                addr_info[pos].is_ipv4 = 1;
+                memcpy(addr_info[pos].addr, &addr_in->sin_addr,
+                       sizeof(addr_in->sin_addr));
+            }
+            else {
+                struct sockaddr_in6 *addr_in =
+                    (struct sockaddr_in6 *)res->ai_addr;
+
+                addr_info[pos].port = addr_in->sin6_port;
+                addr_info[pos].is_ipv4 = 0;
+                for (int i = 0; i < 8; i++) {
+                    ((uint16 *)addr_info[pos].addr)[i] =
+                        ntohs(((uint16_t *)&addr_in->sin6_addr)[i]);
+                }
+            }
+
+            addr_info[pos].is_tcp = res->ai_socktype == SOCK_STREAM;
+        }
+
+        pos++;
+        res = res->ai_next;
+    }
+
+    *max_info_size = pos;
+    freeaddrinfo(result);
+
+    return BHT_OK;
+}

+ 35 - 0
core/shared/platform/include/platform_api_extension.h

@@ -339,6 +339,41 @@ os_socket_shutdown(bh_socket_t socket);
 int
 os_socket_inet_network(const char *cp, uint32 *out);
 
+typedef struct {
+    uint8_t addr[16];
+    uint16_t port;
+    uint8_t is_ipv4;
+    uint8_t is_tcp;
+} bh_addr_info_t;
+
+/**
+ * Resolve a host a hostname and a service to one or more IP addresses
+ *
+ * @param host a host to resolve
+ *
+ * @param service a service to find a port for
+ *
+ * @param hint_is_tcp an optional flag that determines a preferred socket type
+ (TCP or UDP).
+ *
+ * @param hint_is_ipv4 an optional flag that determines a preferred address
+ family (IPv4 or IPv6)
+ *
+ * @param addr_info a buffer for resolved addresses
+ *
+ * @param addr_info_size a size of the buffer for resolved addresses
+
+ * @param max_info_size a maximum number of addresses available (can be bigger
+ or smaller than buffer size)
+
+ * @return On success, the function returns 0; otherwise, it returns -1
+ */
+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);
+
 #ifdef __cplusplus
 }
 #endif

+ 12 - 0
core/shared/platform/linux-sgx/sgx_socket.c

@@ -4,6 +4,7 @@
  */
 
 #include "platform_api_vmcore.h"
+#include "platform_api_extension.h"
 
 #ifndef SGX_DISABLE_WASI
 
@@ -620,4 +621,15 @@ os_socket_shutdown(bh_socket_t socket)
     return shutdown(socket, O_RDWR);
 }
 
+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;
+}
+
 #endif

+ 11 - 0
core/shared/platform/windows/win_socket.c

@@ -161,4 +161,15 @@ os_socket_inet_network(const char *cp, uint32 *out)
 
     *out = inet_addr(cp);
     return BHT_OK;
+}
+
+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;
 }