From 0ffac101a185753a0cb51f33339763a6de8e3485 Mon Sep 17 00:00:00 2001 From: Marcin Kolny Date: Thu, 1 Sep 2022 16:20:53 +0200 Subject: [PATCH] Add support for IPv6 in WAMR (#1411) For now this implementation only covers posix platforms, as defined in MVP #1336 --- core/iwasm/libraries/debug-engine/gdbserver.c | 2 +- .../lib-socket/src/wasi/wasi_socket_ext.c | 45 +++- .../sandboxed-system-primitives/src/posix.c | 193 +++++++++++++----- .../sandboxed-system-primitives/src/posix.h | 8 +- .../platform/common/posix/posix_socket.c | 78 +++++-- .../platform/include/platform_api_extension.h | 24 ++- core/shared/platform/linux-sgx/sgx_socket.c | 163 ++++++++++----- core/shared/platform/linux-sgx/sgx_socket.h | 7 + core/shared/platform/windows/win_socket.c | 40 +++- samples/socket-api/wasm-src/tcp_client.c | 55 +++-- samples/socket-api/wasm-src/tcp_server.c | 53 +++-- samples/socket-api/wasm-src/tcp_utils.h | 48 +++++ 12 files changed, 551 insertions(+), 165 deletions(-) create mode 100644 samples/socket-api/wasm-src/tcp_utils.h diff --git a/core/iwasm/libraries/debug-engine/gdbserver.c b/core/iwasm/libraries/debug-engine/gdbserver.c index eeb0c06b..2cb6a14f 100644 --- a/core/iwasm/libraries/debug-engine/gdbserver.c +++ b/core/iwasm/libraries/debug-engine/gdbserver.c @@ -59,7 +59,7 @@ wasm_create_gdbserver(const char *host, int32 *port) memset(server->receive_ctx, 0, sizeof(rsp_recv_context_t)); - if (0 != os_socket_create(&listen_fd, 1)) { + if (0 != os_socket_create(&listen_fd, true, true)) { LOG_ERROR("wasm gdb server error: create socket failed"); goto fail; } 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 index f798b3d2..bd008bbd 100644 --- a/core/iwasm/libraries/lib-socket/src/wasi/wasi_socket_ext.c +++ b/core/iwasm/libraries/lib-socket/src/wasi/wasi_socket_ext.c @@ -31,21 +31,38 @@ ipv4_addr_to_wasi_addr(uint32_t addr_num, uint16_t port, __wasi_addr_t *out) out->addr.ip4.addr.n3 = (addr_num & 0x000000FF); } +static void +ipv6_addr_to_wasi_addr(uint16_t *addr, uint16_t port, __wasi_addr_t *out) +{ + out->kind = IPv6; + out->addr.ip6.port = ntohs(port); + out->addr.ip6.addr.n0 = ntohs(addr[0]); + out->addr.ip6.addr.n1 = ntohs(addr[1]); + out->addr.ip6.addr.n2 = ntohs(addr[2]); + out->addr.ip6.addr.n3 = ntohs(addr[3]); + out->addr.ip6.addr.h0 = ntohs(addr[4]); + out->addr.ip6.addr.h1 = ntohs(addr[5]); + out->addr.ip6.addr.h2 = ntohs(addr[6]); + out->addr.ip6.addr.h3 = ntohs(addr[7]); +} + 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); + 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; + assert(sizeof(struct sockaddr_in6) == addrlen); + ipv6_addr_to_wasi_addr( + (uint16_t *)((struct sockaddr_in6 *)sock_addr)->sin6_addr.s6_addr, + ((struct sockaddr_in6 *)sock_addr)->sin6_port, wasi_addr); } else { ret = __WASI_ERRNO_AFNOSUPPORT; @@ -78,8 +95,26 @@ wasi_addr_to_sockaddr(const __wasi_addr_t *wasi_addr, break; } case IPv6: - // TODO: IPV6 - return __WASI_ERRNO_AFNOSUPPORT; + { + struct sockaddr_in6 sock_addr_in6 = { 0 }; + uint16_t *addr_buf = (uint16_t *)sock_addr_in6.sin6_addr.s6_addr; + + addr_buf[0] = htons(wasi_addr->addr.ip6.addr.n0); + addr_buf[1] = htons(wasi_addr->addr.ip6.addr.n1); + addr_buf[2] = htons(wasi_addr->addr.ip6.addr.n2); + addr_buf[3] = htons(wasi_addr->addr.ip6.addr.n3); + addr_buf[4] = htons(wasi_addr->addr.ip6.addr.h0); + addr_buf[5] = htons(wasi_addr->addr.ip6.addr.h1); + addr_buf[6] = htons(wasi_addr->addr.ip6.addr.h2); + addr_buf[7] = htons(wasi_addr->addr.ip6.addr.h3); + + sock_addr_in6.sin6_family = AF_INET6; + sock_addr_in6.sin6_port = htons(wasi_addr->addr.ip6.port); + memcpy(sock_addr, &sock_addr_in6, sizeof(sock_addr_in6)); + + *addrlen = sizeof(sock_addr_in6); + break; + } default: return __WASI_ERRNO_AFNOSUPPORT; } 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 e3b79a88..64e9298f 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 @@ -2948,6 +2948,35 @@ wasi_ssp_sock_addr_remote( return __WASI_ESUCCESS; } +static bool +wasi_addr_to_string(__wasi_addr_t *addr, char *buf, size_t buflen) +{ + if (addr->kind == IPv4) { + const char *format = "%u.%u.%u.%u"; + + assert(buflen >= 16); + + snprintf(buf, buflen, format, addr->addr.ip4.addr.n0, + addr->addr.ip4.addr.n1, addr->addr.ip4.addr.n2, + addr->addr.ip4.addr.n3); + + return true; + } + else if (addr->kind == IPv6) { + const char *format = "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x"; + __wasi_addr_ip6_t ipv6 = addr->addr.ip6.addr; + + assert(buflen >= 40); + + snprintf(buf, buflen, format, ipv6.n0, ipv6.n1, ipv6.n2, ipv6.n3, + ipv6.h0, ipv6.h1, ipv6.h2, ipv6.h3); + + return true; + } + + return false; +} + __wasi_errno_t wasi_ssp_sock_bind( #if !defined(WASMTIME_SSP_STATIC_CURFDS) @@ -2955,15 +2984,15 @@ wasi_ssp_sock_bind( #endif __wasi_fd_t fd, __wasi_addr_t *addr) { - char buf[24] = { 0 }; - const char *format = "%u.%u.%u.%u"; + char buf[48] = { 0 }; struct fd_object *fo; __wasi_errno_t error; - int port = addr->addr.ip4.port; + int port = addr->kind == IPv4 ? addr->addr.ip4.port : addr->addr.ip6.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 (!wasi_addr_to_string(addr, buf, sizeof(buf))) { + return __WASI_EPROTONOSUPPORT; + } if (!addr_pool_search(addr_pool, buf)) { return __WASI_EACCES; @@ -3017,7 +3046,8 @@ wasi_ssp_sock_addr_resolve( addr_info_size < *max_info_size ? addr_info_size : *max_info_size; for (size_t i = 0; i < actual_info_size; i++) { - addr_info[i].type = wamr_addr_info[i].is_tcp ? SOCK_STREAM : SOCK_DGRAM; + addr_info[i].type = + wamr_addr_info[i].is_tcp ? SOCKET_STREAM : SOCKET_DGRAM; if (wamr_addr_info[i].is_ipv4) { ipv4_addr_to_wasi_addr(*(uint32_t *)wamr_addr_info[i].addr, wamr_addr_info[i].port, &addr_info[i].addr); @@ -3039,14 +3069,14 @@ wasi_ssp_sock_connect( #endif __wasi_fd_t fd, __wasi_addr_t *addr) { - char buf[24] = { 0 }; - const char *format = "%u.%u.%u.%u"; + char buf[48] = { 0 }; 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 (!wasi_addr_to_string(addr, buf, sizeof(buf))) { + return __WASI_EPROTONOSUPPORT; + } if (!addr_pool_search(addr_pool, buf)) { return __WASI_EACCES; @@ -3056,7 +3086,9 @@ wasi_ssp_sock_connect( if (error != __WASI_ESUCCESS) return error; - ret = os_socket_connect(fd_number(fo), buf, addr->addr.ip4.port); + ret = os_socket_connect(fd_number(fo), buf, + addr->kind == IPv4 ? addr->addr.ip4.port + : addr->addr.ip6.port); fd_object_release(fo); if (BHT_OK != ret) { return convert_errno(errno); @@ -3097,7 +3129,8 @@ wasi_ssp_sock_open( __wasi_fd_t *sockfd) { bh_socket_t sock; - int tcp_or_udp = 0; + bool is_tcp = SOCKET_DGRAM == socktype ? false : true; + bool is_ipv4 = INET6 ? false : true; int ret; __wasi_filetype_t wasi_type; __wasi_rights_t max_base, max_inheriting; @@ -3105,13 +3138,7 @@ wasi_ssp_sock_open( (void)poolfd; - if (INET4 != af) { - return __WASI_EAFNOSUPPORT; - } - - tcp_or_udp = SOCKET_DGRAM == socktype ? 0 : 1; - - ret = os_socket_create(&sock, tcp_or_udp); + ret = os_socket_create(&sock, is_ipv4, is_tcp); if (BHT_OK != ret) { return convert_errno(errno); } @@ -3337,9 +3364,8 @@ fd_prestats_destroy(struct fd_prestats *pt) bool addr_pool_init(struct addr_pool *addr_pool) { - addr_pool->next = NULL; - addr_pool->addr = 0; - addr_pool->mask = 0; + memset(addr_pool, 0, sizeof(*addr_pool)); + return true; } @@ -3348,6 +3374,7 @@ addr_pool_insert(struct addr_pool *addr_pool, const char *addr, uint8 mask) { struct addr_pool *cur = addr_pool; struct addr_pool *next; + bh_inet_network_output_t target; if (!addr_pool) { return false; @@ -3359,9 +3386,20 @@ addr_pool_insert(struct addr_pool *addr_pool, const char *addr, uint8 mask) next->next = NULL; next->mask = mask; - if (os_socket_inet_network(addr, &next->addr) != BHT_OK) { - wasm_runtime_free(next); - return false; + + if (os_socket_inet_network(true, addr, &target) != BHT_OK) { + // If parsing IPv4 fails, try IPv6 + if (os_socket_inet_network(false, addr, &target) != BHT_OK) { + wasm_runtime_free(next); + return false; + } + next->type = IPv6; + bh_memcpy_s(next->addr.ip6, sizeof(next->addr.ip6), target.ipv6, + sizeof(target.ipv6)); + } + else { + next->type = IPv4; + next->addr.ip4 = target.ipv4; } /* attach with */ @@ -3372,47 +3410,106 @@ addr_pool_insert(struct addr_pool *addr_pool, const char *addr, uint8 mask) return true; } -static bool -compare_address(const struct addr_pool *addr_pool_entry, const char *addr) +static inline size_t +min(size_t a, size_t b) { - /* host order */ - uint32 target; - uint32 address = addr_pool_entry->addr; - /* 0.0.0.0 means any address */ - if (0 == address) { + return a > b ? b : a; +} + +static void +init_address_mask(uint8_t *buf, size_t buflen, size_t mask) +{ + size_t element_size = sizeof(uint8_t) * 8; + + for (size_t i = 0; i < buflen; i++) { + if (mask <= i * element_size) { + buf[i] = 0; + } + else { + size_t offset = min(mask - i * element_size, element_size); + buf[i] = (~0u) << (element_size - offset); + } + } +} + +/* target must be in network byte order */ +static bool +compare_address(const struct addr_pool *addr_pool_entry, + bh_inet_network_output_t *target) +{ + uint8_t maskbuf[16] = { 0 }; + uint8_t basebuf[16] = { 0 }; + size_t addr_size; + uint8_t max_addr_mask; + + if (addr_pool_entry->type == IPv4) { + uint32_t addr_ip4 = htonl(addr_pool_entry->addr.ip4); + bh_memcpy_s(basebuf, sizeof(addr_ip4), &addr_ip4, sizeof(addr_ip4)); + addr_size = 4; + } + else { + uint16_t partial_addr_ip6; + for (int i = 0; i < 8; i++) { + partial_addr_ip6 = htons(addr_pool_entry->addr.ip6[i]); + bh_memcpy_s(&basebuf[i * sizeof(partial_addr_ip6)], + sizeof(partial_addr_ip6), &partial_addr_ip6, + sizeof(partial_addr_ip6)); + } + addr_size = 16; + } + max_addr_mask = addr_size * 8; + + /* IPv4 0.0.0.0 or IPv6 :: means any address */ + if (basebuf[0] == 0 && !memcmp(basebuf, basebuf + 1, addr_size - 1)) { return true; } - if (os_socket_inet_network(addr, &target) != BHT_OK) { + /* No support for invalid mask value */ + if (addr_pool_entry->mask > max_addr_mask) { return false; } - const uint32 max_mask_value = 32; - /* no support for invalid mask values */ - if (addr_pool_entry->mask > max_mask_value) { - return false; + init_address_mask(maskbuf, addr_size, addr_pool_entry->mask); + + for (size_t i = 0; i < addr_size; i++) { + uint8_t addr_mask = target->data[i] & maskbuf[i]; + uint8_t range_mask = basebuf[i] & maskbuf[i]; + if (addr_mask != range_mask) { + return false; + } } - /* convert mask number into 32-bit mask value, i.e. mask /24 will be - converted to 4294967040 (binary: 11111111 11111111 11111111 00000000) */ - uint32 mask = 0; - for (int i = 0; i < addr_pool_entry->mask; i++) { - mask |= 1 << (max_mask_value - 1 - i); - } - - uint32 first_address = address & mask; - uint32 last_address = address | (~mask); - return first_address <= target && target <= last_address; + return true; } bool addr_pool_search(struct addr_pool *addr_pool, const char *addr) { struct addr_pool *cur = addr_pool->next; + bh_inet_network_output_t target; + __wasi_addr_type_t addr_type; + + if (os_socket_inet_network(true, addr, &target) != BHT_OK) { + size_t i; + + if (os_socket_inet_network(false, addr, &target) != BHT_OK) { + return false; + } + addr_type = IPv6; + for (i = 0; i < sizeof(target.ipv6) / sizeof(target.ipv6[0]); i++) { + target.ipv6[i] = htons(target.ipv6[i]); + } + } + else { + addr_type = IPv4; + target.ipv4 = htonl(target.ipv4); + } while (cur) { - if (compare_address(cur, addr)) + if (cur->type == addr_type && compare_address(cur, &target)) { return true; + } + 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 ad124c82..7a593390 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 @@ -47,9 +47,13 @@ struct argv_environ_values { }; struct addr_pool { - struct addr_pool *next; /* addr and mask in host order */ - uint32 addr; + union { + uint32 ip4; + uint16 ip6[8]; + } addr; + struct addr_pool *next; + __wasi_addr_type_t type; uint8 mask; }; diff --git a/core/shared/platform/common/posix/posix_socket.c b/core/shared/platform/common/posix/posix_socket.c index ccfde372..27609b1b 100644 --- a/core/shared/platform/common/posix/posix_socket.c +++ b/core/shared/platform/common/posix/posix_socket.c @@ -9,28 +9,45 @@ #include #include -static void -textual_addr_to_sockaddr(const char *textual, int port, struct sockaddr_in *out) +static bool +textual_addr_to_sockaddr(const char *textual, int port, struct sockaddr *out) { + struct sockaddr_in *v4; + struct sockaddr_in6 *v6; + assert(textual); - out->sin_family = AF_INET; - out->sin_port = htons(port); - out->sin_addr.s_addr = inet_addr(textual); + v4 = (struct sockaddr_in *)out; + if (inet_pton(AF_INET, textual, &v4->sin_addr.s_addr) == 1) { + v4->sin_family = AF_INET; + v4->sin_port = htons(port); + return true; + } + + v6 = (struct sockaddr_in6 *)out; + if (inet_pton(AF_INET6, textual, &v6->sin6_addr.s6_addr) == 1) { + v6->sin6_family = AF_INET6; + v6->sin6_port = htons(port); + return true; + } + + return false; } int -os_socket_create(bh_socket_t *sock, int tcp_or_udp) +os_socket_create(bh_socket_t *sock, bool is_ipv4, bool is_tcp) { + int af = is_ipv4 ? AF_INET : AF_INET6; + if (!sock) { return BHT_ERROR; } - if (1 == tcp_or_udp) { - *sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (is_tcp) { + *sock = socket(af, SOCK_STREAM, IPPROTO_TCP); } - else if (0 == tcp_or_udp) { - *sock = socket(AF_INET, SOCK_DGRAM, 0); + else { + *sock = socket(af, SOCK_DGRAM, 0); } return (*sock == -1) ? BHT_ERROR : BHT_OK; @@ -39,7 +56,7 @@ os_socket_create(bh_socket_t *sock, int tcp_or_udp) int os_socket_bind(bh_socket_t socket, const char *host, int *port) { - struct sockaddr_in addr; + struct sockaddr_storage addr; struct linger ling; socklen_t socklen; int ret; @@ -60,7 +77,9 @@ os_socket_bind(bh_socket_t socket, const char *host, int *port) goto fail; } - textual_addr_to_sockaddr(host, *port, &addr); + if (!textual_addr_to_sockaddr(host, *port, (struct sockaddr *)&addr)) { + goto fail; + } ret = bind(socket, (struct sockaddr *)&addr, sizeof(addr)); if (ret < 0) { @@ -72,7 +91,9 @@ os_socket_bind(bh_socket_t socket, const char *host, int *port) goto fail; } - *port = ntohs(addr.sin_port); + *port = ntohs(addr.ss_family == AF_INET + ? ((struct sockaddr_in *)&addr)->sin_port + : ((struct sockaddr_in6 *)&addr)->sin6_port); return BHT_OK; @@ -113,7 +134,7 @@ os_socket_accept(bh_socket_t server_sock, bh_socket_t *sock, void *addr, struct sockaddr addr_tmp; unsigned int len = sizeof(struct sockaddr); - *sock = accept(server_sock, (struct sockaddr *)&addr_tmp, &len); + *sock = accept(server_sock, &addr_tmp, &len); if (*sock < 0) { return BHT_ERROR; @@ -125,11 +146,13 @@ os_socket_accept(bh_socket_t server_sock, bh_socket_t *sock, void *addr, 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); + struct sockaddr_storage addr_in = { 0 }; + socklen_t addr_len = sizeof(struct sockaddr_storage); int ret = 0; - textual_addr_to_sockaddr(addr, port, &addr_in); + if (!textual_addr_to_sockaddr(addr, port, (struct sockaddr *)&addr_in)) { + return BHT_ERROR; + } ret = connect(socket, (struct sockaddr *)&addr_in, addr_len); if (ret == -1) { @@ -166,13 +189,28 @@ os_socket_shutdown(bh_socket_t socket) } int -os_socket_inet_network(const char *cp, uint32 *out) +os_socket_inet_network(bool is_ipv4, const char *cp, + bh_inet_network_output_t *out) { if (!cp) return BHT_ERROR; - /* Note: ntohl(INADDR_NONE) == INADDR_NONE */ - *out = ntohl(inet_addr(cp)); + if (is_ipv4) { + if (inet_pton(AF_INET, cp, &out->ipv4) != 1) { + return BHT_ERROR; + } + /* Note: ntohl(INADDR_NONE) == INADDR_NONE */ + out->ipv4 = ntohl(out->ipv4); + } + else { + if (inet_pton(AF_INET6, cp, out->ipv6) != 1) { + return BHT_ERROR; + } + for (int i = 0; i < 8; i++) { + out->ipv6[i] = ntohs(out->ipv6[i]); + } + } + return BHT_OK; } diff --git a/core/shared/platform/include/platform_api_extension.h b/core/shared/platform/include/platform_api_extension.h index 0ab0d15d..b4e75266 100644 --- a/core/shared/platform/include/platform_api_extension.h +++ b/core/shared/platform/include/platform_api_extension.h @@ -296,12 +296,13 @@ os_sem_unlink(const char *name); * Create a socket * * @param sock [OUTPUT] the pointer of socket - * @param tcp_or_udp 1 for tcp, 0 for udp + * @param is_ipv4 true for IPv4, false for IPv6 + * @param is_tcp true for tcp, false for udp * * @return 0 if success, -1 otherwise */ int -os_socket_create(bh_socket_t *sock, int tcp_or_udp); +os_socket_create(bh_socket_t *sock, bool is_ipv4, bool is_tcp); /** * Assign the address and port to the socket @@ -412,17 +413,30 @@ os_socket_close(bh_socket_t socket); int os_socket_shutdown(bh_socket_t socket); +typedef union { + uint32 ipv4; + uint16 ipv6[8]; + uint8_t data[0]; +} bh_inet_network_output_t; + /** * 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 + * @param is_ipv4 a flag that indicates whether the string is an IPv4 or + * IPv6 address * - * @return On success, the converted address is returned. + * @param cp a string in IPv4 numbers-and-dots notation or IPv6 + * numbers-and-colons notation + * + * @param out an output buffer to store binary address + * + * @return On success, the function returns 0. * If the input is invalid, -1 is returned */ int -os_socket_inet_network(const char *cp, uint32 *out); +os_socket_inet_network(bool is_ipv4, const char *cp, + bh_inet_network_output_t *out); typedef struct { uint8_t addr[16]; diff --git a/core/shared/platform/linux-sgx/sgx_socket.c b/core/shared/platform/linux-sgx/sgx_socket.c index c588d7f9..4f422c96 100644 --- a/core/shared/platform/linux-sgx/sgx_socket.c +++ b/core/shared/platform/linux-sgx/sgx_socket.c @@ -92,7 +92,7 @@ swap16(uint8 *pData) *(pData + 1) = value; } -static uint32 +uint32 htonl(uint32 value) { uint32 ret; @@ -111,7 +111,7 @@ ntohl(uint32 value) return htonl(value); } -static uint16 +uint16 htons(uint16 value) { uint16 ret; @@ -132,57 +132,96 @@ ntohs(uint16 value) /* Coming from musl, under MIT license */ static int -__inet_aton(const char *s0, struct in_addr *dest) +hexval(unsigned c) { - const char *s = s0; - unsigned char *d = (void *)dest; - unsigned long a[4] = { 0 }; - char *z; - int i; - - for (i = 0; i < 4; i++) { - a[i] = strtoul(s, &z, 0); - if (z == s || (*z && *z != '.') || !isdigit(*s)) - return 0; - if (!*z) - break; - s = z + 1; - } - if (i == 4) - return 0; - switch (i) { - case 0: - a[1] = a[0] & 0xffffff; - a[0] >>= 24; - case 1: - a[2] = a[1] & 0xffff; - a[1] >>= 16; - case 2: - a[3] = a[2] & 0xff; - a[2] >>= 8; - } - for (i = 0; i < 4; i++) { - if (a[i] > 255) - return 0; - d[i] = a[i]; - } - return 1; + if (c - '0' < 10) + return c - '0'; + c |= 32; + if (c - 'a' < 6) + return c - 'a' + 10; + return -1; } /* Coming from musl, under MIT license */ static int -inet_addr(const char *p) +inet_pton(int af, const char *restrict s, void *restrict a0) { - struct in_addr a; - if (!__inet_aton(p, &a)) + uint16_t ip[8]; + unsigned char *a = a0; + int i, j, v, d, brk = -1, need_v4 = 0; + + if (af == AF_INET) { + for (i = 0; i < 4; i++) { + for (v = j = 0; j < 3 && isdigit(s[j]); j++) + v = 10 * v + s[j] - '0'; + if (j == 0 || (j > 1 && s[0] == '0') || v > 255) + return 0; + a[i] = v; + if (s[j] == 0 && i == 3) + return 1; + if (s[j] != '.') + return 0; + s += j + 1; + } + return 0; + } + else if (af != AF_INET6) { + errno = EAFNOSUPPORT; return -1; - return a.s_addr; + } + + if (*s == ':' && *++s != ':') + return 0; + + for (i = 0;; i++) { + if (s[0] == ':' && brk < 0) { + brk = i; + ip[i & 7] = 0; + if (!*++s) + break; + if (i == 7) + return 0; + continue; + } + for (v = j = 0; j < 4 && (d = hexval(s[j])) >= 0; j++) + v = 16 * v + d; + if (j == 0) + return 0; + ip[i & 7] = v; + if (!s[j] && (brk >= 0 || i == 7)) + break; + if (i == 7) + return 0; + if (s[j] != ':') { + if (s[j] != '.' || (i < 6 && brk < 0)) + return 0; + need_v4 = 1; + i++; + break; + } + s += j + 1; + } + if (brk >= 0) { + memmove(ip + brk + 7 - i, ip + brk, 2 * (i + 1 - brk)); + for (j = 0; j < 7 - i; j++) + ip[brk + j] = 0; + } + for (j = 0; j < 8; j++) { + *a++ = ip[j] >> 8; + *a++ = ip[j]; + } + if (need_v4 && inet_pton(AF_INET, (void *)s, a - 4) <= 0) + return 0; + return 1; } static int -inet_network(const char *p) +inet_addr(const char *p) { - return ntohl(inet_addr(p)); + struct in_addr a; + if (!inet_pton(AF_INET, p, &a)) + return -1; + return a.s_addr; } /** In-enclave implementation of POSIX functions end **/ @@ -528,21 +567,30 @@ os_socket_connect(bh_socket_t socket, const char *addr, int port) } int -os_socket_create(bh_socket_t *sock, int tcp_or_udp) +os_socket_create(bh_socket_t *sock, bool is_ipv4, bool is_tcp) { + int af; + if (!sock) { return BHT_ERROR; } - if (1 == tcp_or_udp) { - if (ocall_socket(sock, AF_INET, SOCK_STREAM, IPPROTO_TCP) - != SGX_SUCCESS) { + if (is_ipv4) { + af = AF_INET; + } + else { + errno = ENOSYS; + return BHT_ERROR; + } + + if (is_tcp) { + if (ocall_socket(sock, af, SOCK_STREAM, IPPROTO_TCP) != SGX_SUCCESS) { TRACE_OCALL_FAIL(); return -1; } } - else if (0 == tcp_or_udp) { - if (ocall_socket(sock, AF_INET, SOCK_DGRAM, 0) != SGX_SUCCESS) { + else { + if (ocall_socket(sock, af, SOCK_DGRAM, 0) != SGX_SUCCESS) { TRACE_OCALL_FAIL(); return -1; } @@ -557,12 +605,27 @@ os_socket_create(bh_socket_t *sock, int tcp_or_udp) } int -os_socket_inet_network(const char *cp, uint32 *out) +os_socket_inet_network(bool is_ipv4, const char *cp, + bh_inet_network_output_t *out) { if (!cp) return BHT_ERROR; - *out = inet_network(cp); + if (is_ipv4) { + if (inet_pton(AF_INET, cp, &out->ipv4) != 1) { + return BHT_ERROR; + } + /* Note: ntohl(INADDR_NONE) == INADDR_NONE */ + out->ipv4 = ntohl(out->ipv4); + } + else { + if (inet_pton(AF_INET6, cp, out->ipv6) != 1) { + return BHT_ERROR; + } + for (int i = 0; i < 8; i++) { + out->ipv6[i] = ntohs(out->ipv6[i]); + } + } return BHT_OK; } diff --git a/core/shared/platform/linux-sgx/sgx_socket.h b/core/shared/platform/linux-sgx/sgx_socket.h index 19e2b8cd..adb12967 100644 --- a/core/shared/platform/linux-sgx/sgx_socket.h +++ b/core/shared/platform/linux-sgx/sgx_socket.h @@ -47,6 +47,7 @@ extern "C" { /* Address families. */ #define AF_INET 2 /* IP protocol family. */ +#define AF_INET6 10 /* IP version 6. */ /* Standard well-defined IP protocols. */ #define IPPROTO_TCP 6 /* Transmission Control Protocol. */ @@ -98,6 +99,12 @@ struct sockaddr { uint32_t ntohl(uint32_t value); +uint32_t +htonl(uint32_t value); + +uint16_t +htons(uint16_t value); + int socket(int domain, int type, int protocol); diff --git a/core/shared/platform/windows/win_socket.c b/core/shared/platform/windows/win_socket.c index ae502bfc..67660ee9 100644 --- a/core/shared/platform/windows/win_socket.c +++ b/core/shared/platform/windows/win_socket.c @@ -37,17 +37,27 @@ deinit_winsock() } int -os_socket_create(bh_socket_t *sock, int tcp_or_udp) +os_socket_create(bh_socket_t *sock, bool is_ipv4, bool is_tcp) { + int af; + if (!sock) { return BHT_ERROR; } - if (1 == tcp_or_udp) { - *sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (is_ipv4) { + af = AF_INET; } - else if (0 == tcp_or_udp) { - *sock = socket(AF_INET, SOCK_DGRAM, 0); + else { + errno = ENOSYS; + return BHT_ERROR; + } + + if (is_tcp) { + *sock = socket(af, SOCK_STREAM, IPPROTO_TCP); + } + else { + *sock = socket(af, SOCK_DGRAM, 0); } return (*sock == -1) ? BHT_ERROR : BHT_OK; @@ -154,12 +164,28 @@ os_socket_shutdown(bh_socket_t socket) } int -os_socket_inet_network(const char *cp, uint32 *out) +os_socket_inet_network(bool is_ipv4, const char *cp, + bh_inet_network_output_t *out) { if (!cp) return BHT_ERROR; - *out = inet_addr(cp); + if (is_ipv4) { + if (inet_pton(AF_INET, cp, &out->ipv4) != 1) { + return BHT_ERROR; + } + /* Note: ntohl(INADDR_NONE) == INADDR_NONE */ + out->ipv4 = ntohl(out->ipv4); + } + else { + if (inet_pton(AF_INET6, cp, out->ipv6) != 1) { + return BHT_ERROR; + } + for (int i = 0; i < 8; i++) { + out->ipv6[i] = ntohs(out->ipv6[i]); + } + } + return BHT_OK; } diff --git a/samples/socket-api/wasm-src/tcp_client.c b/samples/socket-api/wasm-src/tcp_client.c index e4a7ea34..06892070 100644 --- a/samples/socket-api/wasm-src/tcp_client.c +++ b/samples/socket-api/wasm-src/tcp_client.c @@ -2,6 +2,7 @@ * Copyright (C) 2019 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ +#include "tcp_utils.h" #include #include @@ -14,28 +15,50 @@ #include #endif +static void +init_sockaddr_inet(struct sockaddr_in *addr) +{ + /* 127.0.0.1:1234 */ + addr->sin_family = AF_INET; + addr->sin_port = htons(1234); + addr->sin_addr.s_addr = htonl(INADDR_LOOPBACK); +} + +static void +init_sockaddr_inet6(struct sockaddr_in6 *addr) +{ + /* [::1]:1234 */ + addr->sin6_family = AF_INET6; + addr->sin6_port = htons(1234); + addr->sin6_addr = in6addr_loopback; +} + int main(int argc, char *argv[]) { - int socket_fd, ret, total_size = 0; + int socket_fd, ret, total_size = 0, af; char buffer[1024] = { 0 }; - char ip_string[16] = { 0 }; - struct sockaddr_in server_address = { 0 }; - struct sockaddr_in local_address = { 0 }; + char ip_string[64] = { 0 }; socklen_t len; + struct sockaddr_storage server_address = { 0 }; + struct sockaddr_storage local_address = { 0 }; + + if (argc > 1 && strcmp(argv[1], "inet6") == 0) { + af = AF_INET6; + init_sockaddr_inet6((struct sockaddr_in6 *)&server_address); + } + else { + af = AF_INET; + init_sockaddr_inet((struct sockaddr_in *)&server_address); + } printf("[Client] Create socket\n"); - socket_fd = socket(AF_INET, SOCK_STREAM, 0); + socket_fd = socket(af, 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)) @@ -53,11 +76,15 @@ main(int argc, char *argv[]) return EXIT_FAILURE; } - inet_ntop(AF_INET, &local_address.sin_addr, ip_string, - sizeof(ip_string) / sizeof(ip_string[0])); + if (sockaddr_to_string((struct sockaddr *)&local_address, ip_string, + sizeof(ip_string) / sizeof(ip_string[0])) + != 0) { + printf("[Client] failed to parse local address\n"); + close(socket_fd); + return EXIT_FAILURE; + } - printf("[Client] Local address is: %s:%d\n", ip_string, - ntohs(local_address.sin_port)); + printf("[Client] Local address is: %s\n", ip_string); printf("[Client] Client receive\n"); while (1) { diff --git a/samples/socket-api/wasm-src/tcp_server.c b/samples/socket-api/wasm-src/tcp_server.c index b7a79aaf..c0096c45 100644 --- a/samples/socket-api/wasm-src/tcp_server.c +++ b/samples/socket-api/wasm-src/tcp_server.c @@ -2,6 +2,8 @@ * Copyright (C) 2019 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ +#include "tcp_utils.h" + #include #include #include @@ -41,28 +43,50 @@ run(void *arg) return NULL; } +static void +init_sockaddr_inet(struct sockaddr_in *addr) +{ + /* 0.0.0.0:1234 */ + addr->sin_family = AF_INET; + addr->sin_port = htons(1234); + addr->sin_addr.s_addr = htonl(INADDR_ANY); +} + +static void +init_sockaddr_inet6(struct sockaddr_in6 *addr) +{ + /* [::]:1234 */ + addr->sin6_family = AF_INET6; + addr->sin6_port = htons(1234); + addr->sin6_addr = in6addr_any; +} + int main(int argc, char *argv[]) { - int socket_fd = -1, addrlen = 0; - struct sockaddr_in addr = { 0 }; + int socket_fd = -1, addrlen = 0, af; + struct sockaddr_storage addr = { 0 }; unsigned connections = 0; pthread_t workers[WORKER_NUM] = { 0 }; int client_sock_fds[WORKER_NUM] = { 0 }; - char ip_string[16]; + char ip_string[64]; + + if (argc > 1 && strcmp(argv[1], "inet6") == 0) { + af = AF_INET6; + init_sockaddr_inet6((struct sockaddr_in6 *)&addr); + } + else { + af = AF_INET; + init_sockaddr_inet((struct sockaddr_in *)&addr); + } printf("[Server] Create socket\n"); - socket_fd = socket(AF_INET, SOCK_STREAM, 0); + socket_fd = socket(af, 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) { @@ -85,11 +109,14 @@ main(int argc, char *argv[]) break; } - inet_ntop(AF_INET, &addr.sin_addr, ip_string, - sizeof(ip_string) / sizeof(ip_string[0])); + if (sockaddr_to_string((struct sockaddr *)&addr, ip_string, + sizeof(ip_string) / sizeof(ip_string[0])) + != 0) { + printf("[Server] failed to parse client address\n"); + goto fail; + } - printf("[Server] Client connected (%s:%d)\n", ip_string, - ntohs(addr.sin_port)); + printf("[Server] Client connected (%s)\n", ip_string); if (pthread_create(&workers[connections], NULL, run, &client_sock_fds[connections])) { perror("Create a worker thread failed"); diff --git a/samples/socket-api/wasm-src/tcp_utils.h b/samples/socket-api/wasm-src/tcp_utils.h new file mode 100644 index 00000000..b69135b7 --- /dev/null +++ b/samples/socket-api/wasm-src/tcp_utils.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2022 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef TCP_UTILS_H +#define TCP_UTILS_H + +#include +#include +#include + +int +sockaddr_to_string(struct sockaddr *addr, char *str, size_t len) +{ + uint16_t port; + char ip_string[64]; + void *addr_buf; + int ret; + + switch (addr->sa_family) { + case AF_INET: + { + struct sockaddr_in *addr_in = (struct sockaddr_in *)addr; + port = addr_in->sin_port; + addr_buf = &addr_in->sin_addr; + break; + } + case AF_INET6: + { + struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)addr; + port = addr_in6->sin6_port; + addr_buf = &addr_in6->sin6_addr; + break; + } + default: + return -1; + } + + inet_ntop(addr->sa_family, addr_buf, ip_string, + sizeof(ip_string) / sizeof(ip_string[0])); + + ret = snprintf(str, len, "%s:%d", ip_string, ntohs(port)); + + return ret > 0 && (size_t)ret < len ? 0 : -1; +} + +#endif /* TCP_UTILS_H */ \ No newline at end of file