From 5c6ffc3fcb0ccf7335a6ae48a8dd9e33fa660eaf Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Wed, 27 Mar 2024 14:20:37 -0600 Subject: [PATCH] implement `getsockname`, `getpeername`, and `getaddrinfo` This also includes stubs for `gethostbyname`, `gethostbyaddr`, etc. which were necessary to get CPython to build. I believe it will be possible to implement them all properly at some point, but don't have the bandwidth at the moment. Signed-off-by: Joel Dice Co-authored-by: Dave Bakker Signed-off-by: Joel Dice --- Makefile | 9 +- expected/wasm32-wasip2/defined-symbols.txt | 16 ++ expected/wasm32-wasip2/include-all.c | 1 + expected/wasm32-wasip2/predefined-macros.txt | 41 ++++ libc-bottom-half/sources/getsockpeername.c | 229 ++++++++++++++++++ libc-bottom-half/sources/netdb.c | 238 +++++++++++++++++++ libc-top-half/musl/include/netdb.h | 5 + 7 files changed, 537 insertions(+), 2 deletions(-) create mode 100644 libc-bottom-half/sources/getsockpeername.c create mode 100644 libc-bottom-half/sources/netdb.c diff --git a/Makefile b/Makefile index 18471e2bf..41551299d 100644 --- a/Makefile +++ b/Makefile @@ -92,7 +92,9 @@ LIBC_BOTTOM_HALF_OMIT_SOURCES := \ $(LIBC_BOTTOM_HALF_SOURCES)/accept-wasip2.c \ $(LIBC_BOTTOM_HALF_SOURCES)/shutdown.c \ $(LIBC_BOTTOM_HALF_SOURCES)/sockopt.c \ - $(LIBC_BOTTOM_HALF_SOURCES)/poll-wasip2.c + $(LIBC_BOTTOM_HALF_SOURCES)/poll-wasip2.c \ + $(LIBC_BOTTOM_HALF_SOURCES)/getsockpeername.c \ + $(LIBC_BOTTOM_HALF_SOURCES)/netdb.c LIBC_BOTTOM_HALF_ALL_SOURCES := $(filter-out $(LIBC_BOTTOM_HALF_OMIT_SOURCES),$(LIBC_BOTTOM_HALF_ALL_SOURCES)) # Omit p2-specific headers from include-all.c test. INCLUDE_ALL_CLAUSES := -not -name wasip2.h -not -name descriptor_table.h @@ -495,7 +497,6 @@ MUSL_OMIT_HEADERS += \ "sys/auxv.h" \ "pwd.h" "shadow.h" "grp.h" \ "mntent.h" \ - "netdb.h" \ "resolv.h" \ "pty.h" \ "setjmp.h" \ @@ -519,6 +520,10 @@ MUSL_OMIT_HEADERS += \ "sys/sysmacros.h" \ "aio.h" +ifeq ($(WASI_SNAPSHOT), p1) +MUSL_OMIT_HEADERS += "netdb.h" +endif + ifeq ($(THREAD_MODEL), single) # Remove headers not supported in single-threaded mode. MUSL_OMIT_HEADERS += "pthread.h" diff --git a/expected/wasm32-wasip2/defined-symbols.txt b/expected/wasm32-wasip2/defined-symbols.txt index 3b11fb413..cbd0f8eb3 100644 --- a/expected/wasm32-wasip2/defined-symbols.txt +++ b/expected/wasm32-wasip2/defined-symbols.txt @@ -709,6 +709,7 @@ fputws_unlocked fread fread_unlocked free +freeaddrinfo freelocale freopen freopen64 @@ -742,6 +743,7 @@ get_avphys_pages get_nprocs get_nprocs_conf get_phys_pages +getaddrinfo getc getc_unlocked getchar @@ -753,14 +755,22 @@ getdelim getdomainname getentropy getenv +gethostbyaddr +gethostbyname gethostid getline +getnameinfo getopt getopt_long getopt_long_only getpagesize +getpeername getpid +getprotobyname getrusage +getservbyname +getservbyport +getsockname getsockopt getsubopt gettimeofday @@ -775,12 +785,14 @@ globfree globfree64 gmtime gmtime_r +h_errno hcreate hcreate_r hdestroy hdestroy_r hsearch hsearch_r +hstrerror htonl htons hypot @@ -1246,6 +1258,8 @@ tcp_bind tcp_borrow_tcp_socket tcp_create_socket_create_tcp_socket tcp_create_socket_result_own_tcp_socket_error_code_free +tcp_getpeername +tcp_getsockname tcp_getsockopt tcp_ip_socket_address_free tcp_listen @@ -1338,6 +1352,8 @@ udp_borrow_outgoing_datagram_stream udp_borrow_udp_socket udp_create_socket_create_udp_socket udp_create_socket_result_own_udp_socket_error_code_free +udp_getpeername +udp_getsockname udp_getsockopt udp_incoming_datagram_free udp_incoming_datagram_stream_drop_borrow diff --git a/expected/wasm32-wasip2/include-all.c b/expected/wasm32-wasip2/include-all.c index 045fd1ef7..b2fd79b13 100644 --- a/expected/wasm32-wasip2/include-all.c +++ b/expected/wasm32-wasip2/include-all.c @@ -102,6 +102,7 @@ #include #include #include +#include #include #include #include diff --git a/expected/wasm32-wasip2/predefined-macros.txt b/expected/wasm32-wasip2/predefined-macros.txt index 20add244c..fb0cb15d1 100644 --- a/expected/wasm32-wasip2/predefined-macros.txt +++ b/expected/wasm32-wasip2/predefined-macros.txt @@ -37,6 +37,13 @@ #define AF_INET6 PF_INET6 #define AF_UNIX 3 #define AF_UNSPEC PF_UNSPEC +#define AI_ADDRCONFIG 0x20 +#define AI_ALL 0x10 +#define AI_CANONNAME 0x02 +#define AI_NUMERICHOST 0x04 +#define AI_NUMERICSERV 0x400 +#define AI_PASSIVE 0x01 +#define AI_V4MAPPED 0x08 #define ALT_DIGITS 0x2002F #define AM_STR 0x20026 #define ANYMARK 0x01 @@ -192,6 +199,24 @@ #define EADDRNOTAVAIL __WASI_ERRNO_ADDRNOTAVAIL #define EAFNOSUPPORT __WASI_ERRNO_AFNOSUPPORT #define EAGAIN __WASI_ERRNO_AGAIN +#define EAI_ADDRFAMILY -9 +#define EAI_AGAIN -3 +#define EAI_ALLDONE -103 +#define EAI_BADFLAGS -1 +#define EAI_CANCELED -101 +#define EAI_FAIL -4 +#define EAI_FAMILY -6 +#define EAI_IDN_ENCODE -105 +#define EAI_INPROGRESS -100 +#define EAI_INTR -104 +#define EAI_MEMORY -10 +#define EAI_NODATA -5 +#define EAI_NONAME -2 +#define EAI_NOTCANCELED -102 +#define EAI_OVERFLOW -12 +#define EAI_SERVICE -8 +#define EAI_SOCKTYPE -7 +#define EAI_SYSTEM -11 #define EALREADY __WASI_ERRNO_ALREADY #define EBADF __WASI_ERRNO_BADF #define EBADID 5 @@ -489,6 +514,7 @@ #define HIBITL MINLONG #define HIBITS MINSHORT #define HOST_NAME_MAX 255 +#define HOST_NOT_FOUND 1 #define HUGE 3.40282346638528859812e+38F #define HUGE_VAL ((double)INFINITY) #define HUGE_VALF INFINITY @@ -1239,6 +1265,14 @@ #define NEW_ENV_VAR 0 #define NGROUPS 32 #define NGROUPS_MAX 32 +#define NI_DGRAM 0x10 +#define NI_MAXHOST 255 +#define NI_MAXSERV 32 +#define NI_NAMEREQD 0x08 +#define NI_NOFQDN 0x04 +#define NI_NUMERICHOST 0x01 +#define NI_NUMERICSCOPE 0x100 +#define NI_NUMERICSERV 0x02 #define NL_ARGMAX 9 #define NL_CAT_LOCALE 1 #define NL_LANGMAX 32 @@ -1257,6 +1291,9 @@ #define NOTAUTH ns_r_notauth #define NOTIMP ns_r_notimpl #define NOTZONE ns_r_notzone +#define NO_ADDRESS NO_DATA +#define NO_DATA 4 +#define NO_RECOVERY 3 #define NR_ICMP_TYPES 18 #define NR_ICMP_UNREACH 15 #define NSLC 18 @@ -1968,6 +2005,7 @@ #define TOREAD 00004 #define TOWRITE 00002 #define TRANSIENT 4 +#define TRY_AGAIN 2 #define TSGID 02000 #define TSS_DTOR_ITERATIONS 4 #define TSUID 04000 @@ -2173,6 +2211,7 @@ #define _MATH_H #define _MONETARY_H #define _MQUEUE_H +#define _NETDB_H #define _NETINET_ICMP6_H #define _NETINET_IGMP_H #define _NETINET_IN_H @@ -3281,6 +3320,8 @@ #define glob64 glob #define glob64_t glob_t #define globfree64 globfree +#define h_addr h_addr_list[0] +#define h_errno h_errno #define howmany(n,d) (((n)+((d)-1))/(d)) #define htobe16(x) __bswap16(x) #define htobe32(x) __bswap32(x) diff --git a/libc-bottom-half/sources/getsockpeername.c b/libc-bottom-half/sources/getsockpeername.c new file mode 100644 index 000000000..463c233f2 --- /dev/null +++ b/libc-bottom-half/sources/getsockpeername.c @@ -0,0 +1,229 @@ +#include +#include + +#include +#include + +int tcp_getsockname(tcp_socket_t *socket, struct sockaddr *addr, + socklen_t *addrlen) +{ + output_sockaddr_t output_addr; + if (!__wasi_sockets_utils__output_addr_validate( + socket->family, addr, addrlen, &output_addr)) { + errno = EINVAL; + return -1; + } + + if (output_addr.tag == OUTPUT_SOCKADDR_NULL) { + errno = EINVAL; + return -1; + } + + switch (socket->state.tag) { + case TCP_SOCKET_STATE_UNBOUND: + errno = EINVAL; + return -1; + + case TCP_SOCKET_STATE_BOUND: + case TCP_SOCKET_STATE_CONNECTING: + case TCP_SOCKET_STATE_CONNECT_FAILED: + case TCP_SOCKET_STATE_LISTENING: + case TCP_SOCKET_STATE_CONNECTED: + // OK. Continue.. + break; + + default: /* unreachable */ + abort(); + } + + network_error_code_t error; + network_ip_socket_address_t result; + tcp_borrow_tcp_socket_t socket_borrow = + tcp_borrow_tcp_socket(socket->socket); + if (!tcp_method_tcp_socket_local_address(socket_borrow, &result, + &error)) { + errno = __wasi_sockets_utils__map_error(error); + return -1; + } + + __wasi_sockets_utils__output_addr_write(result, &output_addr); + + return 0; +} + +int tcp_getpeername(tcp_socket_t *socket, struct sockaddr *addr, + socklen_t *addrlen) +{ + output_sockaddr_t output_addr; + if (!__wasi_sockets_utils__output_addr_validate( + socket->family, addr, addrlen, &output_addr)) { + errno = EINVAL; + return -1; + } + + if (output_addr.tag == OUTPUT_SOCKADDR_NULL) { + errno = EINVAL; + return -1; + } + + switch (socket->state.tag) { + case TCP_SOCKET_STATE_UNBOUND: + case TCP_SOCKET_STATE_BOUND: + case TCP_SOCKET_STATE_CONNECTING: + case TCP_SOCKET_STATE_CONNECT_FAILED: + case TCP_SOCKET_STATE_LISTENING: + errno = ENOTCONN; + return -1; + + case TCP_SOCKET_STATE_CONNECTED: + // OK. Continue.. + break; + + default: /* unreachable */ + abort(); + } + + network_error_code_t error; + network_ip_socket_address_t result; + tcp_borrow_tcp_socket_t socket_borrow = + tcp_borrow_tcp_socket(socket->socket); + if (!tcp_method_tcp_socket_remote_address(socket_borrow, &result, + &error)) { + errno = __wasi_sockets_utils__map_error(error); + return -1; + } + + __wasi_sockets_utils__output_addr_write(result, &output_addr); + + return 0; +} + +int udp_getsockname(udp_socket_t *socket, struct sockaddr *addr, + socklen_t *addrlen) +{ + output_sockaddr_t output_addr; + if (!__wasi_sockets_utils__output_addr_validate( + socket->family, addr, addrlen, &output_addr)) { + errno = EINVAL; + return -1; + } + + if (output_addr.tag == OUTPUT_SOCKADDR_NULL) { + errno = EINVAL; + return -1; + } + + switch (socket->state.tag) { + case UDP_SOCKET_STATE_UNBOUND: + errno = EINVAL; + return -1; + + case UDP_SOCKET_STATE_BOUND_NOSTREAMS: + case UDP_SOCKET_STATE_BOUND_STREAMING: + case UDP_SOCKET_STATE_CONNECTED: + // OK. Continue.. + break; + + default: /* unreachable */ + abort(); + } + + network_error_code_t error; + network_ip_socket_address_t result; + udp_borrow_udp_socket_t socket_borrow = + udp_borrow_udp_socket(socket->socket); + if (!udp_method_udp_socket_local_address(socket_borrow, &result, + &error)) { + errno = __wasi_sockets_utils__map_error(error); + return -1; + } + + __wasi_sockets_utils__output_addr_write(result, &output_addr); + + return 0; +} + +int udp_getpeername(udp_socket_t *socket, struct sockaddr *addr, + socklen_t *addrlen) +{ + output_sockaddr_t output_addr; + if (!__wasi_sockets_utils__output_addr_validate( + socket->family, addr, addrlen, &output_addr)) { + errno = EINVAL; + return -1; + } + + if (output_addr.tag == OUTPUT_SOCKADDR_NULL) { + errno = EINVAL; + return -1; + } + + switch (socket->state.tag) { + case UDP_SOCKET_STATE_UNBOUND: + case UDP_SOCKET_STATE_BOUND_NOSTREAMS: + case UDP_SOCKET_STATE_BOUND_STREAMING: + errno = ENOTCONN; + return -1; + + case UDP_SOCKET_STATE_CONNECTED: + // OK. Continue.. + break; + + default: /* unreachable */ + abort(); + } + + network_error_code_t error; + network_ip_socket_address_t result; + udp_borrow_udp_socket_t socket_borrow = + udp_borrow_udp_socket(socket->socket); + if (!udp_method_udp_socket_remote_address(socket_borrow, &result, + &error)) { + errno = __wasi_sockets_utils__map_error(error); + return -1; + } + + __wasi_sockets_utils__output_addr_write(result, &output_addr); + + return 0; +} + +int getsockname(int socket, struct sockaddr *__restrict addr, + socklen_t *__restrict addrlen) +{ + descriptor_table_entry_t *entry; + if (!descriptor_table_get_ref(socket, &entry)) { + errno = EBADF; + return -1; + } + + switch (entry->tag) { + case DESCRIPTOR_TABLE_ENTRY_TCP_SOCKET: + return tcp_getsockname(&entry->tcp_socket, addr, addrlen); + case DESCRIPTOR_TABLE_ENTRY_UDP_SOCKET: + return udp_getsockname(&entry->udp_socket, addr, addrlen); + default: + errno = EOPNOTSUPP; + return -1; + } +} + +int getpeername(int socket, struct sockaddr *__restrict addr, + socklen_t *__restrict addrlen) +{ + descriptor_table_entry_t *entry; + if (!descriptor_table_get_ref(socket, &entry)) { + errno = EBADF; + return -1; + } + + switch (entry->tag) { + case DESCRIPTOR_TABLE_ENTRY_TCP_SOCKET: + return tcp_getpeername(&entry->tcp_socket, addr, addrlen); + case DESCRIPTOR_TABLE_ENTRY_UDP_SOCKET: + return udp_getpeername(&entry->udp_socket, addr, addrlen); + default: + errno = EOPNOTSUPP; + return -1; + } +} diff --git a/libc-bottom-half/sources/netdb.c b/libc-bottom-half/sources/netdb.c new file mode 100644 index 000000000..c30a3967c --- /dev/null +++ b/libc-bottom-half/sources/netdb.c @@ -0,0 +1,238 @@ +#include +#include +#include + +#include + +_Thread_local int h_errno = 0; + +static int map_error(ip_name_lookup_error_code_t error) +{ + switch (error) { + case NETWORK_ERROR_CODE_OUT_OF_MEMORY: + return EAI_MEMORY; + case NETWORK_ERROR_CODE_NAME_UNRESOLVABLE: + return EAI_NONAME; + case NETWORK_ERROR_CODE_TEMPORARY_RESOLVER_FAILURE: + return EAI_AGAIN; + case NETWORK_ERROR_CODE_PERMANENT_RESOLVER_FAILURE: + return EAI_FAIL; + + default: + errno = __wasi_sockets_utils__map_error(error); + return EAI_SYSTEM; + } +} + +static int add_addr(ip_name_lookup_option_ip_address_t address, + const struct addrinfo *restrict hint, + struct addrinfo **restrict current, + struct addrinfo **restrict res) +{ + int family; + struct sockaddr *addr; + socklen_t addrlen; + switch (address.val.tag) { + case NETWORK_IP_ADDRESS_IPV4: { + if (hint && hint->ai_family != AF_UNSPEC && + hint->ai_family != AF_INET) { + return 0; + } + + network_ipv4_address_t ip = address.val.val.ipv4; + + family = PF_INET; + addrlen = sizeof(struct sockaddr_in); + addr = malloc(addrlen); + if (addr == NULL) { + freeaddrinfo(*res); + return EAI_MEMORY; + } + + struct sockaddr_in sockaddr = { + .sin_family = AF_INET, + .sin_port = 0, + .sin_addr = { .s_addr = ip.f0 | (ip.f1 << 8) | + (ip.f2 << 16) | (ip.f3 << 24) }, + }; + memcpy(addr, &sockaddr, addrlen); + break; + } + case NETWORK_IP_ADDRESS_IPV6: { + if (hint && hint->ai_family != AF_UNSPEC && + hint->ai_family != AF_INET6) { + return 0; + } + + network_ipv6_address_t ip = address.val.val.ipv6; + + family = PF_INET6; + addrlen = sizeof(struct sockaddr_in6); + addr = malloc(addrlen); + if (addr == NULL) { + freeaddrinfo(*res); + return EAI_MEMORY; + } + + struct sockaddr_in6 sockaddr = { + .sin6_family = AF_INET6, + .sin6_port = 0, + .sin6_addr = { + .s6_addr = { + ip.f0 >> 8, + ip.f0 & 0xFF, + ip.f1 >> 8, + ip.f1 & 0xFF, + ip.f2 >> 8, + ip.f2 & 0xFF, + ip.f3 >> 8, + ip.f3 & 0xFF, + ip.f4 >> 8, + ip.f4 & 0xFF, + ip.f5 >> 8, + ip.f5 & 0xFF, + ip.f6 >> 8, + ip.f6 & 0xFF, + ip.f7 >> 8, + ip.f7 & 0xFF, + } }, + .sin6_flowinfo = 0, + .sin6_scope_id = 0, + }; + memcpy(addr, &sockaddr, addrlen); + break; + } + default: /* unreachable */ + abort(); + } + + struct addrinfo *result = malloc(sizeof(struct addrinfo)); + if (result == NULL) { + freeaddrinfo(*res); + return EAI_MEMORY; + } + + *result = (struct addrinfo){ + .ai_family = family, + .ai_flags = 0, + .ai_socktype = SOCK_STREAM, + .ai_protocol = 0, + .ai_addrlen = addrlen, + .ai_addr = addr, + .ai_canonname = NULL, + .ai_next = NULL, + }; + + if (*current) { + (*current)->ai_next = result; + *current = result; + } else { + *current = result; + *res = result; + } + + return 0; +} + +int getaddrinfo(const char *restrict host, const char *restrict serv, + const struct addrinfo *restrict hint, + struct addrinfo **restrict res) +{ + if (host == NULL) { + host = "localhost"; + } + + *res = NULL; + struct addrinfo *current = NULL; + wasip2_string_t name = { .ptr = (uint8_t *)host, .len = strlen(host) }; + ip_name_lookup_own_resolve_address_stream_t stream; + ip_name_lookup_error_code_t error; + if (ip_name_lookup_resolve_addresses( + __wasi_sockets_utils__borrow_network(), &name, &stream, + &error)) { + ip_name_lookup_borrow_resolve_address_stream_t stream_borrow = + ip_name_lookup_borrow_resolve_address_stream(stream); + while (true) { + ip_name_lookup_option_ip_address_t address; + if (ip_name_lookup_method_resolve_address_stream_resolve_next_address( + stream_borrow, &address, &error)) { + if (address.is_some) { + int error = add_addr(address, hint, + ¤t, res); + if (error) { + return error; + } + } else { + return 0; + } + } else if (error == NETWORK_ERROR_CODE_WOULD_BLOCK) { + ip_name_lookup_own_pollable_t pollable = + ip_name_lookup_method_resolve_address_stream_subscribe( + stream_borrow); + poll_borrow_pollable_t pollable_borrow = + poll_borrow_pollable(pollable); + poll_method_pollable_block(pollable_borrow); + poll_pollable_drop_own(pollable); + } else { + freeaddrinfo(*res); + return map_error(error); + } + } + } else { + return map_error(error); + } +} + +void freeaddrinfo(struct addrinfo *p) +{ + while (p) { + struct addrinfo *next = p->ai_next; + free(p->ai_addr); + free(p); + p = next; + } +} + +int getnameinfo(const struct sockaddr *restrict sa, socklen_t salen, + char *restrict host, socklen_t hostlen, char *restrict serv, + socklen_t servlen, int flags) +{ + // TODO wasi-sockets + abort(); +} + +struct hostent *gethostbyname(const char *name) +{ + // TODO wasi-sockets + return NULL; +} + +struct hostent *gethostbyaddr(const void *addr, socklen_t len, int type) +{ + // TODO wasi-sockets + return NULL; +} + +const char *hstrerror(int err) +{ + // TODO wasi-sockets + return "hstrerror: TODO"; +} + +struct servent *getservbyname(const char *name, const char *proto) +{ + // TODO wasi-sockets + return NULL; +} + +struct servent *getservbyport(int port, const char *proto) +{ + // TODO wasi-sockets + return NULL; +} + +struct protoent *getprotobyname(const char *name) +{ + // TODO wasi-sockets + return NULL; +} diff --git a/libc-top-half/musl/include/netdb.h b/libc-top-half/musl/include/netdb.h index d096c7818..f9797bc84 100644 --- a/libc-top-half/musl/include/netdb.h +++ b/libc-top-half/musl/include/netdb.h @@ -118,8 +118,13 @@ struct hostent *gethostbyaddr (const void *, socklen_t, int); #ifdef __GNUC__ __attribute__((const)) #endif +#ifdef __wasilibc_unmodified_upstream int *__h_errno_location(void); #define h_errno (*__h_errno_location()) +#elif (defined __wasilibc_use_wasip2) +extern _Thread_local int h_errno; +#define h_errno h_errno +#endif #define HOST_NOT_FOUND 1 #define TRY_AGAIN 2 #define NO_RECOVERY 3