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
This commit is contained in:
Marcin Kolny
2022-07-28 05:25:05 +01:00
committed by GitHub
parent ab752cd5c3
commit f6bbeade2a
8 changed files with 306 additions and 17 deletions

View File

@ -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);
}
/**

View File

@ -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"),

View File

@ -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)

View File

@ -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)