Implement part of Berkeley Socket API for libc-wasi (#1036)
Refer to [Networking API design](https://github.com/WebAssembly/WASI/issues/370) and [feat(socket): berkeley socket API v2](https://github.com/WebAssembly/WASI/pull/459): - Support the socket API of synchronous mode, including `socket/bind/listen/accept/send/recv/close/shutdown`, the asynchronous mode isn't supported yet. - Support adding `--addr-pool=<pool1,pool2,..>` argument for command line to identify the valid ip address range - Add socket-api sample and update the document
This commit is contained in:
@ -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.
|
||||
@ -2715,44 +2716,246 @@ wasmtime_ssp_random_get(void *buf, size_t nbyte)
|
||||
return 0;
|
||||
}
|
||||
|
||||
__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)
|
||||
{
|
||||
__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, fd, __WASI_RIGHT_SOCK_ACCEPT, 0);
|
||||
if (error != __WASI_ESUCCESS)
|
||||
return error;
|
||||
|
||||
ret = os_socket_accept(fd_number(fo), &new_sock, NULL, NULL);
|
||||
fd_object_release(fo);
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
wasi_ssp_sock_addr_local(
|
||||
#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_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;
|
||||
}
|
||||
|
||||
__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);
|
||||
|
||||
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;
|
||||
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 (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 {
|
||||
assert(wasi_type == __WASI_FILETYPE_SOCKET_STREAM);
|
||||
}
|
||||
|
||||
// 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_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)
|
||||
__wasi_fd_t sock, void *buf, size_t buf_len, size_t *recv_len)
|
||||
{
|
||||
// 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;
|
||||
|
||||
struct fd_object *fo;
|
||||
__wasi_errno_t error =
|
||||
fd_object_get(curfds, &fo, sock, __WASI_RIGHT_FD_READ, 0);
|
||||
__wasi_errno_t error;
|
||||
int ret;
|
||||
|
||||
error = fd_object_get(curfds, &fo, sock, __WASI_RIGHT_FD_READ, 0);
|
||||
if (error != 0) {
|
||||
return error;
|
||||
}
|
||||
|
||||
ssize_t datalen = recvmsg(fd_number(fo), &hdr, nflags);
|
||||
ret = os_socket_recv(fd_number(fo), buf, buf_len);
|
||||
fd_object_release(fo);
|
||||
if (datalen < 0) {
|
||||
if (ret == BHT_ERROR) {
|
||||
return convert_errno(errno);
|
||||
}
|
||||
|
||||
// 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;
|
||||
*recv_len = (size_t)ret;
|
||||
return __WASI_ESUCCESS;
|
||||
}
|
||||
|
||||
__wasi_errno_t
|
||||
@ -2760,34 +2963,25 @@ wasmtime_ssp_sock_send(
|
||||
#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 sock, const void *buf, size_t buf_len, size_t *sent_len)
|
||||
{
|
||||
// Convert input to msghdr.
|
||||
struct msghdr hdr = {
|
||||
.msg_iov = (struct iovec *)si_data,
|
||||
.msg_iovlen = si_data_len,
|
||||
};
|
||||
|
||||
// Attach file descriptors if present.
|
||||
__wasi_errno_t error;
|
||||
|
||||
// Send message.
|
||||
struct fd_object *fo;
|
||||
__wasi_errno_t error;
|
||||
int ret;
|
||||
|
||||
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);
|
||||
fd_object_release(fo);
|
||||
if (len < 0) {
|
||||
error = convert_errno(errno);
|
||||
}
|
||||
else {
|
||||
*so_datalen = (size_t)len;
|
||||
if (error != 0) {
|
||||
return error;
|
||||
}
|
||||
|
||||
out:
|
||||
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
|
||||
@ -2795,34 +2989,22 @@ 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)
|
||||
{
|
||||
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 =
|
||||
fd_object_get(curfds, &fo, sock, __WASI_RIGHT_SOCK_SHUTDOWN, 0);
|
||||
__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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
|
||||
Reference in New Issue
Block a user