Skip to content

Commit 1b19fc6

Browse files
authored
getaddrinfo: improve the service/port resolution (#524)
Hello, While experimenting with the `wasm32-wasip2` target and CPython, I discovered an issue with the `getaddrinfo()` implementation: it fails to resolve the provided service into a port number, causing `sin_port` to always be set to 0. This issue leads to failures in network-related functions that rely on `getaddrinfo()`, such as Python's `urllib3` library, which passes the result directly to `connect()`. This results in connection attempts using a port value of 0, which naturally fails. ### Minimal example to reproduce the problem ```c #include <arpa/inet.h> #include <netdb.h> #include <stdio.h> int main(void) { struct addrinfo *res = NULL; getaddrinfo("google.com", "443", NULL, &res); for (struct addrinfo *i = res; i != NULL; i = i->ai_next) { char str[INET6_ADDRSTRLEN]; if (i->ai_addr->sa_family == AF_INET) { struct sockaddr_in *p = (struct sockaddr_in *)i->ai_addr; int port = ntohs(p->sin_port); printf("%s: %i\n", inet_ntop(AF_INET, &p->sin_addr, str, sizeof(str)), port); } else if (i->ai_addr->sa_family == AF_INET6) { struct sockaddr_in6 *p = (struct sockaddr_in6 *)i->ai_addr; int port = ntohs(p->sin6_port); printf("%s: %i\n", inet_ntop(AF_INET6, &p->sin6_addr, str, sizeof(str)), port); } } return 0; } ``` ``` $ /opt/wasi-sdk/bin/clang -target wasm32-wasip2 -o foo foo.c $ wasmtime run -S allow-ip-name-lookup=y foo 216.58.211.238: 0 2a00:1450:4026:808::200e: 0 ``` Expected output: ``` 216.58.211.238: 443 2a00:1450:4026:808::200e: 443 ``` ### Root Cause The root cause is that `getaddrinfo()` does not correctly translate the provided service into a port number. As described in the `getaddrinfo()` man [page](https://man7.org/linux/man-pages/man3/getaddrinfo.3.html), the function should: > service sets the port in each returned address structure. If this argument is a service name (see [services(5)](https://man7.org/linux/man-pages/man5/services.5.html)), it is translated to the corresponding port number. This argument can also be specified as a decimal number, which is simply converted to binary. If service is NULL, then the port number of the returned socket addresses will be left uninitialized. ### Proposed Fix This pull request addresses the issue by implementing the following behavior for `getaddrinfo()`: * If the service is `NULL`, the port number in the returned socket addresses remains uninitialized. * The value is converted to an integer and validated if the service is numeric. The PR does not currently add support for translating named services into port numbers because `getservbyname()` has not been implemented. In cases where a named service is provided, the `EAI_NONAME` error code is returned.
1 parent 3f812ab commit 1b19fc6

File tree

4 files changed

+42
-3
lines changed

4 files changed

+42
-3
lines changed

expected/wasm32-wasip2/defined-symbols.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,7 @@ __wasi_sockets_utils__map_error
309309
__wasi_sockets_utils__output_addr_validate
310310
__wasi_sockets_utils__output_addr_write
311311
__wasi_sockets_utils__parse_address
312+
__wasi_sockets_utils__parse_port
312313
__wasi_sockets_utils__posix_family
313314
__wasi_sockets_utils__stream
314315
__wasi_sockets_utils__tcp_bind

libc-bottom-half/headers/private/wasi/sockets_utils.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,6 @@ bool __wasi_sockets_utils__stream(udp_socket_t *socket,
4949
udp_socket_streams_t *result,
5050
network_error_code_t *error);
5151
void __wasi_sockets_utils__drop_streams(udp_socket_streams_t streams);
52+
int __wasi_sockets_utils__parse_port(const char *port);
5253

5354
#endif

libc-bottom-half/sources/netdb.c

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include <netdb.h>
22
#include <errno.h>
33
#include <stdlib.h>
4+
#include <arpa/inet.h>
45

56
#include <wasi/sockets_utils.h>
67

@@ -25,6 +26,7 @@ static int map_error(ip_name_lookup_error_code_t error)
2526
}
2627

2728
static int add_addr(ip_name_lookup_option_ip_address_t address,
29+
in_port_t port,
2830
const struct addrinfo *restrict hint,
2931
struct addrinfo **restrict current,
3032
struct addrinfo **restrict res)
@@ -51,7 +53,7 @@ static int add_addr(ip_name_lookup_option_ip_address_t address,
5153

5254
struct sockaddr_in sockaddr = {
5355
.sin_family = AF_INET,
54-
.sin_port = 0,
56+
.sin_port = port,
5557
.sin_addr = { .s_addr = ip.f0 | (ip.f1 << 8) |
5658
(ip.f2 << 16) | (ip.f3 << 24) },
5759
};
@@ -76,7 +78,7 @@ static int add_addr(ip_name_lookup_option_ip_address_t address,
7678

7779
struct sockaddr_in6 sockaddr = {
7880
.sin6_family = AF_INET6,
79-
.sin6_port = 0,
81+
.sin6_port = port,
8082
.sin6_addr = {
8183
.s6_addr = {
8284
ip.f0 >> 8,
@@ -152,12 +154,25 @@ int getaddrinfo(const char *restrict host, const char *restrict serv,
152154
&error)) {
153155
ip_name_lookup_borrow_resolve_address_stream_t stream_borrow =
154156
ip_name_lookup_borrow_resolve_address_stream(stream);
157+
// The 'serv' parameter can be either a port number or a service name.
158+
//
159+
// TODO wasi-sockets: If the conversion of 'serv' to a valid port
160+
// number fails, use getservbyname() to resolve the service name to
161+
// its corresponding port number. This can be done after the
162+
// getservbyname function is implemented.)
163+
int port = 0;
164+
if (serv != NULL) {
165+
port = __wasi_sockets_utils__parse_port(serv);
166+
if (port < 0) {
167+
return EAI_NONAME;
168+
}
169+
}
155170
while (true) {
156171
ip_name_lookup_option_ip_address_t address;
157172
if (ip_name_lookup_method_resolve_address_stream_resolve_next_address(
158173
stream_borrow, &address, &error)) {
159174
if (address.is_some) {
160-
int error = add_addr(address, hint,
175+
int error = add_addr(address, htons(port), hint,
161176
&current, res);
162177
if (error) {
163178
return error;

libc-bottom-half/sources/sockets_utils.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
#include <errno.h>
2+
#include <stdlib.h>
3+
#include <ctype.h>
24

35
#include <wasi/sockets_utils.h>
46

@@ -460,3 +462,23 @@ bool __wasi_sockets_utils__stream(
460462

461463
return true;
462464
}
465+
466+
int __wasi_sockets_utils__parse_port(const char *restrict port_str)
467+
{
468+
char *end = NULL;
469+
errno = 0;
470+
long port = strtol(port_str, &end, 10);
471+
472+
// Check for various possible errors:
473+
// - the input is not a valid number
474+
// - the entire input string is not consumed
475+
// - the number is not within the valid port range
476+
// - the input does not start with a digit (strtol allows leading
477+
// whitespace and optional sign)
478+
if (errno != 0 || end == NULL || *end != '\0' || port < 0
479+
|| port > 65535 || !isdigit(*port_str)) {
480+
return -1;
481+
}
482+
483+
return (int)port;
484+
}

0 commit comments

Comments
 (0)