diff --git a/README.md b/README.md index 1465271bb0..4645bc4f7f 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ iwasm VM core - [Source debugging support](./doc/source_debugging.md), ref to [document](./doc/source_debugging.md) - [WAMR-IDE (Experimental)](./test-tools/wamr-ide) to develop WebAssembly applications with build, run and debug support, ref to [document](./test-tools/wamr-ide) - [XIP (Execution In Place) support](./doc/xip.md), ref to [document](./doc/xip.md) +- [Berkeley/Posix Socket support](./doc/socket_api.md), ref to [document](./doc/socket_api.md) and [sample](./samples/socket-api) ### WASM post-MVP features - [wasm-c-api](https://github.com/WebAssembly/wasm-c-api), ref to [document](doc/wasm_c_api.md) and [sample](samples/wasm-c-api) @@ -153,6 +154,7 @@ The WAMR [samples](./samples) integrate the iwasm VM core, application manager a - **[multi-module](./samples/multi-module)**: Demonstrating the [multiple modules as dependencies](./doc/multi_module.md) feature which implements the [load-time dynamic linking](https://webassembly.org/docs/dynamic-linking/). - **[ref-types](./samples/ref-types)**: Demonstrating how to call wasm functions with argument of externref type introduced by [reference types proposal](https://github.com/WebAssembly/reference-types). - **[wasm-c-api](./samples/wasm-c-api/README.md)**: Demonstrating how to run some samples from [wasm-c-api proposal](https://github.com/WebAssembly/wasm-c-api) and showing the supported API's. +- **[socket-api](./samples/socket-api/README.md)**: Demonstrating how to run wasm tcp server and tcp client applications, and how they communicate with each other. - **[workload](./samples/workload/README.md)**: Demonstrating how to build and run some complex workloads, e.g. tensorflow-lite, XNNPACK, wasm-av1, meshoptimizer and bwa. diff --git a/core/iwasm/aot/aot_runtime.c b/core/iwasm/aot/aot_runtime.c index e4b19a2d92..380bb90229 100644 --- a/core/iwasm/aot/aot_runtime.c +++ b/core/iwasm/aot/aot_runtime.c @@ -1020,6 +1020,7 @@ aot_instantiate(AOTModule *module, bool is_sub_inst, uint32 stack_size, module->wasi_args.dir_list, module->wasi_args.dir_count, module->wasi_args.map_dir_list, module->wasi_args.map_dir_count, module->wasi_args.env, module->wasi_args.env_count, + module->wasi_args.addr_pool, module->wasi_args.addr_count, module->wasi_args.argv, module->wasi_args.argc, module->wasi_args.stdio[0], module->wasi_args.stdio[1], module->wasi_args.stdio[2], error_buf, error_buf_size)) diff --git a/core/iwasm/common/wasm_runtime_common.c b/core/iwasm/common/wasm_runtime_common.c index eebbf27c85..fb9d7c3036 100644 --- a/core/iwasm/common/wasm_runtime_common.c +++ b/core/iwasm/common/wasm_runtime_common.c @@ -2191,14 +2191,36 @@ wasm_runtime_set_wasi_args(WASMModuleCommon *module, const char *dir_list[], argc, -1, -1, -1); } +void +wasm_runtime_set_wasi_addr_pool(wasm_module_t module, const char *addr_pool[], + uint32 addr_pool_size) +{ + WASIArguments *wasi_args = NULL; + +#if WASM_ENABLE_INTERP != 0 || WASM_ENABLE_JIT != 0 + if (module->module_type == Wasm_Module_Bytecode) + wasi_args = &((WASMModule *)module)->wasi_args; +#endif +#if WASM_ENABLE_AOT != 0 + if (module->module_type == Wasm_Module_AoT) + wasi_args = &((AOTModule *)module)->wasi_args; +#endif + + if (wasi_args) { + wasi_args->addr_pool = addr_pool; + wasi_args->addr_count = addr_pool_size; + } +} + #if WASM_ENABLE_UVWASI == 0 bool wasm_runtime_init_wasi(WASMModuleInstanceCommon *module_inst, const char *dir_list[], uint32 dir_count, const char *map_dir_list[], uint32 map_dir_count, - const char *env[], uint32 env_count, char *argv[], - uint32 argc, int stdinfd, int stdoutfd, int stderrfd, - char *error_buf, uint32 error_buf_size) + const char *env[], uint32 env_count, + const char *addr_pool[], uint32 addr_pool_size, + char *argv[], uint32 argc, int stdinfd, int stdoutfd, + int stderrfd, char *error_buf, uint32 error_buf_size) { WASIContext *wasi_ctx; char *argv_buf = NULL; @@ -2210,8 +2232,10 @@ wasm_runtime_init_wasi(WASMModuleInstanceCommon *module_inst, struct fd_table *curfds = NULL; struct fd_prestats *prestats = NULL; struct argv_environ_values *argv_environ = NULL; + struct addr_pool *apool = NULL; bool fd_table_inited = false, fd_prestats_inited = false; bool argv_environ_inited = false; + bool addr_pool_inited = false; __wasi_fd_t wasm_fd = 3; int32 raw_fd; char *path, resolved_path[PATH_MAX]; @@ -2285,7 +2309,8 @@ wasm_runtime_init_wasi(WASMModuleInstanceCommon *module_inst, if (!(curfds = wasm_runtime_malloc(sizeof(struct fd_table))) || !(prestats = wasm_runtime_malloc(sizeof(struct fd_prestats))) || !(argv_environ = - wasm_runtime_malloc(sizeof(struct argv_environ_values)))) { + wasm_runtime_malloc(sizeof(struct argv_environ_values))) + || !(apool = wasm_runtime_malloc(sizeof(struct addr_pool)))) { set_error_buf(error_buf, error_buf_size, "Init wasi environment failed: allocate memory failed"); goto fail; @@ -2316,6 +2341,14 @@ wasm_runtime_init_wasi(WASMModuleInstanceCommon *module_inst, } argv_environ_inited = true; + if (!addr_pool_init(apool)) { + set_error_buf(error_buf, error_buf_size, + "Init wasi environment failed: " + "init the address pool failed"); + goto fail; + } + addr_pool_inited = true; + /* Prepopulate curfds with stdin, stdout, and stderr file descriptors. */ if (!fd_table_insert_existing(curfds, 0, (stdinfd != -1) ? stdinfd : 0) || !fd_table_insert_existing(curfds, 1, (stdoutfd != -1) ? stdoutfd : 1) @@ -2350,9 +2383,34 @@ wasm_runtime_init_wasi(WASMModuleInstanceCommon *module_inst, fd_prestats_insert(prestats, dir_list[i], wasm_fd); } + /* addr_pool(textual) -> apool */ + for (i = 0; i < addr_pool_size; i++) { + char *cp, *address, *mask; + bool ret = false; + + cp = bh_strdup(addr_pool[i]); + if (!cp) { + set_error_buf(error_buf, error_buf_size, + "Init wasi environment failed: copy address failed"); + goto fail; + } + + address = strtok(cp, "/"); + mask = strtok(NULL, "/"); + + ret = addr_pool_insert(apool, address, (uint8)(atoi(mask))); + wasm_runtime_free(cp); + if (!ret) { + set_error_buf(error_buf, error_buf_size, + "Init wasi environment failed: store address failed"); + goto fail; + } + } + wasi_ctx->curfds = curfds; wasi_ctx->prestats = prestats; wasi_ctx->argv_environ = argv_environ; + wasi_ctx->addr_pool = apool; wasi_ctx->argv_buf = argv_buf; wasi_ctx->argv_list = argv_list; wasi_ctx->env_buf = env_buf; @@ -2367,12 +2425,16 @@ wasm_runtime_init_wasi(WASMModuleInstanceCommon *module_inst, fd_prestats_destroy(prestats); if (fd_table_inited) fd_table_destroy(curfds); + if (addr_pool_inited) + addr_pool_destroy(apool); if (curfds) wasm_runtime_free(curfds); if (prestats) wasm_runtime_free(prestats); if (argv_environ) wasm_runtime_free(argv_environ); + if (apool) + wasm_runtime_free(apool); if (argv_buf) wasm_runtime_free(argv_buf); if (argv_list) @@ -2430,9 +2492,10 @@ bool wasm_runtime_init_wasi(WASMModuleInstanceCommon *module_inst, const char *dir_list[], uint32 dir_count, const char *map_dir_list[], uint32 map_dir_count, - const char *env[], uint32 env_count, char *argv[], - uint32 argc, int stdinfd, int stdoutfd, int stderrfd, - char *error_buf, uint32 error_buf_size) + const char *env[], uint32 env_count, + const char *addr_pool[], uint32 addr_pool_size, + char *argv[], uint32 argc, int stdinfd, int stdoutfd, + int stderrfd, char *error_buf, uint32 error_buf_size) { uvwasi_t *uvwasi = NULL; uvwasi_options_t init_options; @@ -2595,6 +2658,10 @@ wasm_runtime_destroy_wasi(WASMModuleInstanceCommon *module_inst) fd_prestats_destroy(wasi_ctx->prestats); wasm_runtime_free(wasi_ctx->prestats); } + if (wasi_ctx->addr_pool) { + addr_pool_destroy(wasi_ctx->addr_pool); + wasm_runtime_free(wasi_ctx->addr_pool); + } if (wasi_ctx->argv_buf) wasm_runtime_free(wasi_ctx->argv_buf); if (wasi_ctx->argv_list) diff --git a/core/iwasm/common/wasm_runtime_common.h b/core/iwasm/common/wasm_runtime_common.h index ce188c8d18..d317801d25 100644 --- a/core/iwasm/common/wasm_runtime_common.h +++ b/core/iwasm/common/wasm_runtime_common.h @@ -352,6 +352,7 @@ typedef struct WASIContext { struct fd_table *curfds; struct fd_prestats *prestats; struct argv_environ_values *argv_environ; + struct addr_pool *addr_pool; char *argv_buf; char **argv_list; char *env_buf; @@ -712,9 +713,10 @@ bool wasm_runtime_init_wasi(WASMModuleInstanceCommon *module_inst, const char *dir_list[], uint32 dir_count, const char *map_dir_list[], uint32 map_dir_count, - const char *env[], uint32 env_count, char *argv[], - uint32 argc, int stdinfd, int stdoutfd, int stderrfd, - char *error_buf, uint32 error_buf_size); + const char *env[], uint32 env_count, + const char *addr_pool[], uint32 addr_pool_size, + char *argv[], uint32 argc, int stdinfd, int stdoutfd, + int stderrfd, char *error_buf, uint32 error_buf_size); void wasm_runtime_destroy_wasi(WASMModuleInstanceCommon *module_inst); @@ -726,6 +728,9 @@ wasm_runtime_set_wasi_ctx(WASMModuleInstanceCommon *module_inst, WASIContext * wasm_runtime_get_wasi_ctx(WASMModuleInstanceCommon *module_inst); +WASM_RUNTIME_API_EXTERN void +wasm_runtime_set_wasi_addr_pool(wasm_module_t module, const char *addr_pool[], + uint32 addr_pool_size); #endif /* end of WASM_ENABLE_LIBC_WASI */ #if WASM_ENABLE_REF_TYPES != 0 diff --git a/core/iwasm/include/wasm_export.h b/core/iwasm/include/wasm_export.h index 5a6d4e8dff..15efb1401b 100644 --- a/core/iwasm/include/wasm_export.h +++ b/core/iwasm/include/wasm_export.h @@ -348,6 +348,10 @@ wasm_runtime_set_wasi_args(wasm_module_t module, const char *env[], uint32_t env_count, char *argv[], int argc); +WASM_RUNTIME_API_EXTERN void +wasm_runtime_set_wasi_addr_pool(wasm_module_t module, const char *addr_pool[], + uint32_t addr_pool_size); + /** * Instantiate a WASM module. * diff --git a/core/iwasm/interpreter/wasm.h b/core/iwasm/interpreter/wasm.h index 9160350bef..ac79092498 100644 --- a/core/iwasm/interpreter/wasm.h +++ b/core/iwasm/interpreter/wasm.h @@ -305,6 +305,9 @@ typedef struct WASIArguments { uint32 map_dir_count; const char **env; uint32 env_count; + /* in CIDR noation */ + const char **addr_pool; + uint32 addr_count; char **argv; uint32 argc; int stdio[3]; diff --git a/core/iwasm/interpreter/wasm_runtime.c b/core/iwasm/interpreter/wasm_runtime.c index 0a5ba3dc63..815f7e89f8 100644 --- a/core/iwasm/interpreter/wasm_runtime.c +++ b/core/iwasm/interpreter/wasm_runtime.c @@ -1522,6 +1522,7 @@ wasm_instantiate(WASMModule *module, bool is_sub_inst, uint32 stack_size, module->wasi_args.dir_list, module->wasi_args.dir_count, module->wasi_args.map_dir_list, module->wasi_args.map_dir_count, module->wasi_args.env, module->wasi_args.env_count, + module->wasi_args.addr_pool, module->wasi_args.addr_count, module->wasi_args.argv, module->wasi_args.argc, module->wasi_args.stdio[0], module->wasi_args.stdio[1], module->wasi_args.stdio[2], error_buf, error_buf_size)) { diff --git a/core/iwasm/libraries/lib-socket/inc/wasi_socket_ext.h b/core/iwasm/libraries/lib-socket/inc/wasi_socket_ext.h new file mode 100644 index 0000000000..6f4d5d421d --- /dev/null +++ b/core/iwasm/libraries/lib-socket/inc/wasi_socket_ext.h @@ -0,0 +1,410 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _WASI_SOCKET_EXT_H_ +#define _WASI_SOCKET_EXT_H_ + +#include +#include + +/*Be a part of */ + +typedef enum { + SOCKET_DGRAM = 0, + SOCKET_STREAM, +} __wasi_sock_type_t; + +typedef uint16_t __wasi_ip_port_t; + +typedef enum { IPv4 = 0, IPv6 } __wasi_addr_type_t; + +/* n0.n1.n2.n3 */ +typedef struct __wasi_addr_ip4_t { + uint8_t n0; + uint8_t n1; + uint8_t n2; + uint8_t n3; +} __wasi_addr_ip4_t; + +typedef struct __wasi_addr_ip4_port_t { + __wasi_addr_ip4_t addr; + __wasi_ip_port_t port; +} __wasi_addr_ip4_port_t; + +typedef struct __wasi_addr_ip6_t { + uint16_t n0; + uint16_t n1; + uint16_t n2; + uint16_t n3; + uint16_t h0; + uint16_t h1; + uint16_t h2; + uint16_t h3; +} __wasi_addr_ip6_t; + +typedef struct __wasi_addr_ip6_port_t { + __wasi_addr_ip6_t addr; + __wasi_ip_port_t port; +} __wasi_addr_ip6_port_t; + +typedef struct __wasi_addr_t { + __wasi_addr_type_t kind; + union { + __wasi_addr_ip4_port_t ip4; + __wasi_addr_ip6_port_t ip6; + } addr; +} __wasi_addr_t; + +typedef enum { INET4 = 0, INET6 } __wasi_address_family_t; + +#ifdef __wasi__ +/** + * Reimplement below POSIX APIs with __wasi_sock_XXX functions. + * + * Keep sync with + * + * + */ + +int +accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); + +int +bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); + +int +connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); + +int +listen(int sockfd, int backlog); + +int +socket(int domain, int type, int protocol); +#endif + +/** + * Accept a connection on a socket + * Note: This is similar to `accept` + */ +int32_t +__imported_wasi_snapshot_preview1_sock_accept(int32_t arg0, int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_accept"))); + +static inline __wasi_errno_t +__wasi_sock_accept(__wasi_fd_t fd, __wasi_fd_t *fd_new) +{ + return (__wasi_errno_t)__imported_wasi_snapshot_preview1_sock_accept( + (int32_t)fd, (int32_t)fd_new); +} + +/** + * Returns the local address to which the socket is bound. + * + * Note: This is similar to `getsockname` in POSIX + * + * When successful, the contents of the output buffer consist of an IP address, + * either IP4 or IP6. + */ +int32_t +__imported_wasi_snapshot_preview1_sock_addr_local(int32_t arg0, int32_t arg1, + int32_t arg2) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_addr_local"))); + +static inline __wasi_errno_t +__wasi_sock_addr_local(__wasi_fd_t fd, uint8_t *buf, __wasi_size_t buf_len) +{ + return (__wasi_errno_t)__imported_wasi_snapshot_preview1_sock_addr_local( + (int32_t)fd, (int32_t)buf, (int32_t)buf_len); +} + +/** + * Returns the remote address to which the socket is connected to. + * + * Note: This is similar to `getpeername` in POSIX + * + * When successful, the contents of the output buffer consist of an IP address, + * either IP4 or IP6. + */ +int32_t +__imported_wasi_snapshot_preview1_sock_addr_remote(int32_t arg0, int32_t arg1, + int32_t arg2) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_addr_remote"))); + +static inline __wasi_errno_t +__wasi_sock_addr_remote(__wasi_fd_t fd, uint8_t *buf, __wasi_size_t buf_len) +{ + return (__wasi_errno_t)__imported_wasi_snapshot_preview1_sock_addr_remote( + (int32_t)fd, (int32_t)buf, (int32_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. + * + * 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. + * + * This function fills the output buffer as much as possible, potentially + * truncating the last address entry. It is advisable that the buffer is + */ +int32_t +__imported_wasi_snapshot_preview1_addr_resolve(int32_t arg0, int32_t arg1, + int32_t arg2, int32_t arg3, + int32_t arg4) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("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) +{ + 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); +} + +/** + * Bind a socket + * Note: This is similar to `bind` in POSIX using PF_INET + */ +int32_t +__imported_wasi_snapshot_preview1_sock_bind(int32_t arg0, int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_bind"))); + +static inline __wasi_errno_t +__wasi_sock_bind(__wasi_fd_t fd, __wasi_addr_t *addr) +{ + return (__wasi_errno_t)__imported_wasi_snapshot_preview1_sock_bind( + (int32_t)fd, (int32_t)addr); +} + +/** + * Close a socket (this is an alias for `fd_close`) + * Note: This is similar to `close` in POSIX. + */ +int32_t +__imported_wasi_snapshot_preview1_sock_close(int32_t arg0) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_close"))); + +static inline __wasi_errno_t +__wasi_sock_close(__wasi_fd_t fd) +{ + return (__wasi_errno_t)__imported_wasi_snapshot_preview1_sock_close( + (int32_t)fd); +} + +/** + * Initiate a connection on a socket to the specified address + * Note: This is similar to `connect` in POSIX + */ + +int32_t +__imported_wasi_snapshot_preview1_sock_connect(int32_t arg0, int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_connect"))); + +static inline __wasi_errno_t +__wasi_sock_connect(__wasi_fd_t fd, __wasi_addr_t *addr) +{ + return (__wasi_errno_t)__imported_wasi_snapshot_preview1_sock_connect( + (int32_t)fd, (int32_t)addr); +} +/** + * Retrieve the size of the receive buffer + * Note: This is similar to `getsockopt` in POSIX for SO_RCVBUF + */ + +int32_t +__imported_wasi_snapshot_preview1_sock_get_recv_buf_size(int32_t arg0, + int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_get_recv_buf_size"))); + +static inline __wasi_errno_t +__wasi_sock_get_recv_buf_size(__wasi_fd_t fd, __wasi_size_t *size) +{ + return (__wasi_errno_t) + __imported_wasi_snapshot_preview1_sock_get_recv_buf_size((int32_t)fd, + (int32_t)size); +} +/** + * Retrieve status of address reuse on a socket + * Note: This is similar to `getsockopt` in POSIX for SO_REUSEADDR + */ +int32_t +__imported_wasi_snapshot_preview1_sock_get_reuse_addr(int32_t arg0, + int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_get_reuse_addr"))); + +static inline __wasi_errno_t +__wasi_sock_get_reuse_addr(__wasi_fd_t fd, uint8_t *reuse) +{ + return (__wasi_errno_t) + __imported_wasi_snapshot_preview1_sock_get_reuse_addr((int32_t)fd, + (int32_t)reuse); +} + +/** + * Retrieve status of port reuse on a socket + * Note: This is similar to `getsockopt` in POSIX for SO_REUSEPORT + */ +int32_t +__imported_wasi_snapshot_preview1_sock_get_reuse_port(int32_t arg0, + int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_get_reuse_port"))); + +static inline __wasi_errno_t +__wasi_sock_get_reuse_port(__wasi_fd_t fd, int8_t *reuse) +{ + return (__wasi_errno_t) + __imported_wasi_snapshot_preview1_sock_get_reuse_port((int32_t)fd, + (int32_t)reuse); +} + +/** + * Retrieve the size of the send buffer + * Note: This is similar to `getsockopt` in POSIX for SO_SNDBUF + */ +int32_t +__imported_wasi_snapshot_preview1_sock_get_send_buf_size(int32_t arg0, + int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_get_send_buf_size"))); + +static inline __wasi_errno_t +__wasi_sock_get_send_buf_size(__wasi_fd_t fd, __wasi_size_t *size) +{ + return (__wasi_errno_t) + __imported_wasi_snapshot_preview1_sock_get_send_buf_size((int32_t)fd, + (int32_t)size); +} + +/** + * Listen for connections on a socket + * Note: This is similar to `listen` + */ +int32_t +__imported_wasi_snapshot_preview1_sock_listen(int32_t arg0, int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_listen"))); + +static inline __wasi_errno_t +__wasi_sock_listen(__wasi_fd_t fd, __wasi_size_t backlog) +{ + return (__wasi_errno_t)__imported_wasi_snapshot_preview1_sock_listen( + (int32_t)fd, (int32_t)backlog); +} + +/** + * Open a socket + + * The first argument to this function is a handle to an + * address pool. The address pool determines what actions can + * be performed and at which addresses they can be performed to. + + * The address pool cannot be re-assigned. You will need to close + * the socket and open a new one to use a different address pool. + + * Note: This is similar to `socket` in POSIX using PF_INET + */ + +int32_t +__imported_wasi_snapshot_preview1_sock_open(int32_t arg0, int32_t arg1, + int32_t arg2, int32_t arg3) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_open"))); + +static inline __wasi_errno_t +__wasi_sock_open(__wasi_fd_t fd, __wasi_address_family_t af, + __wasi_sock_type_t socktype, __wasi_fd_t *sockfd) +{ + return (__wasi_errno_t)__imported_wasi_snapshot_preview1_sock_open( + (int32_t)fd, (int32_t)af, (int32_t)socktype, (int32_t)sockfd); +} + +/** + * Set size of receive buffer + * Note: This is similar to `setsockopt` in POSIX for SO_RCVBUF + */ +int32_t +__imported_wasi_snapshot_preview1_sock_set_recv_buf_size(int32_t arg0, + int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_set_recv_buf_size"))); + +static inline __wasi_errno_t +__wasi_sock_set_recv_buf_size(__wasi_fd_t fd, __wasi_size_t size) +{ + return (__wasi_errno_t) + __imported_wasi_snapshot_preview1_sock_set_recv_buf_size((int32_t)fd, + (int32_t)size); +} + +/** + * Enable/disable address reuse on a socket + * Note: This is similar to `setsockopt` in POSIX for SO_REUSEADDR + */ +int32_t +__imported_wasi_snapshot_preview1_sock_set_reuse_addr(int32_t arg0, + int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_set_reuse_addr"))); + +static inline __wasi_errno_t +__wasi_sock_set_reuse_addr(__wasi_fd_t fd, uint8_t reuse) +{ + return (__wasi_errno_t) + __imported_wasi_snapshot_preview1_sock_set_reuse_addr((int32_t)fd, + (int32_t)reuse); +} + +/** + * Enable port reuse on a socket + * Note: This is similar to `setsockopt` in POSIX for SO_REUSEPORT + */ +int32_t +__imported_wasi_snapshot_preview1_sock_set_reuse_port(int32_t arg0, + int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_set_reuse_port"))); + +static inline __wasi_errno_t +__wasi_sock_set_reuse_port(__wasi_fd_t fd, uint8_t reuse) +{ + return (__wasi_errno_t) + __imported_wasi_snapshot_preview1_sock_set_reuse_port((int32_t)fd, + (int32_t)reuse); +} + +/** + * Set size of send buffer + * Note: This is similar to `setsockopt` in POSIX for SO_SNDBUF + */ +int32_t +__imported_wasi_snapshot_preview1_sock_set_send_buf_size(int32_t arg0) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_set_send_buf_size"))); + +static inline __wasi_errno_t +__wasi_sock_set_send_buf_size(__wasi_fd_t fd) +{ + return (__wasi_errno_t) + __imported_wasi_snapshot_preview1_sock_set_send_buf_size((int32_t)fd); +} + +/** + * TODO: modify recv() and send() + * since don't want to re-compile the wasi-libc, + * we tend to keep original implentations of recv() and send(). + */ +#endif \ No newline at end of file diff --git a/core/iwasm/libraries/lib-socket/lib_socket_wasi.cmake b/core/iwasm/libraries/lib-socket/lib_socket_wasi.cmake new file mode 100644 index 0000000000..209b0c4c9c --- /dev/null +++ b/core/iwasm/libraries/lib-socket/lib_socket_wasi.cmake @@ -0,0 +1,9 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 2.8...3.16) + +project(socket_wasi_ext) + +add_library(${PROJECT_NAME} STATIC ${CMAKE_CURRENT_LIST_DIR}/src/wasi/wasi_socket_ext.c) +target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_LIST_DIR}/inc/) diff --git a/core/iwasm/libraries/lib-socket/src/wasi/wasi_socket_ext.c b/core/iwasm/libraries/lib-socket/src/wasi/wasi_socket_ext.c new file mode 100644 index 0000000000..9f6bae93e0 --- /dev/null +++ b/core/iwasm/libraries/lib-socket/src/wasi/wasi_socket_ext.c @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include +#include +#include +#include +#include + +#define HANDLE_ERROR(error) \ + if (error != __WASI_ERRNO_SUCCESS) { \ + errno = error; \ + return -1; \ + } + +/* addr_num and port are in network order */ +static void +ipv4_addr_to_wasi_addr(uint32_t addr_num, uint16_t port, __wasi_addr_t *out) +{ + out->kind = IPv4; + out->addr.ip4.port = ntohs(port); + out->addr.ip4.addr.n3 = (addr_num & 0xFF000000) >> 24; + out->addr.ip4.addr.n2 = (addr_num & 0x00FF0000) >> 16; + out->addr.ip4.addr.n1 = (addr_num & 0x0000FF00) >> 8; + out->addr.ip4.addr.n0 = (addr_num & 0x000000FF); +} + +static __wasi_errno_t +sockaddr_to_wasi_addr(const struct sockaddr *sock_addr, socklen_t addrlen, + __wasi_addr_t *wasi_addr) +{ + __wasi_errno_t ret = __WASI_ERRNO_SUCCESS; + if (AF_INET == sock_addr->sa_family) { + assert(sizeof(struct sockaddr_in) == addrlen); + + ipv4_addr_to_wasi_addr( + ((struct sockaddr_in *)sock_addr)->sin_addr.s_addr, + ((struct sockaddr_in *)sock_addr)->sin_port, wasi_addr); + } + else if (AF_INET6 == sock_addr->sa_family) { + // TODO: IPV6 + ret = __WASI_ERRNO_AFNOSUPPORT; + } + else { + ret = __WASI_ERRNO_AFNOSUPPORT; + } + + return ret; +} + +static __wasi_errno_t +sock_addr_remote(__wasi_fd_t fd, struct sockaddr *sock_addr, socklen_t *addrlen) +{ + __wasi_addr_t wasi_addr = { 0 }; + __wasi_errno_t error; + + error = + __wasi_sock_addr_remote(fd, (uint8_t *)&wasi_addr, sizeof(wasi_addr)); + if (__WASI_ERRNO_SUCCESS != error) { + return error; + } + + if (IPv4 == wasi_addr.kind) { + struct sockaddr_in sock_addr_in = { 0 }; + + sock_addr_in.sin_family = AF_INET; + sock_addr_in.sin_addr.s_addr = (wasi_addr.addr.ip4.addr.n3 << 24) + | (wasi_addr.addr.ip4.addr.n2 << 16) + | (wasi_addr.addr.ip4.addr.n1 << 8) + | wasi_addr.addr.ip4.addr.n0; + sock_addr_in.sin_port = htons(wasi_addr.addr.ip4.port); + memcpy(sock_addr, &sock_addr_in, sizeof(sock_addr_in)); + + *addrlen = sizeof(sock_addr_in); + } + else if (IPv6 == wasi_addr.kind) { + // TODO: IPV6 + return __WASI_ERRNO_AFNOSUPPORT; + } + else { + return __WASI_ERRNO_AFNOSUPPORT; + } + + return __WASI_ERRNO_SUCCESS; +} + +int +accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) +{ + __wasi_addr_t wasi_addr = { 0 }; + __wasi_fd_t new_sockfd; + __wasi_errno_t error; + + error = __wasi_sock_accept(sockfd, &new_sockfd); + HANDLE_ERROR(error) + + // error = sock_addr_remote(new_sockfd, addr, addrlen); + // HANDLE_ERROR(error) + *addrlen = 0; + + return new_sockfd; +} + +int +bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) +{ + __wasi_addr_t wasi_addr = { 0 }; + __wasi_errno_t error; + + error = sockaddr_to_wasi_addr(addr, addrlen, &wasi_addr); + HANDLE_ERROR(error) + + error = __wasi_sock_bind(sockfd, &wasi_addr); + HANDLE_ERROR(error) + + return __WASI_ERRNO_SUCCESS; +} + +int +connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) +{ + __wasi_addr_t wasi_addr = { 0 }; + __wasi_errno_t error; + + error = sockaddr_to_wasi_addr(addr, addrlen, &wasi_addr); + HANDLE_ERROR(error) + + error = __wasi_sock_connect(sockfd, &wasi_addr); + HANDLE_ERROR(error) + + return __WASI_ERRNO_SUCCESS; +} + +int +listen(int sockfd, int backlog) +{ + __wasi_errno_t error = __wasi_sock_listen(sockfd, backlog); + HANDLE_ERROR(error) + return __WASI_ERRNO_SUCCESS; +} + +int +socket(int domain, int type, int protocol) +{ + // the stub of address pool fd + __wasi_fd_t poolfd = -1; + __wasi_fd_t sockfd; + __wasi_errno_t error; + __wasi_address_family_t af; + __wasi_sock_type_t socktype; + + if (AF_INET == domain) { + af = INET4; + } + else if (AF_INET6 == domain) { + af = INET6; + } + else { + return __WASI_ERRNO_NOPROTOOPT; + } + + if (SOCK_DGRAM == type) { + socktype = SOCKET_DGRAM; + } + else if (SOCK_STREAM == type) { + socktype = SOCKET_STREAM; + } + else { + return __WASI_ERRNO_NOPROTOOPT; + } + + error = __wasi_sock_open(poolfd, af, socktype, &sockfd); + HANDLE_ERROR(error) + + return sockfd; +} diff --git a/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.c b/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.c index a8ecb234d2..733cd4374c 100644 --- a/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.c +++ b/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.c @@ -50,6 +50,7 @@ typedef struct WASIContext { struct fd_table *curfds; struct fd_prestats *prestats; struct argv_environ_values *argv_environ; + struct addr_pool *addr_pool; char *argv_buf; char **argv_list; char *env_buf; @@ -83,6 +84,14 @@ wasi_ctx_get_prestats(wasm_module_inst_t module_inst, wasi_ctx_t wasi_ctx) return wasi_ctx->prestats; } +static inline struct addr_pool * +wasi_ctx_get_addr_pool(wasm_module_inst_t module_inst, wasi_ctx_t wasi_ctx) +{ + if (!wasi_ctx) + return NULL; + return wasi_ctx->addr_pool; +} + static wasi_errno_t wasi_args_get(wasm_exec_env_t exec_env, uint32 *argv_offsets, char *argv_buf) { @@ -986,113 +995,278 @@ wasi_random_get(wasm_exec_env_t exec_env, void *buf, uint32 buf_len) return wasmtime_ssp_random_get(buf, buf_len); } +static wasi_errno_t +wasi_sock_accept(wasm_exec_env_t exec_env, wasi_fd_t fd, wasi_fd_t *fd_new) +{ + 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_accept(curfds, fd, fd_new); +} + +static wasi_errno_t +wasi_sock_addr_local(wasm_exec_env_t exec_env, wasi_fd_t fd, uint8 *buf, + wasi_size_t buf_len) +{ + 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_local(curfds, fd, buf, buf_len); +} + +static wasi_errno_t +wasi_sock_addr_remote(wasm_exec_env_t exec_env, wasi_fd_t fd, uint8 *buf, + wasi_size_t buf_len) +{ + 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_remote(curfds, fd, buf, buf_len); +} + +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) +{ + return __WASI_ENOSYS; +} + +static wasi_errno_t +wasi_sock_bind(wasm_exec_env_t exec_env, wasi_fd_t fd, wasi_addr_t *addr) +{ + 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; + struct addr_pool *addr_pool = NULL; + + if (!wasi_ctx) + return __WASI_EACCES; + + curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx); + addr_pool = wasi_ctx_get_addr_pool(module_inst, wasi_ctx); + + return wasi_ssp_sock_bind(curfds, addr_pool, fd, addr); +} + +static wasi_errno_t +wasi_sock_close(wasm_exec_env_t exec_env, wasi_fd_t fd) +{ + return __WASI_ENOSYS; +} + +static wasi_errno_t +wasi_sock_connect(wasm_exec_env_t exec_env, wasi_fd_t fd, wasi_addr_t *addr) +{ + 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; + struct addr_pool *addr_pool = NULL; + + if (!wasi_ctx) + return __WASI_EACCES; + + curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx); + addr_pool = wasi_ctx_get_addr_pool(module_inst, wasi_ctx); + + return wasi_ssp_sock_connect(curfds, addr_pool, fd, addr); +} + +static wasi_errno_t +wasi_sock_get_recv_buf_size(wasm_exec_env_t exec_env, wasi_fd_t fd, + wasi_size_t *size) +{ + return __WASI_ENOSYS; +} + +static wasi_errno_t +wasi_sock_get_reuse_addr(wasm_exec_env_t exec_env, wasi_fd_t fd, uint8 *reuse) +{ + return __WASI_ENOSYS; +} + +static wasi_errno_t +wasi_sock_get_reuse_port(wasm_exec_env_t exec_env, wasi_fd_t fd, uint8 *reuse) +{ + return __WASI_ENOSYS; +} + +static wasi_errno_t +wasi_sock_get_send_buf_size(wasm_exec_env_t exec_env, wasi_fd_t fd, + wasi_size_t *size) +{ + return __WASI_ENOSYS; +} + +static wasi_errno_t +wasi_sock_listen(wasm_exec_env_t exec_env, wasi_fd_t fd, uint32 backlog) +{ + 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_listen(curfds, fd, backlog); +} + +static wasi_errno_t +wasi_sock_open(wasm_exec_env_t exec_env, wasi_fd_t poolfd, + wasi_address_family_t af, wasi_sock_type_t socktype, + wasi_fd_t *sockfd) +{ + 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_open(curfds, poolfd, af, socktype, sockfd); +} + +static wasi_errno_t +wasi_sock_set_recv_buf_size(wasm_exec_env_t exec_env, wasi_fd_t fd, + wasi_size_t size) +{ + return __WASI_ENOSYS; +} + +static wasi_errno_t +wasi_sock_set_reuse_addr(wasm_exec_env_t exec_env, wasi_fd_t fd, uint8 reuse) +{ + return __WASI_ENOSYS; +} + +static wasi_errno_t +wasi_sock_set_reuse_port(wasm_exec_env_t exec_env, wasi_fd_t fd, uint8 reuse) +{ + return __WASI_ENOSYS; +} + +static wasi_errno_t +wasi_sock_set_send_buf_size(wasm_exec_env_t exec_env, wasi_fd_t fd, + wasi_size_t size) +{ + return __WASI_ENOSYS; +} + static wasi_errno_t wasi_sock_recv(wasm_exec_env_t exec_env, wasi_fd_t sock, iovec_app_t *ri_data, - uint32 ri_data_len, wasi_riflags_t ri_flags, - uint32 *ro_datalen_app, wasi_roflags_t *ro_flags) + uint32 ri_data_len, wasi_riflags_t ri_flags, uint32 *ro_data_len, + wasi_roflags_t *ro_flags) { + /** + * ri_data_len is the length of a list of iovec_app_t, which head is + * ri_data. ro_data_len is the number of bytes received + **/ 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 = wasi_ctx_get_curfds(module_inst, wasi_ctx); - wasi_iovec_t *iovec, *iovec_begin; uint64 total_size; - size_t ro_datalen; uint32 i; wasi_errno_t err; if (!wasi_ctx) - return (wasi_errno_t)-1; + return __WASI_EINVAL; total_size = sizeof(iovec_app_t) * (uint64)ri_data_len; - if (!validate_native_addr(ro_datalen_app, (uint32)sizeof(uint32)) + if (!validate_native_addr(ro_data_len, (uint32)sizeof(uint32)) || !validate_native_addr(ro_flags, (uint32)sizeof(wasi_roflags_t)) || total_size >= UINT32_MAX || !validate_native_addr(ri_data, (uint32)total_size)) - return (wasi_errno_t)-1; - - total_size = sizeof(wasi_iovec_t) * (uint64)ri_data_len; - if (total_size >= UINT32_MAX - || !(iovec_begin = wasm_runtime_malloc((uint32)total_size))) - return (wasi_errno_t)-1; + return __WASI_EINVAL; - iovec = iovec_begin; + /* recv ri_data one by one */ + *ro_data_len = 0; + for (i = 0; i < ri_data_len; ri_data++, i++) { + void *buf; + size_t bytes_recv; - for (i = 0; i < ri_data_len; i++, ri_data++, iovec++) { if (!validate_app_addr(ri_data->buf_offset, ri_data->buf_len)) { - err = (wasi_errno_t)-1; - goto fail; + return __WASI_EINVAL; } - iovec->buf = (void *)addr_app_to_native(ri_data->buf_offset); - iovec->buf_len = ri_data->buf_len; - } - - err = wasmtime_ssp_sock_recv(curfds, sock, iovec_begin, ri_data_len, - ri_flags, &ro_datalen, ro_flags); - if (err) - goto fail; - - *(uint32 *)ro_datalen_app = (uint32)ro_datalen; - /* success */ - err = 0; + buf = (void *)addr_app_to_native(ri_data->buf_offset); + err = wasmtime_ssp_sock_recv(curfds, sock, buf, ri_data->buf_len, + &bytes_recv); + if (err != __WASI_ESUCCESS) { + return err; + } + *ro_data_len += bytes_recv; + } -fail: - wasm_runtime_free(iovec_begin); - return err; + *ro_flags = ri_flags; + return __WASI_ESUCCESS; } static wasi_errno_t wasi_sock_send(wasm_exec_env_t exec_env, wasi_fd_t sock, const iovec_app_t *si_data, uint32 si_data_len, - wasi_siflags_t si_flags, uint32 *so_datalen_app) + wasi_siflags_t si_flags, uint32 *so_data_len) { + /** + * si_data_len is the length of a list of iovec_app_t, which head is + * si_data. so_data_len is the number of bytes sent + **/ 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 = wasi_ctx_get_curfds(module_inst, wasi_ctx); - wasi_ciovec_t *ciovec, *ciovec_begin; uint64 total_size; - size_t so_datalen; uint32 i; wasi_errno_t err; if (!wasi_ctx) - return (wasi_errno_t)-1; + return __WASI_EINVAL; total_size = sizeof(iovec_app_t) * (uint64)si_data_len; - if (!validate_native_addr(so_datalen_app, sizeof(uint32)) + if (!validate_native_addr(so_data_len, sizeof(uint32)) || total_size >= UINT32_MAX || !validate_native_addr((void *)si_data, (uint32)total_size)) - return (wasi_errno_t)-1; + return __WASI_EINVAL; - total_size = sizeof(wasi_ciovec_t) * (uint64)si_data_len; - if (total_size >= UINT32_MAX - || !(ciovec_begin = wasm_runtime_malloc((uint32)total_size))) - return (wasi_errno_t)-1; - - ciovec = ciovec_begin; + /* send si_data one by one */ + *so_data_len = 0; + for (i = 0; i < si_data_len; i++, si_data++) { + void *buf; + size_t bytes_sent; - for (i = 0; i < si_data_len; i++, si_data++, ciovec++) { if (!validate_app_addr(si_data->buf_offset, si_data->buf_len)) { - err = (wasi_errno_t)-1; - goto fail; + return __WASI_EINVAL; } - ciovec->buf = (char *)addr_app_to_native(si_data->buf_offset); - ciovec->buf_len = si_data->buf_len; - } - - err = wasmtime_ssp_sock_send(curfds, sock, ciovec_begin, si_data_len, - si_flags, &so_datalen); - if (err) - goto fail; - - *so_datalen_app = (uint32)so_datalen; - /* success */ - err = 0; + buf = (void *)addr_app_to_native(si_data->buf_offset); + err = wasmtime_ssp_sock_send(curfds, sock, buf, si_data->buf_len, + &bytes_sent); + if (err != __WASI_ESUCCESS) { + return err; + } + *so_data_len += bytes_sent; + } -fail: - wasm_runtime_free(ciovec_begin); - return err; + return __WASI_ESUCCESS; } static wasi_errno_t @@ -1103,9 +1277,9 @@ wasi_sock_shutdown(wasm_exec_env_t exec_env, wasi_fd_t sock, wasi_sdflags_t how) struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx); if (!wasi_ctx) - return (wasi_errno_t)-1; + return __WASI_EINVAL; - return wasmtime_ssp_sock_shutdown(curfds, sock, how); + return wasmtime_ssp_sock_shutdown(curfds, sock); } static wasi_errno_t @@ -1161,8 +1335,25 @@ static NativeSymbol native_symbols_libc_wasi[] = { REG_NATIVE_FUNC(proc_exit, "(i)"), REG_NATIVE_FUNC(proc_raise, "(i)i"), REG_NATIVE_FUNC(random_get, "(*~)i"), + 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_bind, "(i*)i"), + REG_NATIVE_FUNC(sock_close, "(i)i"), + REG_NATIVE_FUNC(sock_connect, "(i*)i"), + REG_NATIVE_FUNC(sock_get_recv_buf_size, "(i*)i"), + REG_NATIVE_FUNC(sock_get_reuse_addr, "(i*)i"), + REG_NATIVE_FUNC(sock_get_reuse_port, "(i*)i"), + REG_NATIVE_FUNC(sock_get_send_buf_size, "(i*)i"), + REG_NATIVE_FUNC(sock_listen, "(ii)i"), + REG_NATIVE_FUNC(sock_open, "(iii*)i"), REG_NATIVE_FUNC(sock_recv, "(i*ii**)i"), REG_NATIVE_FUNC(sock_send, "(i*ii*)i"), + REG_NATIVE_FUNC(sock_set_recv_buf_size, "(ii)i"), + REG_NATIVE_FUNC(sock_set_reuse_addr, "(ii)i"), + REG_NATIVE_FUNC(sock_set_reuse_port, "(ii)i"), + REG_NATIVE_FUNC(sock_set_send_buf_size, "(ii)i"), REG_NATIVE_FUNC(sock_shutdown, "(ii)i"), REG_NATIVE_FUNC(sched_yield, "()i"), }; diff --git a/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.h b/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.h index b57f8835e7..be509576b0 100644 --- a/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.h +++ b/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.h @@ -13,38 +13,43 @@ extern "C" { #endif +typedef __wasi_address_family_t wasi_address_family_t; +typedef __wasi_addr_t wasi_addr_t; +typedef __wasi_advice_t wasi_advice_t; +typedef __wasi_ciovec_t wasi_ciovec_t; +typedef __wasi_clockid_t wasi_clockid_t; +typedef __wasi_dircookie_t wasi_dircookie_t; typedef __wasi_errno_t wasi_errno_t; +typedef __wasi_event_t wasi_event_t; +typedef __wasi_exitcode_t wasi_exitcode_t; +typedef __wasi_fdflags_t wasi_fdflags_t; +typedef __wasi_fdstat_t wasi_fdstat_t; typedef __wasi_fd_t wasi_fd_t; -typedef __wasi_clockid_t wasi_clockid_t; -typedef __wasi_timestamp_t wasi_timestamp_t; -typedef __wasi_prestat_t wasi_prestat_t; -typedef __wasi_iovec_t wasi_iovec_t; -typedef __wasi_ciovec_t wasi_ciovec_t; -typedef __wasi_filetype_t wasi_filetype_t; -typedef __wasi_filesize_t wasi_filesize_t; typedef __wasi_filedelta_t wasi_filedelta_t; -typedef __wasi_whence_t wasi_whence_t; -typedef __wasi_fdstat_t wasi_fdstat_t; -typedef __wasi_fdflags_t wasi_fdflags_t; -typedef __wasi_rights_t wasi_rights_t; -typedef __wasi_advice_t wasi_advice_t; -typedef __wasi_lookupflags_t wasi_lookupflags_t; -typedef __wasi_oflags_t wasi_oflags_t; -typedef __wasi_dircookie_t wasi_dircookie_t; +typedef __wasi_filesize_t wasi_filesize_t; typedef __wasi_filestat_t wasi_filestat_t; +typedef __wasi_filetype_t wasi_filetype_t; typedef __wasi_fstflags_t wasi_fstflags_t; -typedef __wasi_subscription_t wasi_subscription_t; -typedef __wasi_event_t wasi_event_t; -typedef __wasi_exitcode_t wasi_exitcode_t; -typedef __wasi_signal_t wasi_signal_t; +typedef __wasi_iovec_t wasi_iovec_t; +typedef __wasi_ip_port_t wasi_ip_port_t; +typedef __wasi_lookupflags_t wasi_lookupflags_t; +typedef __wasi_oflags_t wasi_oflags_t; +typedef __wasi_preopentype_t wasi_preopentype_t; +typedef __wasi_prestat_t wasi_prestat_t; typedef __wasi_riflags_t wasi_riflags_t; +typedef __wasi_rights_t wasi_rights_t; typedef __wasi_roflags_t wasi_roflags_t; -typedef __wasi_siflags_t wasi_siflags_t; typedef __wasi_sdflags_t wasi_sdflags_t; -typedef __wasi_preopentype_t wasi_preopentype_t; +typedef __wasi_siflags_t wasi_siflags_t; +typedef __wasi_signal_t wasi_signal_t; +typedef __wasi_size_t wasi_size_t; +typedef __wasi_sock_type_t wasi_sock_type_t; +typedef __wasi_subscription_t wasi_subscription_t; +typedef __wasi_timestamp_t wasi_timestamp_t; +typedef __wasi_whence_t wasi_whence_t; #ifdef __cplusplus } #endif -#endif /* end of _LIBC_WASI_WRAPPER_H */ +#endif /* end of _LIBC_WASI_WRAPPER_H */ \ No newline at end of file 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 560fdeb009..8c9d265a73 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 @@ -49,6 +49,9 @@ _Static_assert(_Alignof(int64_t) == 8, "non-wasi data layout"); _Static_assert(_Alignof(uint64_t) == 8, "non-wasi data layout"); #endif +typedef uint32_t __wasi_size_t; +_Static_assert(_Alignof(__wasi_size_t) == 4, "non-wasi data layout"); + typedef uint8_t __wasi_advice_t; #define __WASI_ADVICE_NORMAL (0) #define __WASI_ADVICE_SEQUENTIAL (1) @@ -211,35 +214,44 @@ typedef uint64_t __wasi_rights_t; * Observe that WASI defines rights in the plural form * TODO: refactor to use RIGHTS instead of RIGHT */ -#define __WASI_RIGHT_FD_DATASYNC ((__wasi_rights_t)(1 << 0)) -#define __WASI_RIGHT_FD_READ ((__wasi_rights_t)(1 << 1)) -#define __WASI_RIGHT_FD_SEEK ((__wasi_rights_t)(1 << 2)) -#define __WASI_RIGHT_FD_FDSTAT_SET_FLAGS ((__wasi_rights_t)(1 << 3)) -#define __WASI_RIGHT_FD_SYNC ((__wasi_rights_t)(1 << 4)) -#define __WASI_RIGHT_FD_TELL ((__wasi_rights_t)(1 << 5)) -#define __WASI_RIGHT_FD_WRITE ((__wasi_rights_t)(1 << 6)) -#define __WASI_RIGHT_FD_ADVISE ((__wasi_rights_t)(1 << 7)) -#define __WASI_RIGHT_FD_ALLOCATE ((__wasi_rights_t)(1 << 8)) -#define __WASI_RIGHT_PATH_CREATE_DIRECTORY ((__wasi_rights_t)(1 << 9)) -#define __WASI_RIGHT_PATH_CREATE_FILE ((__wasi_rights_t)(1 << 10)) -#define __WASI_RIGHT_PATH_LINK_SOURCE ((__wasi_rights_t)(1 << 11)) -#define __WASI_RIGHT_PATH_LINK_TARGET ((__wasi_rights_t)(1 << 12)) -#define __WASI_RIGHT_PATH_OPEN ((__wasi_rights_t)(1 << 13)) -#define __WASI_RIGHT_FD_READDIR ((__wasi_rights_t)(1 << 14)) -#define __WASI_RIGHT_PATH_READLINK ((__wasi_rights_t)(1 << 15)) -#define __WASI_RIGHT_PATH_RENAME_SOURCE ((__wasi_rights_t)(1 << 16)) -#define __WASI_RIGHT_PATH_RENAME_TARGET ((__wasi_rights_t)(1 << 17)) -#define __WASI_RIGHT_PATH_FILESTAT_GET ((__wasi_rights_t)(1 << 18)) -#define __WASI_RIGHT_PATH_FILESTAT_SET_SIZE ((__wasi_rights_t)(1 << 19)) -#define __WASI_RIGHT_PATH_FILESTAT_SET_TIMES ((__wasi_rights_t)(1 << 20)) -#define __WASI_RIGHT_FD_FILESTAT_GET ((__wasi_rights_t)(1 << 21)) -#define __WASI_RIGHT_FD_FILESTAT_SET_SIZE ((__wasi_rights_t)(1 << 22)) -#define __WASI_RIGHT_FD_FILESTAT_SET_TIMES ((__wasi_rights_t)(1 << 23)) -#define __WASI_RIGHT_PATH_SYMLINK ((__wasi_rights_t)(1 << 24)) -#define __WASI_RIGHT_PATH_REMOVE_DIRECTORY ((__wasi_rights_t)(1 << 25)) -#define __WASI_RIGHT_PATH_UNLINK_FILE ((__wasi_rights_t)(1 << 26)) -#define __WASI_RIGHT_POLL_FD_READWRITE ((__wasi_rights_t)(1 << 27)) -#define __WASI_RIGHT_SOCK_SHUTDOWN ((__wasi_rights_t)(1 << 28)) +#define __WASI_RIGHT_FD_DATASYNC ((__wasi_rights_t)(UINT64_C(1) << 0)) +#define __WASI_RIGHT_FD_READ ((__wasi_rights_t)(UINT64_C(1) << 1)) +#define __WASI_RIGHT_FD_SEEK ((__wasi_rights_t)(UINT64_C(1) << 2)) +#define __WASI_RIGHT_FD_FDSTAT_SET_FLAGS ((__wasi_rights_t)(UINT64_C(1) << 3)) +#define __WASI_RIGHT_FD_SYNC ((__wasi_rights_t)(UINT64_C(1) << 4)) +#define __WASI_RIGHT_FD_TELL ((__wasi_rights_t)(UINT64_C(1) << 5)) +#define __WASI_RIGHT_FD_WRITE ((__wasi_rights_t)(UINT64_C(1) << 6)) +#define __WASI_RIGHT_FD_ADVISE ((__wasi_rights_t)(UINT64_C(1) << 7)) +#define __WASI_RIGHT_FD_ALLOCATE ((__wasi_rights_t)(UINT64_C(1) << 8)) +#define __WASI_RIGHT_PATH_CREATE_DIRECTORY ((__wasi_rights_t)(UINT64_C(1) << 9)) +#define __WASI_RIGHT_PATH_CREATE_FILE ((__wasi_rights_t)(UINT64_C(1) << 10)) +#define __WASI_RIGHT_PATH_LINK_SOURCE ((__wasi_rights_t)(UINT64_C(1) << 11)) +#define __WASI_RIGHT_PATH_LINK_TARGET ((__wasi_rights_t)(UINT64_C(1) << 12)) +#define __WASI_RIGHT_PATH_OPEN ((__wasi_rights_t)(UINT64_C(1) << 13)) +#define __WASI_RIGHT_FD_READDIR ((__wasi_rights_t)(UINT64_C(1) << 14)) +#define __WASI_RIGHT_PATH_READLINK ((__wasi_rights_t)(UINT64_C(1) << 15)) +#define __WASI_RIGHT_PATH_RENAME_SOURCE ((__wasi_rights_t)(UINT64_C(1) << 16)) +#define __WASI_RIGHT_PATH_RENAME_TARGET ((__wasi_rights_t)(UINT64_C(1) << 17)) +#define __WASI_RIGHT_PATH_FILESTAT_GET ((__wasi_rights_t)(UINT64_C(1) << 18)) +#define __WASI_RIGHT_PATH_FILESTAT_SET_SIZE ((__wasi_rights_t)(UINT64_C(1) << 19)) +#define __WASI_RIGHT_PATH_FILESTAT_SET_TIMES ((__wasi_rights_t)(UINT64_C(1) << 20)) +#define __WASI_RIGHT_FD_FILESTAT_GET ((__wasi_rights_t)(UINT64_C(1) << 21)) +#define __WASI_RIGHT_FD_FILESTAT_SET_SIZE ((__wasi_rights_t)(UINT64_C(1) << 22)) +#define __WASI_RIGHT_FD_FILESTAT_SET_TIMES ((__wasi_rights_t)(UINT64_C(1) << 23)) +#define __WASI_RIGHT_PATH_SYMLINK ((__wasi_rights_t)(UINT64_C(1) << 24)) +#define __WASI_RIGHT_PATH_REMOVE_DIRECTORY ((__wasi_rights_t)(UINT64_C(1) << 25)) +#define __WASI_RIGHT_PATH_UNLINK_FILE ((__wasi_rights_t)(UINT64_C(1) << 26)) +#define __WASI_RIGHT_POLL_FD_READWRITE ((__wasi_rights_t)(UINT64_C(1) << 27)) +#define __WASI_RIGHT_SOCK_CONNECT ((__wasi_rights_t)(UINT64_C(1) << 28)) +#define __WASI_RIGHT_SOCK_LISTEN ((__wasi_rights_t)(UINT64_C(1) << 29)) +#define __WASI_RIGHT_SOCK_BIND ((__wasi_rights_t)(UINT64_C(1) << 30)) +#define __WASI_RIGHT_SOCK_ACCEPT ((__wasi_rights_t)(UINT64_C(1) << 31)) +#define __WASI_RIGHT_SOCK_RECV ((__wasi_rights_t)(UINT64_C(1) << 32)) +#define __WASI_RIGHT_SOCK_SEND ((__wasi_rights_t)(UINT64_C(1) << 33)) +#define __WASI_RIGHT_SOCK_ADDR_LOCAL ((__wasi_rights_t)(UINT64_C(1) << 34)) +#define __WASI_RIGHT_SOCK_ADDR_REMOTE ((__wasi_rights_t)(UINT64_C(1) << 35)) +#define __WASI_RIGHT_SOCK_RECV_FROM ((__wasi_rights_t)(UINT64_C(1) << 36)) +#define __WASI_RIGHT_SOCK_SEND_TO ((__wasi_rights_t)(UINT64_C(1) << 37)) typedef uint16_t __wasi_roflags_t; #define __WASI_SOCK_RECV_DATA_TRUNCATED (0x0001) @@ -301,6 +313,7 @@ typedef uint8_t __wasi_preopentype_t; struct fd_table; struct fd_prestats; struct argv_environ_values; +struct addr_pool; typedef struct __wasi_dirent_t { __wasi_dircookie_t d_next; @@ -536,6 +549,55 @@ _Static_assert(_Alignof(__wasi_subscription_t) == 8, "witx calculated align"); _Static_assert(offsetof(__wasi_subscription_t, userdata) == 0, "witx calculated offset"); _Static_assert(offsetof(__wasi_subscription_t, u) == 8, "witx calculated offset"); +/* keep syncing with wasi_socket_ext.h */ +typedef enum { + SOCKET_DGRAM = 0, + SOCKET_STREAM, +} __wasi_sock_type_t; + +typedef uint16_t __wasi_ip_port_t; + +typedef enum { IPv4 = 0, IPv6 } __wasi_addr_type_t; + +/* n0.n1.n2.n3 */ +typedef struct __wasi_addr_ip4_t { + uint8_t n0; + uint8_t n1; + uint8_t n2; + uint8_t n3; +} __wasi_addr_ip4_t; + +typedef struct __wasi_addr_ip4_port_t { + __wasi_addr_ip4_t addr; + __wasi_ip_port_t port; +} __wasi_addr_ip4_port_t; + +typedef struct __wasi_addr_ip6_t { + uint16_t n0; + uint16_t n1; + uint16_t n2; + uint16_t n3; + uint16_t h0; + uint16_t h1; + uint16_t h2; + uint16_t h3; +} __wasi_addr_ip6_t; + +typedef struct __wasi_addr_ip6_port_t { + __wasi_addr_ip6_t addr; + __wasi_ip_port_t port; +} __wasi_addr_ip6_port_t; + +typedef struct __wasi_addr_t { + __wasi_addr_type_t kind; + union { + __wasi_addr_ip4_port_t ip4; + __wasi_addr_ip6_port_t ip6; + } addr; +} __wasi_addr_t; + +typedef enum { INET4 = 0, INET6 } __wasi_address_family_t; + #if defined(WASMTIME_SSP_WASI_API) #define WASMTIME_SSP_SYSCALL_NAME(name) \ asm("__wasi_" #name) @@ -920,16 +982,71 @@ __wasi_errno_t wasmtime_ssp_random_get( size_t buf_len ) WASMTIME_SSP_SYSCALL_NAME(random_get) __attribute__((__warn_unused_result__)); +__wasi_errno_t +wasi_ssp_sock_accept( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, __wasi_fd_t *fd_new +) __attribute__((__warn_unused_result__)); + +__wasi_errno_t +wasi_ssp_sock_addr_local( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, uint8_t *buf, __wasi_size_t buf_len +) __attribute__((__warn_unused_result__)); + +__wasi_errno_t +wasi_ssp_sock_addr_remote( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, uint8_t *buf, __wasi_size_t buf_len +) __attribute__((__warn_unused_result__)); + +__wasi_errno_t +wasi_ssp_sock_open( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t poolfd, __wasi_address_family_t af, __wasi_sock_type_t socktype, + __wasi_fd_t *sockfd +) __attribute__((__warn_unused_result__)); + +__wasi_errno_t +wasi_ssp_sock_bind( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, struct addr_pool *addr_pool, +#endif + __wasi_fd_t fd, __wasi_addr_t *addr +) __attribute__((__warn_unused_result__)); + +__wasi_errno_t +wasi_ssp_sock_connect( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, struct addr_pool *addr_pool, +#endif + __wasi_fd_t fd, __wasi_addr_t *addr +) __attribute__((__warn_unused_result__)); + +__wasi_errno_t +wasi_ssp_sock_listen( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, __wasi_size_t backlog +) __attribute__((__warn_unused_result__)); + __wasi_errno_t wasmtime_ssp_sock_recv( #if !defined(WASMTIME_SSP_STATIC_CURFDS) struct fd_table *curfds, #endif __wasi_fd_t sock, - const __wasi_iovec_t *ri_data, - size_t ri_data_len, - __wasi_riflags_t ri_flags, - size_t *ro_datalen, - __wasi_roflags_t *ro_flags + void *buf, + size_t buf_len, + size_t *recv_len ) WASMTIME_SSP_SYSCALL_NAME(sock_recv) __attribute__((__warn_unused_result__)); __wasi_errno_t wasmtime_ssp_sock_send( @@ -937,18 +1054,16 @@ __wasi_errno_t wasmtime_ssp_sock_send( struct fd_table *curfds, #endif __wasi_fd_t sock, - const __wasi_ciovec_t *si_data, - size_t si_data_len, - __wasi_siflags_t si_flags, - size_t *so_datalen + const void *buf, + size_t buf_len, + size_t *sent_len ) WASMTIME_SSP_SYSCALL_NAME(sock_send) __attribute__((__warn_unused_result__)); __wasi_errno_t wasmtime_ssp_sock_shutdown( #if !defined(WASMTIME_SSP_STATIC_CURFDS) struct fd_table *curfds, #endif - __wasi_fd_t sock, - __wasi_sdflags_t how + __wasi_fd_t sock ) WASMTIME_SSP_SYSCALL_NAME(sock_shutdown) __attribute__((__warn_unused_result__)); __wasi_errno_t wasmtime_ssp_sched_yield(void) 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 7e01f85ca8..7eff817e68 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 @@ -60,6 +60,7 @@ static_assert(sizeof(struct iovec) == sizeof(__wasi_ciovec_t), static __thread struct fd_table *curfds; static __thread struct fd_prestats *prestats; static __thread struct argv_environ_values *argv_environ; +static __thread struct addr_pool *addr_pool; #endif // Converts a POSIX error code to a CloudABI error code. @@ -2716,113 +2717,294 @@ wasmtime_ssp_random_get(void *buf, size_t nbyte) } __wasi_errno_t -wasmtime_ssp_sock_recv( +wasi_ssp_sock_accept( #if !defined(WASMTIME_SSP_STATIC_CURFDS) struct fd_table *curfds, #endif - __wasi_fd_t sock, const __wasi_iovec_t *ri_data, size_t ri_data_len, - __wasi_riflags_t ri_flags, size_t *ro_datalen, __wasi_roflags_t *ro_flags) + __wasi_fd_t fd, __wasi_fd_t *fd_new) { - // Convert input to msghdr. - struct msghdr hdr = { - .msg_iov = (struct iovec *)ri_data, - .msg_iovlen = ri_data_len, - }; - int nflags = 0; - if ((ri_flags & __WASI_SOCK_RECV_PEEK) != 0) - nflags |= MSG_PEEK; - if ((ri_flags & __WASI_SOCK_RECV_WAITALL) != 0) - nflags |= MSG_WAITALL; - + __wasi_filetype_t wasi_type; + __wasi_rights_t max_base, max_inheriting; struct fd_object *fo; + bh_socket_t new_sock; + int ret; __wasi_errno_t error = - fd_object_get(curfds, &fo, sock, __WASI_RIGHT_FD_READ, 0); - if (error != 0) { + fd_object_get(curfds, &fo, fd, __WASI_RIGHT_SOCK_ACCEPT, 0); + if (error != __WASI_ESUCCESS) return error; - } - ssize_t datalen = recvmsg(fd_number(fo), &hdr, nflags); + ret = os_socket_accept(fd_number(fo), &new_sock, NULL, NULL); fd_object_release(fo); - if (datalen < 0) { + if (ret == BHT_ERROR) return convert_errno(errno); + + error = fd_determine_type_rights(new_sock, &wasi_type, &max_base, + &max_inheriting); + if (error != __WASI_ESUCCESS) { + os_socket_close(ret); + return error; } - // Convert msghdr to output. - *ro_datalen = (size_t)datalen; - *ro_flags = 0; - if ((hdr.msg_flags & MSG_TRUNC) != 0) - *ro_flags |= __WASI_SOCK_RECV_DATA_TRUNCATED; - return 0; + error = fd_table_insert_fd(curfds, new_sock, wasi_type, max_base, + max_inheriting, fd_new); + if (error != __WASI_ESUCCESS) { + os_socket_close(ret); + return error; + } + + return __WASI_ESUCCESS; } __wasi_errno_t -wasmtime_ssp_sock_send( +wasi_ssp_sock_addr_local( #if !defined(WASMTIME_SSP_STATIC_CURFDS) struct fd_table *curfds, #endif - __wasi_fd_t sock, const __wasi_ciovec_t *si_data, size_t si_data_len, - __wasi_siflags_t si_flags, size_t *so_datalen) NO_LOCK_ANALYSIS + __wasi_fd_t fd, uint8 *buf, __wasi_size_t buf_len) { - // Convert input to msghdr. - struct msghdr hdr = { - .msg_iov = (struct iovec *)si_data, - .msg_iovlen = si_data_len, - }; + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, __WASI_RIGHT_SOCK_ADDR_LOCAL, 0); + if (error != __WASI_ESUCCESS) + return error; + + fd_object_release(fo); + return __WASI_ENOSYS; +} + +__wasi_errno_t +wasi_ssp_sock_addr_remote( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, uint8 *buf, __wasi_size_t buf_len) +{ + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, __WASI_RIGHT_SOCK_ADDR_REMOTE, 0); + if (error != __WASI_ESUCCESS) + return error; + + fd_object_release(fo); + return __WASI_ENOSYS; +} + +__wasi_errno_t +wasi_ssp_sock_bind( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, struct addr_pool *addr_pool, +#endif + __wasi_fd_t fd, __wasi_addr_t *addr) +{ + char buf[24] = { 0 }; + const char *format = "%u.%u.%u.%u"; + struct fd_object *fo; + __wasi_errno_t error; + int port = addr->addr.ip4.port; + int ret; + + snprintf(buf, 24, format, addr->addr.ip4.addr.n0, addr->addr.ip4.addr.n1, + addr->addr.ip4.addr.n2, addr->addr.ip4.addr.n3); + + if (!addr_pool_search(addr_pool, buf)) { + return __WASI_EACCES; + } + + error = fd_object_get(curfds, &fo, fd, __WASI_RIGHT_SOCK_BIND, 0); + if (error != __WASI_ESUCCESS) + return error; + + ret = os_socket_bind(fd_number(fo), buf, &port); + fd_object_release(fo); + if (ret == BHT_ERROR) { + return convert_errno(errno); + } + + return __WASI_ESUCCESS; +} - // Attach file descriptors if present. +__wasi_errno_t +wasi_ssp_sock_connect( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, struct addr_pool *addr_pool, +#endif + __wasi_fd_t fd, __wasi_addr_t *addr) +{ + char buf[24] = { 0 }; + const char *format = "%u.%u.%u.%u"; + struct fd_object *fo; __wasi_errno_t error; + int ret; + + snprintf(buf, 24, format, addr->addr.ip4.addr.n0, addr->addr.ip4.addr.n1, + addr->addr.ip4.addr.n2, addr->addr.ip4.addr.n3); - // Send message. + if (!addr_pool_search(addr_pool, buf)) { + return __WASI_EACCES; + } + + error = fd_object_get(curfds, &fo, fd, __WASI_RIGHT_SOCK_BIND, 0); + if (error != __WASI_ESUCCESS) + return error; + + ret = os_socket_connect(fd_number(fo), buf, addr->addr.ip4.port); + fd_object_release(fo); + if (ret == BHT_ERROR) { + return convert_errno(errno); + } + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +wasi_ssp_sock_listen( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, __wasi_size_t backlog) +{ struct fd_object *fo; - error = fd_object_get(curfds, &fo, sock, __WASI_RIGHT_FD_WRITE, 0); - if (error != 0) - goto out; - ssize_t len = sendmsg(fd_number(fo), &hdr, 0); + int ret; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, __WASI_RIGHT_SOCK_LISTEN, 0); + if (error != __WASI_ESUCCESS) + return error; + + ret = os_socket_listen(fd_number(fo), backlog); fd_object_release(fo); - if (len < 0) { - error = convert_errno(errno); + if (ret == BHT_ERROR) { + return convert_errno(errno); + } + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +wasi_ssp_sock_open( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t poolfd, __wasi_address_family_t af, __wasi_sock_type_t socktype, + __wasi_fd_t *sockfd) +{ + bh_socket_t sock; + int tcp_or_udp = 0; + int ret; + __wasi_filetype_t wasi_type; + __wasi_rights_t max_base, max_inheriting; + __wasi_errno_t error; + + (void)poolfd; + + if (INET4 != af) { + return __WASI_EAFNOSUPPORT; + } + + tcp_or_udp = SOCKET_DGRAM == socktype ? 0 : 1; + + ret = os_socket_create(&sock, tcp_or_udp); + if (ret == BHT_ERROR) { + return convert_errno(errno); + } + + error = + fd_determine_type_rights(sock, &wasi_type, &max_base, &max_inheriting); + if (error != __WASI_ESUCCESS) { + os_socket_close(ret); + return error; + } + + if (SOCKET_DGRAM == socktype) { + assert(wasi_type == __WASI_FILETYPE_SOCKET_DGRAM); } else { - *so_datalen = (size_t)len; + assert(wasi_type == __WASI_FILETYPE_SOCKET_STREAM); } -out: - return error; + // TODO: base rights and inheriting rights ? + error = fd_table_insert_fd(curfds, sock, wasi_type, max_base, + max_inheriting, sockfd); + if (error != __WASI_ESUCCESS) { + os_socket_close(ret); + return error; + } + + return __WASI_ESUCCESS; } __wasi_errno_t -wasmtime_ssp_sock_shutdown( +wasmtime_ssp_sock_recv( #if !defined(WASMTIME_SSP_STATIC_CURFDS) struct fd_table *curfds, #endif - __wasi_fd_t sock, __wasi_sdflags_t how) + __wasi_fd_t sock, void *buf, size_t buf_len, size_t *recv_len) { - int nhow; - switch (how) { - case __WASI_SHUT_RD: - nhow = SHUT_RD; - break; - case __WASI_SHUT_WR: - nhow = SHUT_WR; - break; - case __WASI_SHUT_RD | __WASI_SHUT_WR: - nhow = SHUT_RDWR; - break; - default: - return __WASI_EINVAL; + struct fd_object *fo; + __wasi_errno_t error; + int ret; + + error = fd_object_get(curfds, &fo, sock, __WASI_RIGHT_FD_READ, 0); + if (error != 0) { + return error; } + ret = os_socket_recv(fd_number(fo), buf, buf_len); + fd_object_release(fo); + if (ret == BHT_ERROR) { + return convert_errno(errno); + } + + *recv_len = (size_t)ret; + return __WASI_ESUCCESS; +} + +__wasi_errno_t +wasmtime_ssp_sock_send( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t sock, const void *buf, size_t buf_len, size_t *sent_len) +{ struct fd_object *fo; - __wasi_errno_t error = - fd_object_get(curfds, &fo, sock, __WASI_RIGHT_SOCK_SHUTDOWN, 0); + __wasi_errno_t error; + int ret; + + error = fd_object_get(curfds, &fo, sock, __WASI_RIGHT_FD_WRITE, 0); + if (error != 0) { + return error; + } + + ret = os_socket_send(fd_number(fo), buf, buf_len); + fd_object_release(fo); + if (ret == BHT_ERROR) { + return convert_errno(errno); + } + + *sent_len = (size_t)ret; + return __WASI_ESUCCESS; +} + +__wasi_errno_t +wasmtime_ssp_sock_shutdown( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t sock) +{ + struct fd_object *fo; + __wasi_errno_t error; + int ret; + + error = fd_object_get(curfds, &fo, sock, 0, 0); if (error != 0) return error; - int ret = shutdown(fd_number(fo), nhow); + ret = os_socket_shutdown(fd_number(fo)); fd_object_release(fo); - if (ret < 0) + if (ret == BHT_ERROR) return convert_errno(errno); - return 0; + + return __WASI_ESUCCESS; } __wasi_errno_t @@ -2943,3 +3125,88 @@ fd_prestats_destroy(struct fd_prestats *pt) wasm_runtime_free(pt->prestats); } } + +bool +addr_pool_init(struct addr_pool *addr_pool) +{ + addr_pool->next = NULL; + addr_pool->addr = 0; + addr_pool->mask = 0; + return true; +} + +bool +addr_pool_insert(struct addr_pool *addr_pool, const char *addr, uint8 mask) +{ + struct addr_pool *cur = addr_pool; + struct addr_pool *next; + + if (!addr_pool) { + return false; + } + + if (!(next = wasm_runtime_malloc(sizeof(struct addr_pool)))) { + return false; + } + + next->next = NULL; + next->mask = mask; + if (os_socket_inet_network(addr, &next->addr) != BHT_OK) { + wasm_runtime_free(next); + return false; + } + + /* attach with */ + while (cur->next) { + cur = cur->next; + } + cur->next = next; + return true; +} + +static bool +compare_address(const struct addr_pool *addr_pool_entry, const char *addr) +{ + /* host order */ + uint32 target; + uint32 address = addr_pool_entry->addr; + /* 0.0.0.0 means any address */ + if (0 == address) { + return true; + } + + if (os_socket_inet_network(addr, &target) != BHT_OK) { + return false; + } + + uint32 mask = addr_pool_entry->mask; + uint32 first_address = address & mask; + uint32 last_address = address | (~mask); + return first_address <= target && target <= last_address; +} + +bool +addr_pool_search(struct addr_pool *addr_pool, const char *addr) +{ + struct addr_pool *cur = addr_pool->next; + + while (cur) { + if (compare_address(cur, addr)) + return true; + cur = cur->next; + } + + return false; +} + +void +addr_pool_destroy(struct addr_pool *addr_pool) +{ + struct addr_pool *cur = addr_pool->next; + + while (cur) { + struct addr_pool *next = cur->next; + wasm_runtime_free(cur); + cur = next; + } +} diff --git a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/posix.h b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/posix.h index 62214dc2fa..ad124c8228 100644 --- a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/posix.h +++ b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/posix.h @@ -46,6 +46,13 @@ struct argv_environ_values { size_t environ_count; }; +struct addr_pool { + struct addr_pool *next; + /* addr and mask in host order */ + uint32 addr; + uint8 mask; +}; + bool fd_table_init(struct fd_table *); bool @@ -66,4 +73,13 @@ fd_table_destroy(struct fd_table *ft); void fd_prestats_destroy(struct fd_prestats *pt); +bool +addr_pool_init(struct addr_pool *); +bool +addr_pool_insert(struct addr_pool *, const char *, uint8 mask); +bool +addr_pool_search(struct addr_pool *, const char *); +void +addr_pool_destroy(struct addr_pool *); + #endif diff --git a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/rights.h b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/rights.h index 434561dcbf..4f58381598 100644 --- a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/rights.h +++ b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/rights.h @@ -32,7 +32,13 @@ __WASI_RIGHT_FD_FILESTAT_SET_SIZE | \ __WASI_RIGHT_PATH_SYMLINK | __WASI_RIGHT_PATH_UNLINK_FILE | \ __WASI_RIGHT_PATH_REMOVE_DIRECTORY | \ - __WASI_RIGHT_POLL_FD_READWRITE | __WASI_RIGHT_SOCK_SHUTDOWN) + __WASI_RIGHT_POLL_FD_READWRITE | __WASI_RIGHT_SOCK_CONNECT | \ + __WASI_RIGHT_SOCK_LISTEN | __WASI_RIGHT_SOCK_BIND | \ + __WASI_RIGHT_SOCK_ACCEPT | __WASI_RIGHT_SOCK_RECV | \ + __WASI_RIGHT_SOCK_SEND | __WASI_RIGHT_SOCK_ADDR_LOCAL | \ + __WASI_RIGHT_SOCK_ADDR_REMOTE | __WASI_RIGHT_SOCK_RECV_FROM | \ + __WASI_RIGHT_SOCK_SEND_TO) + // Block and character device interaction is outside the scope of // CloudABI. Simply allow everything. @@ -71,10 +77,15 @@ #define RIGHTS_REGULAR_FILE_INHERITING 0 // Operations that apply to sockets and socket pairs. -#define RIGHTS_SOCKET_BASE \ - (__WASI_RIGHT_FD_READ | __WASI_RIGHT_FD_FDSTAT_SET_FLAGS | \ - __WASI_RIGHT_FD_WRITE | __WASI_RIGHT_FD_FILESTAT_GET | \ - __WASI_RIGHT_POLL_FD_READWRITE | __WASI_RIGHT_SOCK_SHUTDOWN) +#define RIGHTS_SOCKET_BASE \ + (__WASI_RIGHT_FD_READ | __WASI_RIGHT_FD_FDSTAT_SET_FLAGS | \ + __WASI_RIGHT_FD_WRITE | __WASI_RIGHT_FD_FILESTAT_GET | \ + __WASI_RIGHT_POLL_FD_READWRITE | __WASI_RIGHT_SOCK_CONNECT | \ + __WASI_RIGHT_SOCK_LISTEN | __WASI_RIGHT_SOCK_BIND | \ + __WASI_RIGHT_SOCK_ACCEPT | __WASI_RIGHT_SOCK_RECV | \ + __WASI_RIGHT_SOCK_SEND | __WASI_RIGHT_SOCK_ADDR_LOCAL | \ + __WASI_RIGHT_SOCK_ADDR_REMOTE | __WASI_RIGHT_SOCK_RECV_FROM | \ + __WASI_RIGHT_SOCK_SEND_TO) #define RIGHTS_SOCKET_INHERITING RIGHTS_ALL // Operations that apply to TTYs. diff --git a/core/shared/platform/common/posix/posix_socket.c b/core/shared/platform/common/posix/posix_socket.c index 0f7e8fcaea..fb8c9ceb39 100644 --- a/core/shared/platform/common/posix/posix_socket.c +++ b/core/shared/platform/common/posix/posix_socket.c @@ -8,6 +8,16 @@ #include +static void +textual_addr_to_sockaddr(const char *textual, int port, struct sockaddr_in *out) +{ + assert(textual); + + out->sin_family = AF_INET; + out->sin_port = htons(port); + out->sin_addr.s_addr = inet_addr(textual); +} + int os_socket_create(bh_socket_t *sock, int tcp_or_udp) { @@ -113,6 +123,23 @@ os_socket_accept(bh_socket_t server_sock, bh_socket_t *sock, void *addr, return BHT_OK; } +int +os_socket_connect(bh_socket_t socket, const char *addr, int port) +{ + struct sockaddr_in addr_in = { 0 }; + socklen_t addr_len = sizeof(struct sockaddr_in); + int ret = 0; + + textual_addr_to_sockaddr(addr, port, &addr_in); + + ret = connect(socket, (struct sockaddr *)&addr_in, addr_len); + if (ret == -1) { + return BHT_ERROR; + } + + return BHT_OK; +} + int os_socket_recv(bh_socket_t socket, void *buf, unsigned int len) { @@ -138,3 +165,13 @@ os_socket_shutdown(bh_socket_t socket) shutdown(socket, O_RDWR); return BHT_OK; } + +int +os_socket_inet_network(const char *cp, uint32 *out) +{ + if (!cp) + return BHT_ERROR; + + *out = inet_network(cp); + 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 937882ca95..8513d79437 100644 --- a/core/shared/platform/include/platform_api_extension.h +++ b/core/shared/platform/include/platform_api_extension.h @@ -265,6 +265,17 @@ int os_socket_accept(bh_socket_t server_sock, bh_socket_t *sock, void *addr, unsigned int *addrlen); +/** + * initiate a connection on a socket + * + * @param socket the socket to connect with + * @param addr the ip address, only IPv4 supported currently + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_connect(bh_socket_t socket, const char *addr, int port); + /** * Blocking receive message from a socket. * @@ -310,6 +321,18 @@ os_socket_close(bh_socket_t socket); int os_socket_shutdown(bh_socket_t socket); +/** + * converts cp into a number in host byte order suitable for use as + * an Internet network address + * + * @param cp a string in IPv4 numbers-and-dots notation + * + * @return On success, the converted address is returned. + * If the input is invalid, -1 is returned + */ +int +os_socket_inet_network(const char *cp, uint32 *out); + #ifdef __cplusplus } #endif diff --git a/core/shared/platform/linux-sgx/sgx_socket.c b/core/shared/platform/linux-sgx/sgx_socket.c index e9a824fda6..9ed92f53ef 100644 --- a/core/shared/platform/linux-sgx/sgx_socket.c +++ b/core/shared/platform/linux-sgx/sgx_socket.c @@ -227,4 +227,72 @@ shutdown(int sockfd, int how) return ret; } +int +os_socket_accept(bh_socket_t server_sock, bh_socket_t *sock, void *addr, + unsigned int *addrlen) + +{ + errno = ENOSYS; + return -1; +} +int +os_socket_bind(bh_socket_t socket, const char *host, int *port) +{ + errno = ENOSYS; + return -1; +} + +int +os_socket_close(bh_socket_t socket) +{ + errno = ENOSYS; + return -1; +} + +int +os_socket_connect(bh_socket_t socket, const char *addr, int port) +{} + +int +os_socket_create(bh_socket_t *sock, int tcp_or_udp) +{ + errno = ENOSYS; + return -1; +} + +int +os_socket_inet_network(const char *cp, uint32 *out) +{ + errno = ENOSYS; + return -1; +} + +int +os_socket_listen(bh_socket_t socket, int max_client) +{ + errno = ENOSYS; + return -1; +} + +int +os_socket_recv(bh_socket_t socket, void *buf, unsigned int len) +{ + errno = ENOSYS; + return -1; +} + +int +os_socket_send(bh_socket_t socket, const void *buf, unsigned int len) +{ + errno = ENOSYS; + return -1; +} + +int +os_socket_shutdown(bh_socket_t socket) +{ + errno = ENOSYS; + return -1; +} + #endif diff --git a/core/shared/platform/windows/win_socket.c b/core/shared/platform/windows/win_socket.c index 512d50280e..2bf7a84fbb 100644 --- a/core/shared/platform/windows/win_socket.c +++ b/core/shared/platform/windows/win_socket.c @@ -152,3 +152,13 @@ os_socket_shutdown(bh_socket_t socket) shutdown(socket, SD_BOTH); return BHT_OK; } + +int +os_socket_inet_network(const char *cp, uint32 *out) +{ + if (!cp) + return BHT_ERROR; + + *out = inet_addr(cp); + return BHT_OK; +} \ No newline at end of file diff --git a/doc/socket_api.md b/doc/socket_api.md new file mode 100644 index 0000000000..91f668e55a --- /dev/null +++ b/doc/socket_api.md @@ -0,0 +1,66 @@ +# How to use Berkeley/Posix Socket APIs in WebAssembly + +**_Berkeley sockets_** usually means an API for Internet sockets and Unix domain +sockets. A socket is an abstract representation of the local endpoint of a +network communication path. + +Currently, WAMR supports a limit set of all well-known functions: +`accept()`, `bind()`, `connect()`, `listen()`, `recv()`, `send()`, `shutdown()` +and `socket()`. Users can call those functions in WebAssembly code directly. +Those WebAssembly socket calls will be dispatched to the imported +functions and eventually will be implemented by host socket APIs. + +This document introduces a way to support _Berkeley/Posix Socket APIs_ in +WebAssembly code. + +## Patch the native code + +The first step is to include a header file of the WAMR socket extension in the +native source code. + +```c +#ifdef __wasi__ +#include +#endif +``` + +`__wasi__` is a Marco defined by WASI. The host compiler will not enable it. + +## CMake files + +It is recommended that the project should use CMake as its build system. Use +[_wasi-sdk_](https://github.com/WebAssembly/wasi-sdk) +as a toolchain to compile C/C++ to WebAssembly + +```bash +$ cmake -DWASI_SDK_PREFIX=${WASI_SDK_DIR} + -DCMAKE_TOOLCHAIN_FILE=${WASI_TOOLCHAIN_FILE} + -DCMAKE_SYSROOT=${WASI_SYS_ROOT} + .. +``` + +In the *CMakeLists.txt*, include an extension of socket support and link with it. + +```cmake +include(${CMAKE_CURRENT_SOURCE_DIR}/../../../core/iwasm/libraries/lib-socket/lib_socket_wasi.cmake) +add_executable(socket_example tcp_server.c) +target_link_libraries(socket_example socket_wasi_ext) +``` + +Now, the native code with socket APIs is ready for compilation. + +## Run with iwasm + +If having the _.wasm_, the last step is to run it with _iwasm_. + +The _iwasm_ should be compiled with `WAMR_BUILD_LIBC_WASI=1`. By default, it is +enabled. + +_iwasm_ accepts address ranges via an option, `--addr-pool`, to implement +the capability control. All IP address the WebAssebmly application may need to `bind()` or `connect()` should be announced first. Every IP address should be in CIRD notation. + +```bash +$ iwasm --addr-pool=1.2.3.4/15,2.3.4.6/16 socket_example.wasm +``` + +Refer to [socket api sample](../samples/socket-api) for more details. diff --git a/product-mini/platforms/posix/main.c b/product-mini/platforms/posix/main.c index 2392d7a667..466bda89be 100644 --- a/product-mini/platforms/posix/main.c +++ b/product-mini/platforms/posix/main.c @@ -16,8 +16,6 @@ static int app_argc; static char **app_argv; -#define MODULE_PATH ("--module-path=") - /* clang-format off */ static int print_help() @@ -41,6 +39,10 @@ print_help() printf(" --dir= Grant wasi access to the given host directories\n"); printf(" to the program, for example:\n"); printf(" --dir= --dir=\n"); + printf(" --addr-pool= Grant wasi access to the given network addresses in\n"); + printf(" CIRD notation to the program, seperated with ',',\n"); + printf(" for example:\n"); + printf(" --addr-pool=1.2.3.4/15,2.3.4.5/16\n"); #endif #if WASM_ENABLE_MULTI_MODULE != 0 printf(" --module-path= Indicate a module search path. default is current\n" @@ -244,6 +246,8 @@ main(int argc, char *argv[]) uint32 dir_list_size = 0; const char *env_list[8] = { NULL }; uint32 env_list_size = 0; + const char *addr_pool[8] = { NULL }; + uint32 addr_pool_size = 0; #endif #if WASM_ENABLE_DEBUG_INTERP != 0 char *ip_addr = NULL; @@ -312,9 +316,30 @@ main(int argc, char *argv[]) return print_help(); } } + /* TODO: parse the configuration file via --addr-pool-file */ + else if (!strncmp(argv[0], "--addr-pool=", strlen("--addr-pool="))) { + /* like: --addr-pool=100.200.244.255/30 */ + char *token = NULL; + + if ('\0' == argv[0][12]) + return print_help(); + + token = strtok(argv[0] + strlen("--addr-pool="), ","); + while (token) { + if (addr_pool_size >= sizeof(addr_pool) / sizeof(char *)) { + printf("Only allow max address number %d\n", + (int)(sizeof(addr_pool) / sizeof(char *))); + return -1; + } + + addr_pool[addr_pool_size++] = token; + token = strtok(NULL, ";"); + } + } #endif /* WASM_ENABLE_LIBC_WASI */ #if WASM_ENABLE_MULTI_MODULE != 0 - else if (!strncmp(argv[0], MODULE_PATH, strlen(MODULE_PATH))) { + else if (!strncmp(argv[0], + "--module-path=", strlen("--module-path="))) { module_search_path = handle_module_path(argv[0]); if (!strlen(module_search_path)) { return print_help(); @@ -422,6 +447,8 @@ main(int argc, char *argv[]) #if WASM_ENABLE_LIBC_WASI != 0 wasm_runtime_set_wasi_args(wasm_module, dir_list, dir_list_size, NULL, 0, env_list, env_list_size, argv, argc); + + wasm_runtime_set_wasi_addr_pool(wasm_module, addr_pool, addr_pool_size); #endif /* instantiate the module */ diff --git a/samples/socket-api/CMakeLists.txt b/samples/socket-api/CMakeLists.txt new file mode 100644 index 0000000000..16cafdb45e --- /dev/null +++ b/samples/socket-api/CMakeLists.txt @@ -0,0 +1,162 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 2.8...3.18) +project(socket_api_sample) + +####################################### +## Detect toolchain +####################################### +message(CHECK_START "Detecting WASI-SDK at /opt/wasi-sdk") +if(NOT (DEFINED WASI_SDK_DIR OR DEFINED CACHE{WASI_SDK_DIR})) + find_path(WASI_SDK_PARENT + wasi-sdk + PATHS /opt + NO_DEFAULT_PATH + NO_CMAKE_FIND_ROOT_PATH + ) + if(WASI_SDK_PARENT) + set(WASI_SDK_DIR ${WASI_SDK_PARENT}/wasi-sdk) + endif() +endif() +if(WASI_SDK_DIR) + message(CHECK_PASS "found") +else() + message(CHECK_FAIL "not found") +endif() + +if(NOT EXISTS ${WASI_SDK_DIR}) + message(FATAL_ERROR "Please install WASI-SDK under /opt/wasi-sdk") +endif() + +message(CHECK_START "Detecting WASI_TOOLCHAIN_FILE at ${WASI_SDK_DIR}") +find_file(WASI_TOOLCHAIN_FILE + wasi-sdk.cmake + PATHS "${WASI_SDK_DIR}/share/cmake" + NO_DEFAULT_PATH + NO_CMAKE_FIND_ROOT_PATH +) +if(WASI_TOOLCHAIN_FILE) + message(CHECK_PASS "found") +else() + message(CHECK_FAIL "not found") +endif() + +if(WASI_TOOLCHAIN_FILE-NOTFOUND) + message(FATAL_ERROR "Can not find wasi-sdk.cmake under ${WASI_SDK_DIR}") +endif() + +message(CHECK_START "Detecting WASI_SYS_ROOT at ${WASI_SDK_DIR}") +find_path(WASI_SYS_ROOT + wasi-sysroot + PATHS "${WASI_SDK_DIR}/share" + NO_DEFAULT_PATH + NO_CMAKE_FIND_ROOT_PATH +) +if(WASI_SYS_ROOT) + message(CHECK_PASS "found") + set(WASI_SYS_ROOT ${WASI_SYS_ROOT}/wasi-sysroot) +else() + message(CHECK_FAIL "not found") +endif() + +if(WASI_SYS_ROOT-NOTFOUND) + message(FATAL_ERROR "Can not find wasi-sysroot/ under ${WASI_SDK_DIR}") +endif() + +message(STATUS "WASI_SDK_DIR is ${WASI_SDK_DIR}") +message(STATUS "WASI_TOOLCHAIN_FILE is ${WASI_TOOLCHAIN_FILE}") +message(STATUS "WASI_SYS_ROOT is ${WASI_SYS_ROOT}") + +############################################################### +## Build socket applications of wasm version and native version +############################################################### +include(ExternalProject) + +ExternalProject_Add(wasm-app + SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/wasm-src + UPDATE_COMMAND "" + PATCH_COMMAND "" + CONFIGURE_COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/../../wamr-sdk/app/libc-builtin-sysroot/include/pthread.h + ${CMAKE_CURRENT_SOURCE_DIR}/wasm-src/inc + && ${CMAKE_COMMAND} + -DWASI_SDK_PREFIX=${WASI_SDK_DIR} + -DCMAKE_TOOLCHAIN_FILE=${WASI_TOOLCHAIN_FILE} + -DCMAKE_SYSROOT=${WASI_SYS_ROOT} + ${CMAKE_CURRENT_SOURCE_DIR}/wasm-src + BUILD_COMMAND ${CMAKE_COMMAND} --build . + INSTALL_COMMAND ${CMAKE_COMMAND} -E copy + tcp_client.wasm ${CMAKE_CURRENT_SOURCE_DIR}/build + tcp_server.wasm ${CMAKE_CURRENT_SOURCE_DIR}/build + tcp_client.wasm.dump ${CMAKE_CURRENT_SOURCE_DIR}/build + tcp_server.wasm.dump ${CMAKE_CURRENT_SOURCE_DIR}/build +) + +add_executable(tcp_server ${CMAKE_CURRENT_SOURCE_DIR}/wasm-src/tcp_server.c) +target_link_libraries(tcp_server pthread) +add_executable(tcp_client ${CMAKE_CURRENT_SOURCE_DIR}/wasm-src/tcp_client.c) + +############################################ +## Build iwasm with wasi and pthread support +############################################ +string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM) +if (APPLE) + add_definitions(-DBH_PLATFORM_DARWIN) +endif () + +# Reset linker flags +set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") + +# Set WAMR features + +# Set WAMR_BUILD_TARGET, currently values supported: +# "X86_64", "AMD_64", "X86_32", "AARCH64[sub]", "ARM[sub]", "THUMB[sub]", +# "MIPS", "XTENSA", "RISCV64[sub]", "RISCV32[sub]" +if (NOT DEFINED WAMR_BUILD_TARGET) + if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm64|aarch64)") + set (WAMR_BUILD_TARGET "AARCH64") + elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv64") + set (WAMR_BUILD_TARGET "RISCV64") + elseif (CMAKE_SIZEOF_VOID_P EQUAL 8) + # Build as X86_64 by default in 64-bit platform + set (WAMR_BUILD_TARGET "X86_64") + else () + # Build as X86_32 by default in 32-bit platform + set (WAMR_BUILD_TARGET "X86_32") + endif () +endif () + +if (NOT CMAKE_BUILD_TYPE) + set (CMAKE_BUILD_TYPE Release) +endif () + +set(WAMR_BUILD_INTERP 1) +set(WAMR_BUILD_FAST_INTERP 1) +set(WAMR_BUILD_AOT 1) +set(WAMR_BUILD_JIT 0) +set(WAMR_BUILD_LIBC_BUILTIN 1) +set(WAMR_BUILD_LIBC_WASI 1) +set(WAMR_BUILD_LIB_PTHREAD 1) + +# compiling and linking flags +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pie -fPIE") +if (NOT (CMAKE_C_COMPILER MATCHES ".*clang.*" OR CMAKE_C_COMPILER_ID MATCHES ".*Clang")) + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") +endif () +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wformat -Wformat-security") + +# build vmlib static lib +set(WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..) +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) +add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) + +# build iwasm +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) +set (RUNTIME_SOURCE_ALL + ${CMAKE_CURRENT_LIST_DIR}/../../product-mini/platforms/linux/main.c + ${UNCOMMON_SHARED_SOURCE} +) +add_executable (iwasm ${RUNTIME_SOURCE_ALL}) +target_link_libraries(iwasm vmlib -lpthread -lm) diff --git a/samples/socket-api/README.md b/samples/socket-api/README.md new file mode 100644 index 0000000000..320f4a6ada --- /dev/null +++ b/samples/socket-api/README.md @@ -0,0 +1,57 @@ +"socket-api" sample introduction +================================ + +This sample demonstrates how to use WAMR socket-api to develop wasm network applications. +Two wasm applications are provided: tcp-server and tcp-client, and this sample demonstrates +how they communicate with each other. + +## Preparation + +Please install WASI SDK, download the [wasi-sdk release](https://github.com/CraneStation/wasi-sdk/releases) and extract the archive to default path `/opt/wasi-sdk`. +And install wabt, download the [wabt release](https://github.com/WebAssembly/wabt/releases) and extract the archive to default path `/opt/wabt` + +## Build the sample + +```bash +mkdir build +cd build +cmake .. +make +``` + +The file `tcp_server.wasm`, `tcp_client.wasm` and `iwasm` will be created. +And also file `tcp_server` and `tcp_client` of native version are created. + +Note that iwasm is built with libc-wasi and lib-pthread enabled. + +## Run workload + +Start the tcp server, which opens port 1234 and waits for clients to connect. +```bash +cd build +./iwasm --addr-pool=0.0.0.0/15 tcp_server.wasm +``` + +Start the tcp client, which connects the server and receives message. +```bash +cd build +./iwasm --addr-pool=127.0.0.1/15 tcp_client.wasm +``` + +The output of client is like: +```bash +[Client] Create socket +[Client] Connect socket +[Client] Client receive +[Client] 115 bytes received: +Buffer recieved: +Say Hi from the Server +Say Hi from the Server +Say Hi from the Server +Say Hi from the Server +Say Hi from the Server + +[Client] BYE +``` + +Refer to [socket api document](../../doc/socket_api.md) for more details. diff --git a/samples/socket-api/wasm-src/CMakeLists.txt b/samples/socket-api/wasm-src/CMakeLists.txt new file mode 100644 index 0000000000..b83c8f88fd --- /dev/null +++ b/samples/socket-api/wasm-src/CMakeLists.txt @@ -0,0 +1,80 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 2.8...3.18) +project(socket_api_sample_wasm_app) + +message(CHECK_START "Detecting WABT") +if(NOT (DEFINED WABT_DIR OR DEFINED CACHE{WABT_DIR})) + find_path(WABT_DIR + wabt + PATHS /opt + NO_DEFAULT_PATH + NO_CMAKE_FIND_ROOT_PATH + ) + if(DEFINED WABT_DIR) + set(WABT_DIR ${WABT_DIR}/wabt) + endif() +endif() +if(WABT_DIR) + message(CHECK_PASS "found") +else() + message(CHECK_FAIL "not found") +endif() + +message(CHECK_START "Detecting WASM_OBJDUMP at ${WABT_DIR}") +find_program(WASM_OBJDUMP + wasm-objdump + PATHS "${WABT_DIR}/bin" + NO_DEFAULT_PATH + NO_CMAKE_FIND_ROOT_PATH +) +if(WASM_OBJDUMP) + message(CHECK_PASS "found") +else() + message(CHECK_FAIL "not found") +endif() + +set(SRC ${CMAKE_CURRENT_SOURCE_DIR}) + +include(${CMAKE_CURRENT_SOURCE_DIR}/../../../core/iwasm/libraries/lib-socket/lib_socket_wasi.cmake) + +function(COMPILE_WITH_CLANG SOURCE_FILE) + get_filename_component(FILE_NAME ${SOURCE_FILE} NAME_WLE) + + set(WASM_MODULE ${FILE_NAME}.wasm) + + set(MAIN_TARGET_NAME MODULE_${FILE_NAME}) + + add_executable(${MAIN_TARGET_NAME} ${SOURCE_FILE}) + set_target_properties(${MAIN_TARGET_NAME} PROPERTIES OUTPUT_NAME ${WASM_MODULE}) + target_include_directories(${MAIN_TARGET_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/inc) + target_compile_options(${MAIN_TARGET_NAME} INTERFACE -pthread) + target_link_libraries(${MAIN_TARGET_NAME} socket_wasi_ext) + target_link_options(${MAIN_TARGET_NAME} PRIVATE + LINKER:--export=__heap_base + LINKER:--export=__data_end + LINKER:--shared-memory,--max-memory=196608 + LINKER:--no-check-features + LINKER:--allow-undefined + ) + + if(EXISTS ${WASM_OBJDUMP}) + message(STATUS "Dumping ${WASM_MODULE}...") + set(WASM_DUMP ${WASM_MODULE}.dump) + set(DUMP_TARGET_NAME DUMP_${FILE_NAME}) + + add_custom_command(OUTPUT ${WASM_DUMP} + COMMAND ${WASM_OBJDUMP} -dx ${WASM_MODULE} > ${WASM_DUMP} + COMMENT "Dumping ${WASM_MODULE}..." + DEPENDS ${MAIN_TARGET_NAME} + ) + + add_custom_target(${DUMP_TARGET_NAME} ALL + DEPENDS ${WASM_DUMP} + ) + endif() +endfunction() + +compile_with_clang(tcp_server.c) +compile_with_clang(tcp_client.c) diff --git a/test-tools/build-wasi-sdk/include/.gitkeep b/samples/socket-api/wasm-src/inc/.gitkeep similarity index 100% rename from test-tools/build-wasi-sdk/include/.gitkeep rename to samples/socket-api/wasm-src/inc/.gitkeep diff --git a/samples/socket-api/wasm-src/tcp_client.c b/samples/socket-api/wasm-src/tcp_client.c new file mode 100644 index 0000000000..4fcf87074d --- /dev/null +++ b/samples/socket-api/wasm-src/tcp_client.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include +#include +#include +#include +#include +#ifdef __wasi__ +#include +#endif + +int +main(int argc, char *argv[]) +{ + int socket_fd, ret, total_size = 0; + char buffer[1024] = { 0 }; + struct sockaddr_in server_address = { 0 }; + + printf("[Client] Create socket\n"); + socket_fd = socket(AF_INET, SOCK_STREAM, 0); + if (socket_fd == -1) { + perror("Create socket failed"); + return EXIT_FAILURE; + } + + /* 127.0.0.1:1234 */ + server_address.sin_family = AF_INET; + server_address.sin_port = htons(1234); + server_address.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + printf("[Client] Connect socket\n"); + if (connect(socket_fd, (struct sockaddr *)&server_address, + sizeof(server_address)) + == -1) { + perror("Connect failed"); + close(socket_fd); + return EXIT_FAILURE; + } + + printf("[Client] Client receive\n"); + while (1) { + ret = recv(socket_fd, buffer + total_size, sizeof(buffer) - total_size, + 0); + if (ret <= 0) + break; + total_size += ret; + } + + printf("[Client] %d bytes received:\n", total_size); + if (total_size > 0) { + printf("Buffer recieved:\n%s\n", buffer); + } + + close(socket_fd); + printf("[Client] BYE \n"); + return EXIT_SUCCESS; +} diff --git a/samples/socket-api/wasm-src/tcp_server.c b/samples/socket-api/wasm-src/tcp_server.c new file mode 100644 index 0000000000..4b8b4362a9 --- /dev/null +++ b/samples/socket-api/wasm-src/tcp_server.c @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __wasi__ +#include +#endif + +#define WORKER_NUM 5 + +void * +run(void *arg) +{ + const char *message = "Say Hi from the Server\n"; + int new_socket = *(int *)arg; + int i; + + printf("[Server] Communicate with the new connection #%u @ %p ..\n", + new_socket, (void *)(uintptr_t)pthread_self()); + + for (i = 0; i < 5; i++) { + if (send(new_socket, message, strlen(message), 0) < 0) { + perror("Send failed"); + break; + } + } + + printf("[Server] Shuting down the new connection #%u ..\n", new_socket); + shutdown(new_socket, SHUT_RDWR); + + return NULL; +} + +int +main(int argc, char *argv[]) +{ + int socket_fd = -1, addrlen = 0; + struct sockaddr_in addr = { 0 }; + unsigned connections = 0; + pthread_t workers[WORKER_NUM] = { 0 }; + int client_sock_fds[WORKER_NUM] = { 0 }; + + printf("[Server] Create socket\n"); + socket_fd = socket(AF_INET, SOCK_STREAM, 0); + if (socket_fd < 0) { + perror("Create socket failed"); + goto fail; + } + + /* 0.0.0.0:1234 */ + addr.sin_family = AF_INET; + addr.sin_port = htons(1234); + addr.sin_addr.s_addr = htonl(INADDR_ANY); + + printf("[Server] Bind socket\n"); + addrlen = sizeof(addr); + if (bind(socket_fd, (struct sockaddr *)&addr, addrlen) < 0) { + perror("Bind failed"); + goto fail; + } + + printf("[Server] Listening on socket\n"); + if (listen(socket_fd, 3) < 0) { + perror("Listen failed"); + goto fail; + } + + printf("[Server] Wait for clients to connect ..\n"); + while (connections < WORKER_NUM) { + client_sock_fds[connections] = + accept(socket_fd, (struct sockaddr *)&addr, (socklen_t *)&addrlen); + if (client_sock_fds[connections] < 0) { + perror("Accept failed"); + break; + } + + printf("[Server] Client connected\n"); + if (pthread_create(&workers[connections], NULL, run, + &client_sock_fds[connections])) { + perror("Create a worker thread failed"); + shutdown(client_sock_fds[connections], SHUT_RDWR); + break; + } + + connections++; + } + + if (connections == WORKER_NUM) { + printf("[Server] Achieve maximum amount of connections\n"); + } + + for (int i = 0; i < WORKER_NUM; i++) { + pthread_join(workers[i], NULL); + } + + printf("[Server] Shuting down ..\n"); + shutdown(socket_fd, SHUT_RDWR); + sleep(3); + printf("[Server] BYE \n"); + return EXIT_SUCCESS; + +fail: + printf("[Server] Shuting down ..\n"); + if (socket_fd >= 0) + close(socket_fd); + sleep(3); + return EXIT_FAILURE; +}