Skip to content

Commit f6bbead

Browse files
authored
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
1 parent ab752cd commit f6bbead

File tree

8 files changed

+306
-17
lines changed

8 files changed

+306
-17
lines changed

core/iwasm/libraries/lib-socket/inc/wasi_socket_ext.h

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,18 @@ typedef struct __wasi_addr_t {
5959

6060
typedef enum { INET4 = 0, INET6 } __wasi_address_family_t;
6161

62+
typedef struct __wasi_addr_info_t {
63+
__wasi_addr_t addr;
64+
__wasi_sock_type_t type;
65+
} __wasi_addr_info_t;
66+
67+
typedef struct __wasi_addr_info_hints_t {
68+
__wasi_sock_type_t type;
69+
__wasi_address_family_t family;
70+
// this is to workaround lack of optional parameters
71+
uint8_t hints_enabled;
72+
} __wasi_addr_info_hints_t;
73+
6274
#ifdef __wasi__
6375
/**
6476
* 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)
149161
}
150162

151163
/**
152-
* Resolves a hostname and a port to one or more IP addresses. Port is optional
153-
* and you can pass 0 (zero) in most cases, it is used a hint for protocol.
164+
* Resolve a hostname and a service to one or more IP addresses. Service is
165+
* optional and you can pass empty string in most cases, it is used as a hint
166+
* for protocol.
154167
*
155168
* Note: This is similar to `getaddrinfo` in POSIX
156169
*
157170
* When successful, the contents of the output buffer consist of a sequence of
158-
* IPv4 and/or IPv6 addresses. Each address entry consists of a addr_t object.
171+
* IPv4 and/or IPv6 addresses. Each address entry consists of a wasi_addr_t
172+
* object.
159173
*
160-
* This function fills the output buffer as much as possible, potentially
161-
* truncating the last address entry. It is advisable that the buffer is
174+
* This function fills the output buffer as much as possible, truncating the
175+
* entries that didn't fit into the buffer. A number of available addresses
176+
* will be returned through the last parameter.
162177
*/
163178
int32_t
164-
__imported_wasi_snapshot_preview1_addr_resolve(int32_t arg0, int32_t arg1,
165-
int32_t arg2, int32_t arg3,
166-
int32_t arg4)
179+
__imported_wasi_snapshot_preview1_sock_addr_resolve(int32_t arg0, int32_t arg1,
180+
int32_t arg2, int32_t arg3,
181+
int32_t arg4, int32_t arg5)
167182
__attribute__((__import_module__("wasi_snapshot_preview1"),
168-
__import_name__("addr_resolve")));
183+
__import_name__("sock_addr_resolve")));
169184

170185
static inline __wasi_errno_t
171-
__wasi_addr_resolve(__wasi_fd_t fd, const char *host, __wasi_ip_port_t port,
172-
uint8_t *buf, __wasi_size_t size)
186+
__wasi_sock_addr_resolve(const char *host, const char *service,
187+
__wasi_addr_info_hints_t *hints,
188+
__wasi_addr_info_t *addr_info,
189+
__wasi_size_t addr_info_size,
190+
__wasi_size_t *max_info_size)
173191
{
174-
return (__wasi_errno_t)__imported_wasi_snapshot_preview1_addr_resolve(
175-
(int32_t)fd, (int32_t)host, (int32_t)port, (int32_t)buf, (int32_t)size);
192+
return (__wasi_errno_t)__imported_wasi_snapshot_preview1_sock_addr_resolve(
193+
(int32_t)host, (int32_t)service, (int32_t)hints, (int32_t)addr_info,
194+
(int32_t)addr_info_size, (int32_t)max_info_size);
176195
}
177196

178197
/**

core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.c

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1041,10 +1041,23 @@ wasi_sock_addr_remote(wasm_exec_env_t exec_env, wasi_fd_t fd, uint8 *buf,
10411041
}
10421042

10431043
static wasi_errno_t
1044-
wasi_sock_addr_resolve(wasm_exec_env_t exec_env, wasi_fd_t fd, const char *host,
1045-
wasi_ip_port_t port, uint8 *buf, wasi_size_t size)
1044+
wasi_sock_addr_resolve(wasm_exec_env_t exec_env, const char *host,
1045+
const char *service, __wasi_addr_info_hints_t *hints,
1046+
__wasi_addr_info_t *addr_info,
1047+
__wasi_size_t addr_info_size,
1048+
__wasi_size_t *max_info_size)
10461049
{
1047-
return __WASI_ENOSYS;
1050+
wasm_module_inst_t module_inst = get_module_inst(exec_env);
1051+
wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst);
1052+
struct fd_table *curfds = NULL;
1053+
1054+
if (!wasi_ctx)
1055+
return __WASI_EACCES;
1056+
1057+
curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx);
1058+
1059+
return wasi_ssp_sock_addr_resolve(curfds, host, service, hints, addr_info,
1060+
addr_info_size, max_info_size);
10481061
}
10491062

10501063
static wasi_errno_t
@@ -1390,7 +1403,7 @@ static NativeSymbol native_symbols_libc_wasi[] = {
13901403
REG_NATIVE_FUNC(sock_accept, "(i*)i"),
13911404
REG_NATIVE_FUNC(sock_addr_local, "(i*i)i"),
13921405
REG_NATIVE_FUNC(sock_addr_remote, "(i*i)i"),
1393-
REG_NATIVE_FUNC(sock_addr_resolve, "(i*i*i)i"),
1406+
REG_NATIVE_FUNC(sock_addr_resolve, "($$**i*)i"),
13941407
REG_NATIVE_FUNC(sock_bind, "(i*)i"),
13951408
REG_NATIVE_FUNC(sock_close, "(i)i"),
13961409
REG_NATIVE_FUNC(sock_connect, "(i*)i"),

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,18 @@ typedef struct __wasi_addr_t {
598598

599599
typedef enum { INET4 = 0, INET6 } __wasi_address_family_t;
600600

601+
typedef struct __wasi_addr_info_t {
602+
__wasi_addr_t addr;
603+
__wasi_sock_type_t type;
604+
} __wasi_addr_info_t;
605+
606+
typedef struct __wasi_addr_info_hints_t {
607+
__wasi_sock_type_t type;
608+
__wasi_address_family_t family;
609+
// this is to workaround lack of optional parameters
610+
uint8_t hints_enabled;
611+
} __wasi_addr_info_hints_t;
612+
601613
#if defined(WASMTIME_SSP_WASI_API)
602614
#define WASMTIME_SSP_SYSCALL_NAME(name) \
603615
asm("__wasi_" #name)
@@ -1023,6 +1035,16 @@ wasi_ssp_sock_bind(
10231035
__wasi_fd_t fd, __wasi_addr_t *addr
10241036
) __attribute__((__warn_unused_result__));
10251037

1038+
__wasi_errno_t
1039+
wasi_ssp_sock_addr_resolve(
1040+
#if !defined(WASMTIME_SSP_STATIC_CURFDS)
1041+
struct fd_table *curfds,
1042+
#endif
1043+
const char *host, const char* service,
1044+
__wasi_addr_info_hints_t *hints, __wasi_addr_info_t *addr_info,
1045+
__wasi_size_t addr_info_size, __wasi_size_t *max_info_size
1046+
) __attribute__((__warn_unused_result__));
1047+
10261048
__wasi_errno_t
10271049
wasi_ssp_sock_connect(
10281050
#if !defined(WASMTIME_SSP_STATIC_CURFDS)

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

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,35 @@ convert_clockid(__wasi_clockid_t in, clockid_t *out)
199199
}
200200
}
201201

202+
// Converts an IPv4 binary address object to WASI address.
203+
static void
204+
ipv4_addr_to_wasi_addr(uint32_t addr, __wasi_ip_port_t port, __wasi_addr_t *out)
205+
{
206+
out->kind = IPv4;
207+
out->addr.ip4.port = port;
208+
out->addr.ip4.addr.n3 = (addr & 0xFF000000) >> 24;
209+
out->addr.ip4.addr.n2 = (addr & 0x00FF0000) >> 16;
210+
out->addr.ip4.addr.n1 = (addr & 0x0000FF00) >> 8;
211+
out->addr.ip4.addr.n0 = (addr & 0x000000FF);
212+
}
213+
214+
// Converts an IPv6 binary address object to WASI address object.
215+
static void
216+
ipv6_addr_to_wasi_addr(uint16_t addr[8], __wasi_ip_port_t port,
217+
__wasi_addr_t *out)
218+
{
219+
out->kind = IPv6;
220+
out->addr.ip6.port = port;
221+
out->addr.ip6.addr.n0 = addr[0];
222+
out->addr.ip6.addr.n1 = addr[1];
223+
out->addr.ip6.addr.n2 = addr[2];
224+
out->addr.ip6.addr.n3 = addr[3];
225+
out->addr.ip6.addr.h0 = addr[4];
226+
out->addr.ip6.addr.h1 = addr[5];
227+
out->addr.ip6.addr.h2 = addr[6];
228+
out->addr.ip6.addr.h3 = addr[7];
229+
}
230+
202231
__wasi_errno_t
203232
wasmtime_ssp_clock_res_get(__wasi_clockid_t clock_id,
204233
__wasi_timestamp_t *resolution)
@@ -2915,6 +2944,56 @@ wasi_ssp_sock_bind(
29152944
return __WASI_ESUCCESS;
29162945
}
29172946

2947+
__wasi_errno_t
2948+
wasi_ssp_sock_addr_resolve(
2949+
#if !defined(WASMTIME_SSP_STATIC_CURFDS)
2950+
struct fd_table *curfds,
2951+
#endif
2952+
const char *host, const char *service, __wasi_addr_info_hints_t *hints,
2953+
__wasi_addr_info_t *addr_info, __wasi_size_t addr_info_size,
2954+
__wasi_size_t *max_info_size)
2955+
{
2956+
bh_addr_info_t *wamr_addr_info =
2957+
wasm_runtime_malloc(addr_info_size * sizeof(bh_addr_info_t));
2958+
uint8_t hints_is_ipv4 = hints->family == INET4;
2959+
uint8_t hints_is_tcp = hints->type == SOCKET_STREAM;
2960+
size_t _max_info_size;
2961+
size_t actual_info_size;
2962+
2963+
if (!wamr_addr_info) {
2964+
return __WASI_ENOMEM;
2965+
}
2966+
2967+
int ret = os_socket_addr_resolve(
2968+
host, service, hints->hints_enabled ? &hints_is_tcp : NULL,
2969+
hints->hints_enabled ? &hints_is_ipv4 : NULL, wamr_addr_info,
2970+
addr_info_size, &_max_info_size);
2971+
2972+
if (ret != BHT_OK) {
2973+
wasm_runtime_free(wamr_addr_info);
2974+
return convert_errno(errno);
2975+
}
2976+
2977+
*max_info_size = _max_info_size;
2978+
actual_info_size =
2979+
addr_info_size < *max_info_size ? addr_info_size : *max_info_size;
2980+
2981+
for (size_t i = 0; i < actual_info_size; i++) {
2982+
addr_info[i].type = wamr_addr_info[i].is_tcp ? SOCK_STREAM : SOCK_DGRAM;
2983+
if (wamr_addr_info[i].is_ipv4) {
2984+
ipv4_addr_to_wasi_addr(*(uint32_t *)wamr_addr_info[i].addr,
2985+
wamr_addr_info[i].port, &addr_info[i].addr);
2986+
}
2987+
else {
2988+
ipv6_addr_to_wasi_addr((uint16_t *)wamr_addr_info[i].addr,
2989+
wamr_addr_info[i].port, &addr_info[i].addr);
2990+
}
2991+
}
2992+
2993+
wasm_runtime_free(wamr_addr_info);
2994+
return __WASI_ESUCCESS;
2995+
}
2996+
29182997
__wasi_errno_t
29192998
wasi_ssp_sock_connect(
29202999
#if !defined(WASMTIME_SSP_STATIC_CURFDS)

core/shared/platform/common/posix/posix_socket.c

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "platform_api_extension.h"
88

99
#include <arpa/inet.h>
10+
#include <netdb.h>
1011

1112
static void
1213
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)
176177
*out = ntohl(inet_addr(cp));
177178
return BHT_OK;
178179
}
180+
181+
static int
182+
getaddrinfo_error_to_errno(int error)
183+
{
184+
switch (error) {
185+
case EAI_AGAIN:
186+
return EAGAIN;
187+
case EAI_FAIL:
188+
return EFAULT;
189+
case EAI_MEMORY:
190+
return ENOMEM;
191+
case EAI_SYSTEM:
192+
return errno;
193+
default:
194+
return EINVAL;
195+
}
196+
}
197+
198+
static int
199+
is_addrinfo_supported(struct addrinfo *info)
200+
{
201+
return
202+
// Allow only IPv4 and IPv6
203+
(info->ai_family == AF_INET || info->ai_family == AF_INET6)
204+
// Allow only UDP and TCP
205+
&& (info->ai_socktype == SOCK_DGRAM || info->ai_socktype == SOCK_STREAM)
206+
&& (info->ai_protocol == IPPROTO_TCP
207+
|| info->ai_protocol == IPPROTO_UDP);
208+
}
209+
210+
int
211+
os_socket_addr_resolve(const char *host, const char *service,
212+
uint8_t *hint_is_tcp, uint8_t *hint_is_ipv4,
213+
bh_addr_info_t *addr_info, size_t addr_info_size,
214+
size_t *max_info_size)
215+
{
216+
struct addrinfo hints = { 0 }, *res, *result;
217+
int hints_enabled = hint_is_tcp || hint_is_ipv4;
218+
int ret;
219+
size_t pos = 0;
220+
221+
if (hints_enabled) {
222+
if (hint_is_ipv4) {
223+
hints.ai_family = *hint_is_ipv4 ? PF_INET : PF_INET6;
224+
}
225+
if (hint_is_tcp) {
226+
hints.ai_socktype = *hint_is_tcp ? SOCK_STREAM : SOCK_DGRAM;
227+
}
228+
}
229+
230+
ret = getaddrinfo(host, service, hints_enabled ? &hints : NULL, &result);
231+
if (ret != BHT_OK) {
232+
errno = getaddrinfo_error_to_errno(ret);
233+
return BHT_ERROR;
234+
}
235+
236+
res = result;
237+
while (res) {
238+
if (addr_info_size > pos) {
239+
if (!is_addrinfo_supported(res)) {
240+
res = res->ai_next;
241+
continue;
242+
}
243+
244+
if (res->ai_family == AF_INET) {
245+
struct sockaddr_in *addr_in =
246+
(struct sockaddr_in *)res->ai_addr;
247+
248+
addr_info[pos].port = addr_in->sin_port;
249+
addr_info[pos].is_ipv4 = 1;
250+
memcpy(addr_info[pos].addr, &addr_in->sin_addr,
251+
sizeof(addr_in->sin_addr));
252+
}
253+
else {
254+
struct sockaddr_in6 *addr_in =
255+
(struct sockaddr_in6 *)res->ai_addr;
256+
257+
addr_info[pos].port = addr_in->sin6_port;
258+
addr_info[pos].is_ipv4 = 0;
259+
for (int i = 0; i < 8; i++) {
260+
((uint16 *)addr_info[pos].addr)[i] =
261+
ntohs(((uint16_t *)&addr_in->sin6_addr)[i]);
262+
}
263+
}
264+
265+
addr_info[pos].is_tcp = res->ai_socktype == SOCK_STREAM;
266+
}
267+
268+
pos++;
269+
res = res->ai_next;
270+
}
271+
272+
*max_info_size = pos;
273+
freeaddrinfo(result);
274+
275+
return BHT_OK;
276+
}

core/shared/platform/include/platform_api_extension.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,41 @@ os_socket_shutdown(bh_socket_t socket);
339339
int
340340
os_socket_inet_network(const char *cp, uint32 *out);
341341

342+
typedef struct {
343+
uint8_t addr[16];
344+
uint16_t port;
345+
uint8_t is_ipv4;
346+
uint8_t is_tcp;
347+
} bh_addr_info_t;
348+
349+
/**
350+
* Resolve a host a hostname and a service to one or more IP addresses
351+
*
352+
* @param host a host to resolve
353+
*
354+
* @param service a service to find a port for
355+
*
356+
* @param hint_is_tcp an optional flag that determines a preferred socket type
357+
(TCP or UDP).
358+
*
359+
* @param hint_is_ipv4 an optional flag that determines a preferred address
360+
family (IPv4 or IPv6)
361+
*
362+
* @param addr_info a buffer for resolved addresses
363+
*
364+
* @param addr_info_size a size of the buffer for resolved addresses
365+
366+
* @param max_info_size a maximum number of addresses available (can be bigger
367+
or smaller than buffer size)
368+
369+
* @return On success, the function returns 0; otherwise, it returns -1
370+
*/
371+
int
372+
os_socket_addr_resolve(const char *host, const char *service,
373+
uint8_t *hint_is_tcp, uint8_t *hint_is_ipv4,
374+
bh_addr_info_t *addr_info, size_t addr_info_size,
375+
size_t *max_info_size);
376+
342377
#ifdef __cplusplus
343378
}
344379
#endif

0 commit comments

Comments
 (0)