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 15ec0f51..fd1d0de8 100644 --- a/core/iwasm/libraries/lib-socket/inc/wasi_socket_ext.h +++ b/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); } /** diff --git a/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.c b/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.c index 7611999a..b4e081df 100644 --- a/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.c +++ b/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"), diff --git a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/include/wasmtime_ssp.h b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/include/wasmtime_ssp.h index 8c9d265a..2a164ee3 100644 --- a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/include/wasmtime_ssp.h +++ b/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) diff --git a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/posix.c b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/posix.c index cc618680..cd7c6546 100644 --- a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/posix.c +++ b/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) diff --git a/core/shared/platform/common/posix/posix_socket.c b/core/shared/platform/common/posix/posix_socket.c index d3bcf148..ec425a33 100644 --- a/core/shared/platform/common/posix/posix_socket.c +++ b/core/shared/platform/common/posix/posix_socket.c @@ -7,6 +7,7 @@ #include "platform_api_extension.h" #include +#include 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; +} \ No newline at end of file diff --git a/core/shared/platform/include/platform_api_extension.h b/core/shared/platform/include/platform_api_extension.h index c13b68f5..08bf75b3 100644 --- a/core/shared/platform/include/platform_api_extension.h +++ b/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 diff --git a/core/shared/platform/linux-sgx/sgx_socket.c b/core/shared/platform/linux-sgx/sgx_socket.c index 156d79ef..fb26c905 100644 --- a/core/shared/platform/linux-sgx/sgx_socket.c +++ b/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 diff --git a/core/shared/platform/windows/win_socket.c b/core/shared/platform/windows/win_socket.c index 2bf7a84f..5e60c3ac 100644 --- a/core/shared/platform/windows/win_socket.c +++ b/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; } \ No newline at end of file