implement getsockname, getpeername, and getaddrinfo (#488)

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.

Finally, this includes a few fixes for issues I missed in earlier PRs that
surfaced when running the CPython `asyncio` test suite.

Signed-off-by: Joel Dice <joel.dice@fermyon.com>
Co-authored-by: Dave Bakker <github@davebakker.io>
This commit is contained in:
Joel Dice 2024-04-02 17:54:41 -06:00 committed by GitHub
parent 1ab654e2f5
commit d038294899
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 555 additions and 5 deletions

View File

@ -94,7 +94,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.
# for exception-handling.
@ -249,6 +251,13 @@ LIBC_TOP_HALF_MUSL_SOURCES = \
$(wildcard $(LIBC_TOP_HALF_MUSL_SRC_DIR)/complex/*.c)) \
$(wildcard $(LIBC_TOP_HALF_MUSL_SRC_DIR)/crypt/*.c)
ifeq ($(WASI_SNAPSHOT), p2)
LIBC_TOP_HALF_MUSL_SOURCES += \
$(addprefix $(LIBC_TOP_HALF_MUSL_SRC_DIR)/, \
network/gai_strerror.c \
)
endif
ifeq ($(THREAD_MODEL), posix)
LIBC_TOP_HALF_MUSL_SOURCES += \
$(addprefix $(LIBC_TOP_HALF_MUSL_SRC_DIR)/, \
@ -387,6 +396,10 @@ ASMFLAGS += -matomics
CFLAGS += -I$(LIBC_BOTTOM_HALF_CLOUDLIBC_SRC)
endif
ifeq ($(WASI_SNAPSHOT), p2)
EXTRA_CFLAGS += -D__wasilibc_use_wasip2
endif
# Expose the public headers to the implementation. We use `-isystem` for
# purpose for two reasons:
#
@ -500,7 +513,6 @@ MUSL_OMIT_HEADERS += \
"sys/auxv.h" \
"pwd.h" "shadow.h" "grp.h" \
"mntent.h" \
"netdb.h" \
"resolv.h" \
"pty.h" \
"ulimit.h" \
@ -523,6 +535,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"

View File

@ -535,6 +535,9 @@ dprintf
drand48
drem
dremf
drop_tcp_socket
drop_udp_socket
drop_udp_socket_streams
duplocale
ecvt
encrypt
@ -709,6 +712,7 @@ fputws_unlocked
fread
fread_unlocked
free
freeaddrinfo
freelocale
freopen
freopen64
@ -737,11 +741,13 @@ fwprintf
fwrite
fwrite_unlocked
fwscanf
gai_strerror
gcvt
get_avphys_pages
get_nprocs
get_nprocs_conf
get_phys_pages
getaddrinfo
getc
getc_unlocked
getchar
@ -753,14 +759,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 +789,14 @@ globfree
globfree64
gmtime
gmtime_r
h_errno
hcreate
hcreate_r
hdestroy
hdestroy_r
hsearch
hsearch_r
hstrerror
htonl
htons
hypot
@ -1246,6 +1262,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 +1356,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

View File

@ -102,6 +102,7 @@
#include <memory.h>
#include <monetary.h>
#include <mqueue.h>
#include <netdb.h>
#include <netinet/icmp6.h>
#include <netinet/igmp.h>
#include <netinet/in.h>

View File

@ -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
@ -3194,7 +3233,7 @@
#define __wasilibc___typedef_suseconds_t_h
#define __wasilibc___typedef_time_t_h
#define __wasilibc___typedef_uid_t_h
#define __wasilibc_use_wasip2
#define __wasilibc_use_wasip2 1
#define __wasm 1
#define __wasm32 1
#define __wasm32__ 1
@ -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)

View File

@ -0,0 +1,229 @@
#include <errno.h>
#include <netinet/in.h>
#include <wasi/descriptor_table.h>
#include <wasi/sockets_utils.h>
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;
}
}

View File

@ -0,0 +1,238 @@
#include <netdb.h>
#include <errno.h>
#include <stdlib.h>
#include <wasi/sockets_utils.h>
_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,
&current, 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;
}

View File

@ -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

View File

@ -417,7 +417,7 @@ int listen (int, int);
int accept (int, struct sockaddr *__restrict, socklen_t *__restrict);
int accept4(int, struct sockaddr *__restrict, socklen_t *__restrict, int);
#ifdef __wasilibc_unmodified_upstream /* WASI has no getsockname/getpeername */
#if (defined __wasilibc_unmodified_upstream) || (defined __wasilibc_use_wasip2)
int getsockname (int, struct sockaddr *__restrict, socklen_t *__restrict);
int getpeername (int, struct sockaddr *__restrict, socklen_t *__restrict);
#endif
@ -434,7 +434,7 @@ ssize_t recvmsg (int, struct msghdr *, int);
#endif
int getsockopt (int, int, int, void *__restrict, socklen_t *__restrict);
#ifdef __wasilibc_unmodified_upstream /* WASI has no setsockopt */
#if (defined __wasilibc_unmodified_upstream) || (defined __wasilibc_use_wasip2)
int setsockopt (int, int, int, const void *, socklen_t);
#endif