#include #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, in_port_t port, 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 = port, .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 = port, .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); // The 'serv' parameter can be either a port number or a service name. // // TODO wasi-sockets: If the conversion of 'serv' to a valid port // number fails, use getservbyname() to resolve the service name to // its corresponding port number. This can be done after the // getservbyname function is implemented.) int port = 0; if (serv != NULL) { port = __wasi_sockets_utils__parse_port(serv); if (port < 0) { return EAI_NONAME; } } 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, htons(port), 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; }