From 5e5aea464ea1bfac69fcd5c3d33a2e3084600cbe Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Fri, 23 Aug 2024 10:58:27 -0300 Subject: [PATCH 01/19] Convert gethostbyname(l) to use getaddrinfo php_network_getaddresses is our wrapper function around getaddrinfo, the modern replacement for gethostbyname/addr. This converts these userland functions to use the newer API, which does have some benefits... that these functions can't yet take advantage of. Other references to gethostbyname/addr will be removed in future commits, to eventually remove the PHP wrappers in the next ABI break. Note that php_network_getaddresses does its own E_WARNINGs. Note that these functions are documented as only supporting IPv4 addresses; we need to do another BC break to return IPv6 addresses. --- ext/standard/dns.c | 108 ++++++++++-------- .../tests/network/gethostbyname_error003.phpt | 3 +- .../tests/network/gethostbyname_error006.phpt | 3 +- 3 files changed, 65 insertions(+), 49 deletions(-) diff --git a/ext/standard/dns.c b/ext/standard/dns.c index a574d8dd9aeb8..f3878ef38c5c9 100644 --- a/ext/standard/dns.c +++ b/ext/standard/dns.c @@ -118,7 +118,6 @@ extern void __res_ndestroy(res_state statp); /* }}} */ static zend_string *php_gethostbyaddr(char *ip); -static zend_string *php_gethostbyname(char *name); #ifdef HAVE_GETHOSTNAME /* {{{ Get the host name of the current machine */ @@ -220,7 +219,8 @@ PHP_FUNCTION(gethostbyname) { char *hostname; size_t hostname_len; - zend_string *ipaddr; + char addr4[INET_ADDRSTRLEN]; + zend_string *ipaddr_zs = NULL; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_PATH(hostname, hostname_len) @@ -232,11 +232,47 @@ PHP_FUNCTION(gethostbyname) RETURN_STRINGL(hostname, hostname_len); } - if (!(ipaddr = php_gethostbyname(hostname))) { + struct sockaddr **addresses = NULL; + zend_string *gai_error = NULL; + int address_count = php_network_getaddresses(hostname, 0, &addresses, &gai_error); + if (gai_error) { + zend_string_release_ex(gai_error, 0); + } + if (address_count == 0) { + /* don't need to docref here, getaddresses E_WARNINGs for us */ + RETURN_STRINGL(hostname, hostname_len); + } + + /* + * Future behaviour change: This function is documented as only returning IPv4 + * addresses. We should change this to return IPv6 addresses as well. + */ + for (struct sockaddr **address_p = addresses; *address_p != NULL; address_p++) { + struct sockaddr *address = *address_p; + + if (address->sa_family != AF_INET) { + continue; + } + + struct sockaddr_in *address4 = (struct sockaddr_in*)address; + const char *ipaddr; + if (!(ipaddr = inet_ntop(AF_INET, &address4->sin_addr, addr4, INET_ADDRSTRLEN))) { + /* unlikely regarding (too) long hostname and protocols but checking still */ + php_error_docref(NULL, E_WARNING, "Host name to ip failed %s", hostname); + continue; + } else { + ipaddr_zs = zend_string_init(ipaddr, strlen(ipaddr), 0); + break; /* we take only first IP for this function */ + } + } + + php_network_freeaddresses(addresses); + + if (ipaddr_zs == NULL) { php_error_docref(NULL, E_WARNING, "Host name to ip failed %s", hostname); RETURN_STRINGL(hostname, hostname_len); } else { - RETURN_NEW_STR(ipaddr); + RETURN_NEW_STR(ipaddr_zs); } } /* }}} */ @@ -246,9 +282,6 @@ PHP_FUNCTION(gethostbynamel) { char *hostname; size_t hostname_len; - struct hostent *hp; - struct in_addr in; - int i; char addr4[INET_ADDRSTRLEN]; ZEND_PARSE_PARAMETERS_START(1, 1) @@ -261,24 +294,33 @@ PHP_FUNCTION(gethostbynamel) RETURN_FALSE; } - hp = php_network_gethostbyname(hostname); - if (!hp) { + struct sockaddr **addresses = NULL; + zend_string *gai_error = NULL; + int address_count = php_network_getaddresses(hostname, 0, &addresses, &gai_error); + if (gai_error) { + zend_string_release_ex(gai_error, 0); + } + if (address_count == 0) { + /* don't need to docref here, getaddresses E_WARNINGs for us */ RETURN_FALSE; } array_init(return_value); - for (i = 0;; i++) { - /* On macos h_addr_list entries may be misaligned. */ - const char *ipaddr; - struct in_addr *h_addr_entry; /* Don't call this h_addr, it's a macro! */ - memcpy(&h_addr_entry, &hp->h_addr_list[i], sizeof(struct in_addr *)); - if (!h_addr_entry) { - return; + /* + * Future behaviour change: This function is documented as only returning IPv4 + * addresses. We should change this to return IPv6 addresses as well. + */ + for (struct sockaddr **address_p = addresses; *address_p != NULL; address_p++) { + struct sockaddr *address = *address_p; + + if (address->sa_family != AF_INET) { + continue; } - in = *h_addr_entry; - if (!(ipaddr = inet_ntop(AF_INET, &in, addr4, INET_ADDRSTRLEN))) { + struct sockaddr_in *address4 = (struct sockaddr_in*)address; + const char *ipaddr; + if (!(ipaddr = inet_ntop(AF_INET, &address4->sin_addr, addr4, INET_ADDRSTRLEN))) { /* unlikely regarding (too) long hostname and protocols but checking still */ php_error_docref(NULL, E_WARNING, "Host name to ip failed %s", hostname); continue; @@ -286,36 +328,8 @@ PHP_FUNCTION(gethostbynamel) add_next_index_string(return_value, ipaddr); } } -} -/* }}} */ - -/* {{{ php_gethostbyname */ -static zend_string *php_gethostbyname(char *name) -{ - struct hostent *hp; - struct in_addr *h_addr_0; /* Don't call this h_addr, it's a macro! */ - struct in_addr in; - char addr4[INET_ADDRSTRLEN]; - const char *address; - - hp = php_network_gethostbyname(name); - if (!hp) { - return zend_string_init(name, strlen(name), 0); - } - - /* On macos h_addr_list entries may be misaligned. */ - memcpy(&h_addr_0, &hp->h_addr_list[0], sizeof(struct in_addr *)); - if (!h_addr_0) { - return zend_string_init(name, strlen(name), 0); - } - - memcpy(&in.s_addr, h_addr_0, sizeof(in.s_addr)); - - if (!(address = inet_ntop(AF_INET, &in, addr4, INET_ADDRSTRLEN))) { - return NULL; - } - return zend_string_init(address, strlen(address), 0); + php_network_freeaddresses(addresses); } /* }}} */ diff --git a/ext/standard/tests/network/gethostbyname_error003.phpt b/ext/standard/tests/network/gethostbyname_error003.phpt index ebc8a971cb016..47ae902bb4ad2 100644 --- a/ext/standard/tests/network/gethostbyname_error003.phpt +++ b/ext/standard/tests/network/gethostbyname_error003.phpt @@ -6,5 +6,6 @@ gethostbyname() function - basic type return error test ---EXPECT-- +--EXPECTF-- +Warning: gethostbyname(): php_network_getaddresses: getaddrinfo for asdfasdf failed: nodename nor servname provided, or not known in %s on line %d bool(true) diff --git a/ext/standard/tests/network/gethostbyname_error006.phpt b/ext/standard/tests/network/gethostbyname_error006.phpt index 156ba0e623c30..764f3fce5c5d1 100644 --- a/ext/standard/tests/network/gethostbyname_error006.phpt +++ b/ext/standard/tests/network/gethostbyname_error006.phpt @@ -6,5 +6,6 @@ gethostbyname() function - basic invalid parameter test ---EXPECT-- +--EXPECTF-- +Warning: gethostbyname(): php_network_getaddresses: getaddrinfo for .toto.toto.toto failed: nodename nor servname provided, or not known in %s on line %d string(15) ".toto.toto.toto" From c624de37f673e1bd563d2b2593dc7a7a0798f889 Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Fri, 23 Aug 2024 11:25:58 -0300 Subject: [PATCH 02/19] Add php_network_getaddresses_ex Allows controlling the address family and flags to use. --- main/network.c | 16 +++++++++++++--- main/php_network.h | 1 + 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/main/network.c b/main/network.c index 54dbf2b8e580f..1069943bd4cda 100644 --- a/main/network.c +++ b/main/network.c @@ -142,10 +142,10 @@ PHPAPI void php_network_freeaddresses(struct sockaddr **sal) } /* }}} */ -/* {{{ php_network_getaddresses +/* {{{ php_network_getaddresses_ex * Returns number of addresses, 0 for none/error */ -PHPAPI int php_network_getaddresses(const char *host, int socktype, struct sockaddr ***sal, zend_string **error_string) +PHPAPI int php_network_getaddresses_ex(const char *host, int socktype, int family, int ai_flags, struct sockaddr ***sal, zend_string **error_string) { struct sockaddr **sap; int n; @@ -167,6 +167,7 @@ PHPAPI int php_network_getaddresses(const char *host, int socktype, struct socka hints.ai_family = AF_INET; /* default to regular inet (see below) */ hints.ai_socktype = socktype; + hints.ai_flags = ai_flags; # ifdef HAVE_IPV6 /* probe for a working IPv6 stack; even if detected as having v6 at compile @@ -186,7 +187,7 @@ PHPAPI int php_network_getaddresses(const char *host, int socktype, struct socka closesocket(s); } } - hints.ai_family = ipv6_borked ? AF_INET : AF_UNSPEC; + hints.ai_family = ipv6_borked ? AF_INET : family; # endif if ((n = getaddrinfo(host, NULL, &hints, &res))) { @@ -278,6 +279,15 @@ PHPAPI int php_network_getaddresses(const char *host, int socktype, struct socka } /* }}} */ +/* {{{ php_network_getaddresses + * Returns number of addresses, 0 for none/error + */ +PHPAPI int php_network_getaddresses(const char *host, int socktype, struct sockaddr ***sal, zend_string **error_string) +{ + return php_network_getaddresses_ex(host, socktype, AF_UNSPEC, 0, sal, error_string); +} +/* }}} */ + #ifndef O_NONBLOCK #define O_NONBLOCK O_NDELAY #endif diff --git a/main/php_network.h b/main/php_network.h index 6700ab42dd3fa..84d56f342810b 100644 --- a/main/php_network.h +++ b/main/php_network.h @@ -286,6 +286,7 @@ typedef struct { BEGIN_EXTERN_C() PHPAPI int php_network_getaddresses(const char *host, int socktype, struct sockaddr ***sal, zend_string **error_string); +PHPAPI int php_network_getaddresses_ex(const char *host, int socktype, int family, int ai_flags, struct sockaddr ***sal, zend_string **error_string); PHPAPI void php_network_freeaddresses(struct sockaddr **sal); PHPAPI php_socket_t php_network_connect_socket_to_host_ex(const char *host, unsigned short port, From 7597097b2b792201e1f04c642af78052962c3bb4 Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Fri, 23 Aug 2024 11:42:13 -0300 Subject: [PATCH 03/19] Convert sockaddr_conv.c to use getaddrinfo for v4 Not ideal right now; it should really be using the PHP wrapper function, and have bool conversion. But it does get rid of a use of gethostbyname. --- ext/sockets/sockaddr_conv.c | 74 +++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 40 deletions(-) diff --git a/ext/sockets/sockaddr_conv.c b/ext/sockets/sockaddr_conv.c index 333ef2533a11b..231b35a7f42b5 100644 --- a/ext/sockets/sockaddr_conv.c +++ b/ext/sockets/sockaddr_conv.c @@ -11,24 +11,24 @@ extern zend_result php_string_to_if_index(const char *val, unsigned *out); -#ifdef HAVE_IPV6 -/* Sets addr by hostname, or by ip in string form (AF_INET6) */ -int php_set_inet6_addr(struct sockaddr_in6 *sin6, zend_string *string, php_socket *php_sock) /* {{{ */ +int php_set_common_addr(struct sockaddr *sin, int family, char *string, php_socket *php_sock) /* {{{ */ { - struct in6_addr tmp; #ifdef HAVE_GETADDRINFO + struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)sin; + struct sockaddr_in *sin4 = (struct sockaddr_in*)sin; + struct in6_addr tmp6; + struct in_addr tmp4; + struct addrinfo hints; struct addrinfo *addrinfo = NULL; -#endif - char *scope = strchr(ZSTR_VAL(string), '%'); - if (inet_pton(AF_INET6, ZSTR_VAL(string), &tmp)) { - memcpy(&(sin6->sin6_addr.s6_addr), &(tmp.s6_addr), sizeof(struct in6_addr)); + if (family == AF_INET6 && inet_pton(AF_INET6, string, &tmp6)) { + memcpy(&(sin6->sin6_addr.s6_addr), &(tmp6.s6_addr), sizeof(struct in6_addr)); + } else if (family == AF_INET && inet_pton(AF_INET, string, &tmp4)) { + sin4->sin_addr.s_addr = tmp4.s_addr; } else { -#ifdef HAVE_GETADDRINFO - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = AF_INET6; + hints.ai_family = family; #ifdef AI_V4MAPPED hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG; #else @@ -43,21 +43,37 @@ int php_set_inet6_addr(struct sockaddr_in6 *sin6, zend_string *string, php_socke #endif return 0; } - if (addrinfo->ai_family != PF_INET6 || addrinfo->ai_addrlen != sizeof(struct sockaddr_in6)) { - php_error_docref(NULL, E_WARNING, "Host lookup failed: Non AF_INET6 domain returned on AF_INET6 socket"); + if (addrinfo->ai_family != family) { + php_error_docref(NULL, E_WARNING, "Host lookup failed: Wrong address family returned for socket"); freeaddrinfo(addrinfo); return 0; } - memcpy(&(sin6->sin6_addr.s6_addr), ((struct sockaddr_in6*)(addrinfo->ai_addr))->sin6_addr.s6_addr, sizeof(struct in6_addr)); + if (addrinfo->ai_family == AF_INET6) { + memcpy(&(sin6->sin6_addr.s6_addr), ((struct sockaddr_in6*)(addrinfo->ai_addr))->sin6_addr.s6_addr, sizeof(struct in6_addr)); + } else if (addrinfo->ai_family == AF_INET) { + memcpy(&(sin4->sin_addr.s_addr), &((struct sockaddr_in*)(addrinfo->ai_addr))->sin_addr.s_addr, sizeof(struct in_addr)); + } freeaddrinfo(addrinfo); + } + return 1; #else - /* No IPv6 specific hostname resolution is available on this system? */ - php_error_docref(NULL, E_WARNING, "Host lookup failed: getaddrinfo() not available on this system"); - return 0; + php_error_docref(NULL, E_WARNING, "Host lookup failed: getaddrinfo() not available on this system"); + return 0; #endif +} +/* }}} */ + +#ifdef HAVE_IPV6 +/* Sets addr by hostname, or by ip in string form (AF_INET6) */ +int php_set_inet6_addr(struct sockaddr_in6 *sin6, char *string, php_socket *php_sock) /* {{{ */ +{ + char *scope = strchr(string, '%'); + int ret = php_set_common_addr((struct sockaddr*)sin6, AF_INET6, string, php_sock); + if (!ret) { + return 0; } if (scope) { @@ -86,29 +102,7 @@ int php_set_inet6_addr(struct sockaddr_in6 *sin6, zend_string *string, php_socke /* Sets addr by hostname, or by ip in string form (AF_INET) */ int php_set_inet_addr(struct sockaddr_in *sin, zend_string *string, php_socket *php_sock) /* {{{ */ { - struct in_addr tmp; - struct hostent *host_entry; - - if (inet_pton(AF_INET, ZSTR_VAL(string), &tmp)) { - sin->sin_addr.s_addr = tmp.s_addr; - } else { - if (ZSTR_LEN(string) > MAXFQDNLEN || ! (host_entry = php_network_gethostbyname(ZSTR_VAL(string)))) { - /* Note: < -10000 indicates a host lookup error */ -#ifdef PHP_WIN32 - PHP_SOCKET_ERROR(php_sock, "Host lookup failed", WSAGetLastError()); -#else - PHP_SOCKET_ERROR(php_sock, "Host lookup failed", (-10000 - h_errno)); -#endif - return 0; - } - if (host_entry->h_addrtype != AF_INET) { - php_error_docref(NULL, E_WARNING, "Host lookup failed: Non AF_INET domain returned on AF_INET socket"); - return 0; - } - memcpy(&(sin->sin_addr.s_addr), host_entry->h_addr_list[0], host_entry->h_length); - } - - return 1; + return php_set_common_addr((struct sockaddr*)sin, AF_INET, string, php_sock); } /* }}} */ From 1f67f610e58b5bb81fbda04e091fddf6d5dbdbf4 Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Fri, 23 Aug 2024 12:21:01 -0300 Subject: [PATCH 04/19] Convert fastcgi listen to use getaddrinfo This is curiously IPv4 only, so I kept the the same semantics. --- main/fastcgi.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/main/fastcgi.c b/main/fastcgi.c index 3488d6e21eaea..2fea5b2d411e8 100644 --- a/main/fastcgi.c +++ b/main/fastcgi.c @@ -682,21 +682,30 @@ int fcgi_listen(const char *path, int backlog) sa.sa_inet.sin_addr.s_addr = htonl(INADDR_ANY); } else { if (!inet_pton(AF_INET, host, &sa.sa_inet.sin_addr)) { - struct hostent *hep; + struct sockaddr **addresses = NULL; + int address_count; - if(strlen(host) > MAXFQDNLEN) { - hep = NULL; + if (strlen(host) > MAXFQDNLEN) { + address_count = 0; } else { - hep = php_network_gethostbyname(host); + zend_string *gai_error = NULL; + address_count = php_network_getaddresses_ex(host, 0, AF_INET, 0, &addresses, &gai_error); + if (gai_error) { + zend_string_release_ex(gai_error, 0); + } } - if (!hep || hep->h_addrtype != AF_INET || !hep->h_addr_list[0]) { + if (address_count == 0 || (*addresses)->sa_family != AF_INET) { fcgi_log(FCGI_ERROR, "Cannot resolve host name '%s'!\n", host); + php_network_freeaddresses(addresses); return -1; - } else if (hep->h_addr_list[1]) { + } else if (address_count > 1) { fcgi_log(FCGI_ERROR, "Host '%s' has multiple addresses. You must choose one explicitly!\n", host); + php_network_freeaddresses(addresses); return -1; } - sa.sa_inet.sin_addr.s_addr = ((struct in_addr*)hep->h_addr_list[0])->s_addr; + struct sockaddr_in *address4 = (struct sockaddr_in*)*addresses; + sa.sa_inet.sin_addr.s_addr = address4->sin_addr.s_addr; + php_network_freeaddresses(addresses); } } } else { From c87eedbcfbfb9631d2cc5ad298040b08247426d1 Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Fri, 23 Aug 2024 12:50:20 -0300 Subject: [PATCH 05/19] Provide function for resolving a single address A common idiom is only caring about the first IP address, for i.e. resolving localhost -> 127.0.0.1/::1. Iterating through and freeing the whole list in this case is tedious. In this case, we provide a function that takes a pointer to a sockaddr storage (so it can be stack allocated), as well as information like address family (for i.e. only IPv4 addresses). This should hopefully be only as boilerplate as gethostbyname was. --- ext/standard/dns.c | 27 ++++++--------------------- main/fastcgi.c | 11 ++++------- main/network.c | 28 ++++++++++++++++++++++++++++ main/php_network.h | 1 + 4 files changed, 39 insertions(+), 28 deletions(-) diff --git a/ext/standard/dns.c b/ext/standard/dns.c index f3878ef38c5c9..5c0981026f1c8 100644 --- a/ext/standard/dns.c +++ b/ext/standard/dns.c @@ -232,9 +232,9 @@ PHP_FUNCTION(gethostbyname) RETURN_STRINGL(hostname, hostname_len); } - struct sockaddr **addresses = NULL; + php_sockaddr_storage resolved; zend_string *gai_error = NULL; - int address_count = php_network_getaddresses(hostname, 0, &addresses, &gai_error); + int address_count = php_network_getaddress(&resolved, hostname, 0, AF_INET, 0, &gai_error); if (gai_error) { zend_string_release_ex(gai_error, 0); } @@ -247,27 +247,12 @@ PHP_FUNCTION(gethostbyname) * Future behaviour change: This function is documented as only returning IPv4 * addresses. We should change this to return IPv6 addresses as well. */ - for (struct sockaddr **address_p = addresses; *address_p != NULL; address_p++) { - struct sockaddr *address = *address_p; - - if (address->sa_family != AF_INET) { - continue; - } - - struct sockaddr_in *address4 = (struct sockaddr_in*)address; - const char *ipaddr; - if (!(ipaddr = inet_ntop(AF_INET, &address4->sin_addr, addr4, INET_ADDRSTRLEN))) { - /* unlikely regarding (too) long hostname and protocols but checking still */ - php_error_docref(NULL, E_WARNING, "Host name to ip failed %s", hostname); - continue; - } else { - ipaddr_zs = zend_string_init(ipaddr, strlen(ipaddr), 0); - break; /* we take only first IP for this function */ - } + struct sockaddr_in *address4 = (struct sockaddr_in*)&resolved; + const char *ipaddr; + if (resolved.ss_family == AF_INET && (ipaddr = inet_ntop(AF_INET, &address4->sin_addr, addr4, INET_ADDRSTRLEN))) { + ipaddr_zs = zend_string_init(ipaddr, strlen(ipaddr), 0); } - php_network_freeaddresses(addresses); - if (ipaddr_zs == NULL) { php_error_docref(NULL, E_WARNING, "Host name to ip failed %s", hostname); RETURN_STRINGL(hostname, hostname_len); diff --git a/main/fastcgi.c b/main/fastcgi.c index 2fea5b2d411e8..ee98b7c504081 100644 --- a/main/fastcgi.c +++ b/main/fastcgi.c @@ -682,30 +682,27 @@ int fcgi_listen(const char *path, int backlog) sa.sa_inet.sin_addr.s_addr = htonl(INADDR_ANY); } else { if (!inet_pton(AF_INET, host, &sa.sa_inet.sin_addr)) { - struct sockaddr **addresses = NULL; + php_sockaddr_storage resolved; int address_count; if (strlen(host) > MAXFQDNLEN) { address_count = 0; } else { zend_string *gai_error = NULL; - address_count = php_network_getaddresses_ex(host, 0, AF_INET, 0, &addresses, &gai_error); + address_count = php_network_getaddress(&resolved, host, 0, AF_INET, 0, &gai_error); if (gai_error) { zend_string_release_ex(gai_error, 0); } } - if (address_count == 0 || (*addresses)->sa_family != AF_INET) { + if (address_count == 0 || resolved.ss_family != AF_INET) { fcgi_log(FCGI_ERROR, "Cannot resolve host name '%s'!\n", host); - php_network_freeaddresses(addresses); return -1; } else if (address_count > 1) { fcgi_log(FCGI_ERROR, "Host '%s' has multiple addresses. You must choose one explicitly!\n", host); - php_network_freeaddresses(addresses); return -1; } - struct sockaddr_in *address4 = (struct sockaddr_in*)*addresses; + struct sockaddr_in *address4 = (struct sockaddr_in*)&resolved; sa.sa_inet.sin_addr.s_addr = address4->sin_addr.s_addr; - php_network_freeaddresses(addresses); } } } else { diff --git a/main/network.c b/main/network.c index 1069943bd4cda..978f550e3df7f 100644 --- a/main/network.c +++ b/main/network.c @@ -288,6 +288,34 @@ PHPAPI int php_network_getaddresses(const char *host, int socktype, struct socka } /* }}} */ +/* {{{ php_network_getaddress + * Returns the number of addresses, and puts first address for a hostname in sockaddr. + */ +PHPAPI int php_network_getaddress(php_sockaddr_storage *sockaddr, const char *host, int socktype, int family, int ai_flags, zend_string **error_string) +{ + struct sockaddr** addresses; + int address_count = php_network_getaddresses_ex(host, socktype, family, ai_flags, &addresses, error_string); + if (address_count == 0) { + return 0; + } + + /* + * we only care about the first address, hopefully getaddrinfo + * filtered to the one we want + */ + struct sockaddr *address = *addresses; + + int sa_size = address->sa_family == AF_INET6 + ? sizeof(struct sockaddr_in6) + : sizeof(struct sockaddr_in); + memcpy(sockaddr, address, sa_size); + +fail: + php_network_freeaddresses(addresses); + return address_count; +} +/* }}} */ + #ifndef O_NONBLOCK #define O_NONBLOCK O_NDELAY #endif diff --git a/main/php_network.h b/main/php_network.h index 84d56f342810b..085fe08a1b07e 100644 --- a/main/php_network.h +++ b/main/php_network.h @@ -288,6 +288,7 @@ BEGIN_EXTERN_C() PHPAPI int php_network_getaddresses(const char *host, int socktype, struct sockaddr ***sal, zend_string **error_string); PHPAPI int php_network_getaddresses_ex(const char *host, int socktype, int family, int ai_flags, struct sockaddr ***sal, zend_string **error_string); PHPAPI void php_network_freeaddresses(struct sockaddr **sal); +PHPAPI int php_network_getaddress(php_sockaddr_storage *sockaddr, const char *host, int socktype, int family, int ai_flags, zend_string **error_string); PHPAPI php_socket_t php_network_connect_socket_to_host_ex(const char *host, unsigned short port, int socktype, int asynchronous, struct timeval *timeout, zend_string **error_string, From 7aa5f84f37ec1c30ca611543eeb6aa97538f0ca1 Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Fri, 23 Aug 2024 13:02:14 -0300 Subject: [PATCH 06/19] Don't bother storing the gai_strerror result The underlying php_network_getaddresses will docref this for us, so there's no point in taking a zend_string we don't have to do anything with. --- ext/standard/dns.c | 12 ++---------- main/fastcgi.c | 6 +----- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/ext/standard/dns.c b/ext/standard/dns.c index 5c0981026f1c8..1427887ffa3e7 100644 --- a/ext/standard/dns.c +++ b/ext/standard/dns.c @@ -233,11 +233,7 @@ PHP_FUNCTION(gethostbyname) } php_sockaddr_storage resolved; - zend_string *gai_error = NULL; - int address_count = php_network_getaddress(&resolved, hostname, 0, AF_INET, 0, &gai_error); - if (gai_error) { - zend_string_release_ex(gai_error, 0); - } + int address_count = php_network_getaddress(&resolved, hostname, 0, AF_INET, 0, NULL); if (address_count == 0) { /* don't need to docref here, getaddresses E_WARNINGs for us */ RETURN_STRINGL(hostname, hostname_len); @@ -280,11 +276,7 @@ PHP_FUNCTION(gethostbynamel) } struct sockaddr **addresses = NULL; - zend_string *gai_error = NULL; - int address_count = php_network_getaddresses(hostname, 0, &addresses, &gai_error); - if (gai_error) { - zend_string_release_ex(gai_error, 0); - } + int address_count = php_network_getaddresses(hostname, 0, &addresses, NULL); if (address_count == 0) { /* don't need to docref here, getaddresses E_WARNINGs for us */ RETURN_FALSE; diff --git a/main/fastcgi.c b/main/fastcgi.c index ee98b7c504081..756cb98654246 100644 --- a/main/fastcgi.c +++ b/main/fastcgi.c @@ -688,11 +688,7 @@ int fcgi_listen(const char *path, int backlog) if (strlen(host) > MAXFQDNLEN) { address_count = 0; } else { - zend_string *gai_error = NULL; - address_count = php_network_getaddress(&resolved, host, 0, AF_INET, 0, &gai_error); - if (gai_error) { - zend_string_release_ex(gai_error, 0); - } + address_count = php_network_getaddress(&resolved, host, 0, AF_INET, 0, NULL); } if (address_count == 0 || resolved.ss_family != AF_INET) { fcgi_log(FCGI_ERROR, "Cannot resolve host name '%s'!\n", host); From 3fb4f8ab42c53eba5d1065e25329d62fa5e3c85b Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Fri, 23 Aug 2024 13:03:51 -0300 Subject: [PATCH 07/19] Convert php_open_listen_sock to use getaddrinfo Also curiously IPv4 only... --- ext/sockets/sockets.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index 4582de4bc8ecb..db428831bbc0c 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -244,10 +244,20 @@ ZEND_GET_MODULE(sockets) static bool php_open_listen_sock(php_socket *sock, unsigned short port, int backlog) /* {{{ */ { struct sockaddr_in la = {0}; + php_sockaddr_storage resolved; - la.sin_addr.s_addr = htonl(INADDR_ANY); - la.sin_family = AF_INET; - la.sin_port = htons(port); +#ifndef PHP_WIN32 + const char *hostname = "0.0.0.0"; +#else + const char *hostname = "localhost"; +#endif + + if (php_network_getaddress(&resolved, hostname, SOCK_STREAM, AF_INET, 0, NULL) == 0 || resolved.ss_family != AF_INET) { + return 0; + } + + memcpy(&la, &resolved, sizeof(struct sockaddr_in)); + la.sin_port = htons((unsigned short) port); sock->bsd_socket = socket(PF_INET, SOCK_STREAM, 0); sock->blocking = 1; From 30954d5bd8e101e4d51c6524dc4ef733d1093bd1 Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Fri, 23 Aug 2024 13:52:58 -0300 Subject: [PATCH 08/19] Remove remnant of refactor --- main/network.c | 1 - 1 file changed, 1 deletion(-) diff --git a/main/network.c b/main/network.c index 978f550e3df7f..3a00632bdf63e 100644 --- a/main/network.c +++ b/main/network.c @@ -310,7 +310,6 @@ PHPAPI int php_network_getaddress(php_sockaddr_storage *sockaddr, const char *ho : sizeof(struct sockaddr_in); memcpy(sockaddr, address, sa_size); -fail: php_network_freeaddresses(addresses); return address_count; } From 8abf64708b8447e15dc8c2b524822a9a48d09b29 Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Fri, 23 Aug 2024 14:12:20 -0300 Subject: [PATCH 09/19] Convert sockaddr conv to use bool --- ext/sockets/conversions.c | 4 ++-- ext/sockets/sockaddr_conv.c | 32 ++++++++++++++++---------------- ext/sockets/sockaddr_conv.h | 6 +++--- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/ext/sockets/conversions.c b/ext/sockets/conversions.c index 1c735ef5e6d8f..34b893132d20d 100644 --- a/ext/sockets/conversions.c +++ b/ext/sockets/conversions.c @@ -550,7 +550,7 @@ static void to_zval_read_uid_t(const char *data, zval *zv, res_context *ctx) /* CONVERSIONS for sockaddr */ static void from_zval_write_sin_addr(const zval *zaddr_str, char *inaddr, ser_context *ctx) { - int res; + bool res; struct sockaddr_in saddr = {0}; zend_string *addr_str, *tmp_addr_str; @@ -599,7 +599,7 @@ static void to_zval_read_sockaddr_in(const char *data, zval *zv, res_context *ct #ifdef HAVE_IPV6 static void from_zval_write_sin6_addr(const zval *zaddr_str, char *addr6, ser_context *ctx) { - int res; + bool res; struct sockaddr_in6 saddr6 = {0}; zend_string *addr_str, *tmp_addr_str; diff --git a/ext/sockets/sockaddr_conv.c b/ext/sockets/sockaddr_conv.c index 231b35a7f42b5..773570a0ee62c 100644 --- a/ext/sockets/sockaddr_conv.c +++ b/ext/sockets/sockaddr_conv.c @@ -11,7 +11,7 @@ extern zend_result php_string_to_if_index(const char *val, unsigned *out); -int php_set_common_addr(struct sockaddr *sin, int family, char *string, php_socket *php_sock) /* {{{ */ +bool php_set_common_addr(struct sockaddr *sin, int family, zend_string *string, php_socket *php_sock) /* {{{ */ { #ifdef HAVE_GETADDRINFO struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)sin; @@ -22,9 +22,9 @@ int php_set_common_addr(struct sockaddr *sin, int family, char *string, php_sock struct addrinfo hints; struct addrinfo *addrinfo = NULL; - if (family == AF_INET6 && inet_pton(AF_INET6, string, &tmp6)) { + if (family == AF_INET6 && inet_pton(AF_INET6, ZSTR_VAL(string), &tmp6)) { memcpy(&(sin6->sin6_addr.s6_addr), &(tmp6.s6_addr), sizeof(struct in6_addr)); - } else if (family == AF_INET && inet_pton(AF_INET, string, &tmp4)) { + } else if (family == AF_INET && inet_pton(AF_INET, ZSTR_VAL(string), &tmp4)) { sin4->sin_addr.s_addr = tmp4.s_addr; } else { memset(&hints, 0, sizeof(struct addrinfo)); @@ -41,12 +41,12 @@ int php_set_common_addr(struct sockaddr *sin, int family, char *string, php_sock #else PHP_SOCKET_ERROR(php_sock, "Host lookup failed", (-10000 - h_errno)); #endif - return 0; + return false; } if (addrinfo->ai_family != family) { php_error_docref(NULL, E_WARNING, "Host lookup failed: Wrong address family returned for socket"); freeaddrinfo(addrinfo); - return 0; + return false; } if (addrinfo->ai_family == AF_INET6) { @@ -57,23 +57,23 @@ int php_set_common_addr(struct sockaddr *sin, int family, char *string, php_sock freeaddrinfo(addrinfo); } - return 1; + return true; #else php_error_docref(NULL, E_WARNING, "Host lookup failed: getaddrinfo() not available on this system"); - return 0; + return true; #endif } /* }}} */ #ifdef HAVE_IPV6 /* Sets addr by hostname, or by ip in string form (AF_INET6) */ -int php_set_inet6_addr(struct sockaddr_in6 *sin6, char *string, php_socket *php_sock) /* {{{ */ +bool php_set_inet6_addr(struct sockaddr_in6 *sin6, char *string, php_socket *php_sock) /* {{{ */ { - char *scope = strchr(string, '%'); + char *scope = strchr(ZSTR_VAL(string), '%'); - int ret = php_set_common_addr((struct sockaddr*)sin6, AF_INET6, string, php_sock); + bool ret = php_set_common_addr((struct sockaddr*)sin6, AF_INET6, string, php_sock); if (!ret) { - return 0; + return false; } if (scope) { @@ -100,7 +100,7 @@ int php_set_inet6_addr(struct sockaddr_in6 *sin6, char *string, php_socket *php_ #endif /* Sets addr by hostname, or by ip in string form (AF_INET) */ -int php_set_inet_addr(struct sockaddr_in *sin, zend_string *string, php_socket *php_sock) /* {{{ */ +bool php_set_inet_addr(struct sockaddr_in *sin, zend_string *string, php_socket *php_sock) /* {{{ */ { return php_set_common_addr((struct sockaddr*)sin, AF_INET, string, php_sock); } @@ -108,7 +108,7 @@ int php_set_inet_addr(struct sockaddr_in *sin, zend_string *string, php_socket * /* Sets addr by hostname or by ip in string form (AF_INET or AF_INET6, * depending on the socket) */ -int php_set_inet46_addr(php_sockaddr_storage *ss, socklen_t *ss_len, zend_string *string, php_socket *php_sock) /* {{{ */ +bool php_set_inet46_addr(php_sockaddr_storage *ss, socklen_t *ss_len, zend_string *string, php_socket *php_sock) /* {{{ */ { if (php_sock->type == AF_INET) { struct sockaddr_in t = {0}; @@ -116,7 +116,7 @@ int php_set_inet46_addr(php_sockaddr_storage *ss, socklen_t *ss_len, zend_string memcpy(ss, &t, sizeof t); ss->ss_family = AF_INET; *ss_len = sizeof(t); - return 1; + return true; } } #ifdef HAVE_IPV6 @@ -126,12 +126,12 @@ int php_set_inet46_addr(php_sockaddr_storage *ss, socklen_t *ss_len, zend_string memcpy(ss, &t, sizeof t); ss->ss_family = AF_INET6; *ss_len = sizeof(t); - return 1; + return true; } } #endif else { zend_value_error("IP address used in the context of an unexpected type of socket"); } - return 0; + return false; } diff --git a/ext/sockets/sockaddr_conv.h b/ext/sockets/sockaddr_conv.h index 93104e6470fb6..a22ce2b22875f 100644 --- a/ext/sockets/sockaddr_conv.h +++ b/ext/sockets/sockaddr_conv.h @@ -16,16 +16,16 @@ * The IPv6 literal can be a IPv4 mapped address (like ::ffff:127.0.0.1). * If the hostname yields no IPv6 addresses, a mapped IPv4 address may be returned (AI_V4MAPPED) */ -int php_set_inet6_addr(struct sockaddr_in6 *sin6, zend_string *string, php_socket *php_sock); +bool php_set_inet6_addr(struct sockaddr_in6 *sin6, zend_string *string, php_socket *php_sock); /* * Convert an IPv4 literal or a hostname into a sockaddr_in. */ -int php_set_inet_addr(struct sockaddr_in *sin, zend_string *string, php_socket *php_sock); +bool php_set_inet_addr(struct sockaddr_in *sin, zend_string *string, php_socket *php_sock); /* * Calls either php_set_inet6_addr() or php_set_inet_addr(), depending on the type of the socket. */ -int php_set_inet46_addr(php_sockaddr_storage *ss, socklen_t *ss_len, zend_string *string, php_socket *php_sock); +bool php_set_inet46_addr(php_sockaddr_storage *ss, socklen_t *ss_len, zend_string *string, php_socket *php_sock); #endif From c2983d766fd6607623e0ddc646694065ce01a7c8 Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Fri, 23 Aug 2024 14:25:19 -0300 Subject: [PATCH 10/19] Remove getaddrinfo->gethostbyname fallback We will no longer be using this function. --- main/network.c | 31 +------------------------------ 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/main/network.c b/main/network.c index 3a00632bdf63e..8a23706dcec93 100644 --- a/main/network.c +++ b/main/network.c @@ -242,36 +242,7 @@ PHPAPI int php_network_getaddresses_ex(const char *host, int socktype, int famil freeaddrinfo(res); #else - if (!inet_pton(AF_INET, host, &in)) { - if(strlen(host) > MAXFQDNLEN) { - host_info = NULL; - errno = E2BIG; - } else { - host_info = php_network_gethostbyname(host); - } - if (host_info == NULL) { - if (error_string) { - /* free error string received during previous iteration (if any) */ - if (*error_string) { - zend_string_release_ex(*error_string, 0); - } - *error_string = strpprintf(0, "php_network_getaddresses: gethostbyname failed. errno=%d", errno); - php_error_docref(NULL, E_WARNING, "%s", ZSTR_VAL(*error_string)); - } else { - php_error_docref(NULL, E_WARNING, "php_network_getaddresses: gethostbyname failed"); - } - return 0; - } - in = *((struct in_addr *) host_info->h_addr); - } - - *sal = safe_emalloc(2, sizeof(**sal), 0); - sap = *sal; - *sap = emalloc(sizeof(struct sockaddr_in)); - (*sap)->sa_family = AF_INET; - ((struct sockaddr_in *)*sap)->sin_addr = in; - sap++; - n = 1; + php_error_docref(NULL, E_WARNING, "php_network_getaddresses: getaddrinfo() not available on this system"); #endif *sap = NULL; From fcc6ba8c740570b0564fefe4b9a30ad6a90ee84e Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Fri, 23 Aug 2024 14:28:36 -0300 Subject: [PATCH 11/19] Remove gethostbyname from ABI (mostly) Callers should use php_network_getaddress* instead. --- build/ax_func_which_gethostbyname_r.m4 | 196 ------------------------- configure.ac | 1 - main/network.c | 88 ----------- main/php_network.h | 2 - 4 files changed, 287 deletions(-) delete mode 100644 build/ax_func_which_gethostbyname_r.m4 diff --git a/build/ax_func_which_gethostbyname_r.m4 b/build/ax_func_which_gethostbyname_r.m4 deleted file mode 100644 index bb6bc959e7074..0000000000000 --- a/build/ax_func_which_gethostbyname_r.m4 +++ /dev/null @@ -1,196 +0,0 @@ -# ================================================================================== -# https://www.gnu.org/software/autoconf-archive/ax_func_which_gethostbyname_r.html -# ================================================================================== -# -# SYNOPSIS -# -# AX_FUNC_WHICH_GETHOSTBYNAME_R -# -# DESCRIPTION -# -# Determines which historical variant of the gethostbyname_r() call -# (taking three, five, or six arguments) is available on the system and -# defines one of the following macros accordingly: -# -# HAVE_FUNC_GETHOSTBYNAME_R_6 -# HAVE_FUNC_GETHOSTBYNAME_R_5 -# HAVE_FUNC_GETHOSTBYNAME_R_3 -# -# as well as -# -# HAVE_GETHOSTBYNAME_R -# -# If used in conjunction with gethostname.c, the API demonstrated in -# test.c can be used regardless of which gethostbyname_r() is available. -# These example files can be found at -# http://www.csn.ul.ie/~caolan/publink/gethostbyname_r -# -# based on David Arnold's autoconf suggestion in the threads faq -# -# Originally named "AC_caolan_FUNC_WHICH_GETHOSTBYNAME_R". Rewritten for -# Autoconf 2.5x, and updated for 2.68 by Daniel Richard G. -# -# LICENSE -# -# Copyright (c) 2008 Caolan McNamara -# Copyright (c) 2008 Daniel Richard G. -# -# This program is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2 of the License, or (at your -# option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General -# Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program. If not, see . -# -# As a special exception, the respective Autoconf Macro's copyright owner -# gives unlimited permission to copy, distribute and modify the configure -# scripts that are the output of Autoconf when processing the Macro. You -# need not follow the terms of the GNU General Public License when using -# or distributing such scripts, even though portions of the text of the -# Macro appear in them. The GNU General Public License (GPL) does govern -# all other use of the material that constitutes the Autoconf Macro. -# -# This special exception to the GPL applies to versions of the Autoconf -# Macro released by the Autoconf Archive. When you make and distribute a -# modified version of the Autoconf Macro, you may extend this special -# exception to the GPL to apply to your modified version as well. - -#serial 8 - -AC_DEFUN([AX_FUNC_WHICH_GETHOSTBYNAME_R], [ - - AC_LANG_PUSH([C]) - AC_MSG_CHECKING([how many arguments gethostbyname_r() takes]) - - AC_CACHE_VAL([ac_cv_func_which_gethostbyname_r], [ - -################################################################ - -ac_cv_func_which_gethostbyname_r=unknown - -# -# ONE ARGUMENT (sanity check) -# - -# This should fail, as there is no variant of gethostbyname_r() that takes -# a single argument. If it actually compiles, then we can assume that -# netdb.h is not declaring the function, and the compiler is thereby -# assuming an implicit prototype. In which case, we're out of luck. -# -AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include ], - [ - char *name = "www.gnu.org"; - (void)gethostbyname_r(name) /* ; */ - ])], - [ac_cv_func_which_gethostbyname_r=no]) - -# -# SIX ARGUMENTS -# (e.g. Linux) -# - -if test "$ac_cv_func_which_gethostbyname_r" = "unknown"; then - -AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include ], - [ - char *name = "www.gnu.org"; - struct hostent ret, *retp; - char buf@<:@1024@:>@; - int buflen = 1024; - int my_h_errno; - (void)gethostbyname_r(name, &ret, buf, buflen, &retp, &my_h_errno) /* ; */ - ])], - [ac_cv_func_which_gethostbyname_r=six]) - -fi - -# -# FIVE ARGUMENTS -# (e.g. Solaris) -# - -if test "$ac_cv_func_which_gethostbyname_r" = "unknown"; then - -AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include ], - [ - char *name = "www.gnu.org"; - struct hostent ret; - char buf@<:@1024@:>@; - int buflen = 1024; - int my_h_errno; - (void)gethostbyname_r(name, &ret, buf, buflen, &my_h_errno) /* ; */ - ])], - [ac_cv_func_which_gethostbyname_r=five]) - -fi - -# -# THREE ARGUMENTS -# (e.g. AIX, HP-UX, Tru64) -# - -if test "$ac_cv_func_which_gethostbyname_r" = "unknown"; then - -AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include ], - [ - char *name = "www.gnu.org"; - struct hostent ret; - struct hostent_data data; - (void)gethostbyname_r(name, &ret, &data) /* ; */ - ])], - [ac_cv_func_which_gethostbyname_r=three]) - -fi - -################################################################ - -]) dnl end AC_CACHE_VAL - -case "$ac_cv_func_which_gethostbyname_r" in - three|five|six) - AC_DEFINE([HAVE_GETHOSTBYNAME_R], [1], - [Define to 1 if you have some form of gethostbyname_r().]) - ;; -esac - -case "$ac_cv_func_which_gethostbyname_r" in - three) - AC_MSG_RESULT([three]) - AC_DEFINE([HAVE_FUNC_GETHOSTBYNAME_R_3], [1], - [Define to 1 if you have the three-argument form of gethostbyname_r().]) - ;; - - five) - AC_MSG_RESULT([five]) - AC_DEFINE([HAVE_FUNC_GETHOSTBYNAME_R_5], [1], - [Define to 1 if you have the five-argument form of gethostbyname_r().]) - ;; - - six) - AC_MSG_RESULT([six]) - AC_DEFINE([HAVE_FUNC_GETHOSTBYNAME_R_6], [1], - [Define to 1 if you have the six-argument form of gethostbyname_r().]) - ;; - - no) - AC_MSG_RESULT([cannot find function declaration in netdb.h]) - ;; - - unknown) - AC_MSG_RESULT([can't tell]) - ;; - - *) - AC_MSG_ERROR([internal error]) - ;; -esac - -AC_LANG_POP - -]) dnl end AC_DEFUN diff --git a/configure.ac b/configure.ac index 214c0ab91b2a5..e1fd5805263da 100644 --- a/configure.ac +++ b/configure.ac @@ -4,7 +4,6 @@ dnl Include external macro definitions before the AC_INIT to also remove dnl comments starting with # and empty newlines from the included files. dnl ---------------------------------------------------------------------------- m4_include([build/ax_check_compile_flag.m4]) -m4_include([build/ax_func_which_gethostbyname_r.m4]) m4_include([build/ax_gcc_func_attribute.m4]) m4_include([build/libtool.m4]) diff --git a/main/network.c b/main/network.c index 8a23706dcec93..0aa383a899b3c 100644 --- a/main/network.c +++ b/main/network.c @@ -1459,91 +1459,3 @@ PHPAPI int php_poll2(php_pollfd *ufds, unsigned int nfds, int timeout) return n; } #endif - -#if defined(HAVE_GETHOSTBYNAME_R) -#ifdef HAVE_FUNC_GETHOSTBYNAME_R_6 -static struct hostent * gethostname_re (const char *host,struct hostent *hostbuf,char **tmphstbuf,size_t *hstbuflen) -{ - struct hostent *hp; - int herr,res; - - if (*hstbuflen == 0) { - *hstbuflen = 1024; - *tmphstbuf = (char *)malloc (*hstbuflen); - } - - while (( res = - gethostbyname_r(host,hostbuf,*tmphstbuf,*hstbuflen,&hp,&herr)) - && (errno == ERANGE)) { - /* Enlarge the buffer. */ - *hstbuflen *= 2; - *tmphstbuf = (char *)realloc (*tmphstbuf,*hstbuflen); - } - - if (res != 0) { - return NULL; - } - - return hp; -} -#endif -#ifdef HAVE_FUNC_GETHOSTBYNAME_R_5 -static struct hostent * gethostname_re (const char *host,struct hostent *hostbuf,char **tmphstbuf,size_t *hstbuflen) -{ - struct hostent *hp; - int herr; - - if (*hstbuflen == 0) { - *hstbuflen = 1024; - *tmphstbuf = (char *)malloc (*hstbuflen); - } - - while ((NULL == ( hp = - gethostbyname_r(host,hostbuf,*tmphstbuf,*hstbuflen,&herr))) - && (errno == ERANGE)) { - /* Enlarge the buffer. */ - *hstbuflen *= 2; - *tmphstbuf = (char *)realloc (*tmphstbuf,*hstbuflen); - } - return hp; -} -#endif -#ifdef HAVE_FUNC_GETHOSTBYNAME_R_3 -static struct hostent * gethostname_re (const char *host,struct hostent *hostbuf,char **tmphstbuf,size_t *hstbuflen) -{ - if (*hstbuflen == 0) { - *hstbuflen = sizeof(struct hostent_data); - *tmphstbuf = (char *)malloc (*hstbuflen); - } else { - if (*hstbuflen < sizeof(struct hostent_data)) { - *hstbuflen = sizeof(struct hostent_data); - *tmphstbuf = (char *)realloc(*tmphstbuf, *hstbuflen); - } - } - memset((void *)(*tmphstbuf),0,*hstbuflen); - - if (0 != gethostbyname_r(host,hostbuf,(struct hostent_data *)*tmphstbuf)) { - return NULL; - } - - return hostbuf; -} -#endif -#endif - -PHPAPI struct hostent* php_network_gethostbyname(const char *name) { -#if !defined(HAVE_GETHOSTBYNAME_R) - return gethostbyname(name); -#else - if (FG(tmp_host_buf)) { - free(FG(tmp_host_buf)); - } - - FG(tmp_host_buf) = NULL; - FG(tmp_host_buf_len) = 0; - - memset(&FG(tmp_host_info), 0, sizeof(struct hostent)); - - return gethostname_re(name, &FG(tmp_host_info), &FG(tmp_host_buf), &FG(tmp_host_buf_len)); -#endif -} diff --git a/main/php_network.h b/main/php_network.h index 085fe08a1b07e..1c6d7209cf061 100644 --- a/main/php_network.h +++ b/main/php_network.h @@ -385,8 +385,6 @@ PHPAPI void php_network_populate_name_from_sockaddr( PHPAPI zend_result php_network_parse_network_address_with_port(const char *addr, size_t addrlen, struct sockaddr *sa, socklen_t *sl); -PHPAPI struct hostent* php_network_gethostbyname(const char *name); - PHPAPI zend_result php_set_sock_blocking(php_socket_t socketd, bool block); END_EXTERN_C() From 5957f97032065412c1df112dd673d3fa541db9b3 Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Fri, 23 Aug 2024 14:40:51 -0300 Subject: [PATCH 12/19] Convert php_network_getaddress* to use unsigned size_t Since this can never return -1 (0 on error/none, >0 for addresses) --- ext/snmp/snmp.c | 2 +- ext/standard/dns.c | 4 ++-- main/fastcgi.c | 2 +- main/network.c | 13 +++++++------ main/php_network.h | 6 +++--- sapi/cli/php_cli_server.c | 2 +- 6 files changed, 15 insertions(+), 14 deletions(-) diff --git a/ext/snmp/snmp.c b/ext/snmp/snmp.c index 1daa51851fb66..631f8a0c45a4f 100644 --- a/ext/snmp/snmp.c +++ b/ext/snmp/snmp.c @@ -832,7 +832,7 @@ static bool snmp_session_init(php_snmp_session **session_p, int version, zend_st php_snmp_session *session; char *pptr, *host_ptr; bool force_ipv6 = false; - int n; + size_t n; struct sockaddr **psal; struct sockaddr **res; diff --git a/ext/standard/dns.c b/ext/standard/dns.c index 1427887ffa3e7..debfc2fc05d04 100644 --- a/ext/standard/dns.c +++ b/ext/standard/dns.c @@ -233,7 +233,7 @@ PHP_FUNCTION(gethostbyname) } php_sockaddr_storage resolved; - int address_count = php_network_getaddress(&resolved, hostname, 0, AF_INET, 0, NULL); + size_t address_count = php_network_getaddress(&resolved, hostname, 0, AF_INET, 0, NULL); if (address_count == 0) { /* don't need to docref here, getaddresses E_WARNINGs for us */ RETURN_STRINGL(hostname, hostname_len); @@ -276,7 +276,7 @@ PHP_FUNCTION(gethostbynamel) } struct sockaddr **addresses = NULL; - int address_count = php_network_getaddresses(hostname, 0, &addresses, NULL); + size_t address_count = php_network_getaddresses(hostname, 0, &addresses, NULL); if (address_count == 0) { /* don't need to docref here, getaddresses E_WARNINGs for us */ RETURN_FALSE; diff --git a/main/fastcgi.c b/main/fastcgi.c index 756cb98654246..d07d613124a56 100644 --- a/main/fastcgi.c +++ b/main/fastcgi.c @@ -683,7 +683,7 @@ int fcgi_listen(const char *path, int backlog) } else { if (!inet_pton(AF_INET, host, &sa.sa_inet.sin_addr)) { php_sockaddr_storage resolved; - int address_count; + size_t address_count; if (strlen(host) > MAXFQDNLEN) { address_count = 0; diff --git a/main/network.c b/main/network.c index 0aa383a899b3c..5badbac7e5c34 100644 --- a/main/network.c +++ b/main/network.c @@ -145,10 +145,11 @@ PHPAPI void php_network_freeaddresses(struct sockaddr **sal) /* {{{ php_network_getaddresses_ex * Returns number of addresses, 0 for none/error */ -PHPAPI int php_network_getaddresses_ex(const char *host, int socktype, int family, int ai_flags, struct sockaddr ***sal, zend_string **error_string) +PHPAPI size_t php_network_getaddresses_ex(const char *host, int socktype, int family, int ai_flags, struct sockaddr ***sal, zend_string **error_string) { struct sockaddr **sap; - int n; + size_t n; + int ret; #ifdef HAVE_GETADDRINFO # ifdef HAVE_IPV6 static int ipv6_borked = -1; /* the way this is used *is* thread safe */ @@ -190,7 +191,7 @@ PHPAPI int php_network_getaddresses_ex(const char *host, int socktype, int famil hints.ai_family = ipv6_borked ? AF_INET : family; # endif - if ((n = getaddrinfo(host, NULL, &hints, &res))) { + if ((ret = getaddrinfo(host, NULL, &hints, &res))) { # if defined(PHP_WIN32) char *gai_error = php_win32_error_to_msg(n); # elif defined(HAVE_GAI_STRERROR) @@ -253,7 +254,7 @@ PHPAPI int php_network_getaddresses_ex(const char *host, int socktype, int famil /* {{{ php_network_getaddresses * Returns number of addresses, 0 for none/error */ -PHPAPI int php_network_getaddresses(const char *host, int socktype, struct sockaddr ***sal, zend_string **error_string) +PHPAPI size_t php_network_getaddresses(const char *host, int socktype, struct sockaddr ***sal, zend_string **error_string) { return php_network_getaddresses_ex(host, socktype, AF_UNSPEC, 0, sal, error_string); } @@ -262,10 +263,10 @@ PHPAPI int php_network_getaddresses(const char *host, int socktype, struct socka /* {{{ php_network_getaddress * Returns the number of addresses, and puts first address for a hostname in sockaddr. */ -PHPAPI int php_network_getaddress(php_sockaddr_storage *sockaddr, const char *host, int socktype, int family, int ai_flags, zend_string **error_string) +PHPAPI size_t php_network_getaddress(php_sockaddr_storage *sockaddr, const char *host, int socktype, int family, int ai_flags, zend_string **error_string) { struct sockaddr** addresses; - int address_count = php_network_getaddresses_ex(host, socktype, family, ai_flags, &addresses, error_string); + size_t address_count = php_network_getaddresses_ex(host, socktype, family, ai_flags, &addresses, error_string); if (address_count == 0) { return 0; } diff --git a/main/php_network.h b/main/php_network.h index 1c6d7209cf061..777264bc61a83 100644 --- a/main/php_network.h +++ b/main/php_network.h @@ -285,10 +285,10 @@ typedef struct { } php_sockvals; BEGIN_EXTERN_C() -PHPAPI int php_network_getaddresses(const char *host, int socktype, struct sockaddr ***sal, zend_string **error_string); -PHPAPI int php_network_getaddresses_ex(const char *host, int socktype, int family, int ai_flags, struct sockaddr ***sal, zend_string **error_string); +PHPAPI size_t php_network_getaddresses(const char *host, int socktype, struct sockaddr ***sal, zend_string **error_string); +PHPAPI size_t php_network_getaddresses_ex(const char *host, int socktype, int family, int ai_flags, struct sockaddr ***sal, zend_string **error_string); PHPAPI void php_network_freeaddresses(struct sockaddr **sal); -PHPAPI int php_network_getaddress(php_sockaddr_storage *sockaddr, const char *host, int socktype, int family, int ai_flags, zend_string **error_string); +PHPAPI size_t php_network_getaddress(php_sockaddr_storage *sockaddr, const char *host, int socktype, int family, int ai_flags, zend_string **error_string); PHPAPI php_socket_t php_network_connect_socket_to_host_ex(const char *host, unsigned short port, int socktype, int asynchronous, struct timeval *timeout, zend_string **error_string, diff --git a/sapi/cli/php_cli_server.c b/sapi/cli/php_cli_server.c index 65d6d5a898de3..88e874c56480c 100644 --- a/sapi/cli/php_cli_server.c +++ b/sapi/cli/php_cli_server.c @@ -1275,7 +1275,7 @@ static php_socket_t php_network_listen_socket(const char *host, int *port, int s int err = 0; struct sockaddr *sa = NULL, **p, **sal; - int num_addrs = php_network_getaddresses(host, socktype, &sal, errstr); + size_t num_addrs = php_network_getaddresses(host, socktype, &sal, errstr); if (num_addrs == 0) { return -1; } From b960ae60475ae84b619a0e9410885df2b7b33cea Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Fri, 23 Aug 2024 14:45:32 -0300 Subject: [PATCH 13/19] Clean up remaining gethostbyname(_r) detritus Includes some global state as well. --- configure.ac | 2 -- ext/standard/file.c | 5 ----- ext/standard/file.h | 5 ----- main/php_network.h | 2 +- 4 files changed, 1 insertion(+), 13 deletions(-) diff --git a/configure.ac b/configure.ac index e1fd5805263da..42a66317c6ab2 100644 --- a/configure.ac +++ b/configure.ac @@ -625,8 +625,6 @@ dnl Check for functions inside their belonging headers. AC_CHECK_HEADER([sys/prctl.h], [AC_CHECK_FUNCS([prctl])]) AC_CHECK_HEADER([sys/procctl.h], [AC_CHECK_FUNCS([procctl])]) -AX_FUNC_WHICH_GETHOSTBYNAME_R - dnl Some systems (Solaris 10) do not have nanosleep in libc. AC_CHECK_FUNCS([nanosleep],, [AC_SEARCH_LIBS([nanosleep], [rt], [AC_DEFINE([HAVE_NANOSLEEP], [1])])]) diff --git a/ext/standard/file.c b/ext/standard/file.c index 364985f786b7e..c035ea091abf6 100644 --- a/ext/standard/file.c +++ b/ext/standard/file.c @@ -133,11 +133,6 @@ static void file_globals_ctor(php_file_globals *file_globals_p) static void file_globals_dtor(php_file_globals *file_globals_p) { -#if defined(HAVE_GETHOSTBYNAME_R) - if (file_globals_p->tmp_host_buf) { - free(file_globals_p->tmp_host_buf); - } -#endif } static PHP_INI_MH(OnUpdateAutoDetectLineEndings) diff --git a/ext/standard/file.h b/ext/standard/file.h index f8faebd028293..bc585eb6c659c 100644 --- a/ext/standard/file.h +++ b/ext/standard/file.h @@ -102,11 +102,6 @@ typedef struct { HashTable *stream_filters; /* per-request copy of stream_filters_hash */ HashTable *wrapper_errors; /* key: wrapper address; value: linked list of char* */ int pclose_wait; -#ifdef HAVE_GETHOSTBYNAME_R - struct hostent tmp_host_info; - char *tmp_host_buf; - size_t tmp_host_buf_len; -#endif } php_file_globals; #ifdef ZTS diff --git a/main/php_network.h b/main/php_network.h index 777264bc61a83..f9a88116bafd0 100644 --- a/main/php_network.h +++ b/main/php_network.h @@ -81,7 +81,7 @@ END_EXTERN_C() #include #endif -#ifdef HAVE_GETHOSTBYNAME_R +#ifndef PHP_WIN32 #include #endif From 9dd12f444a95ca43365ad763751fb7e17d559cfc Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Fri, 23 Aug 2024 16:32:29 -0300 Subject: [PATCH 14/19] gai_strerror will return differently per platform --- ext/standard/tests/network/gethostbyname_error003.phpt | 2 +- ext/standard/tests/network/gethostbyname_error006.phpt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/standard/tests/network/gethostbyname_error003.phpt b/ext/standard/tests/network/gethostbyname_error003.phpt index 47ae902bb4ad2..4e16fccba8775 100644 --- a/ext/standard/tests/network/gethostbyname_error003.phpt +++ b/ext/standard/tests/network/gethostbyname_error003.phpt @@ -7,5 +7,5 @@ gethostbyname() function - basic type return error test var_dump(is_string(gethostbyname("asdfasdf"))); ?> --EXPECTF-- -Warning: gethostbyname(): php_network_getaddresses: getaddrinfo for asdfasdf failed: nodename nor servname provided, or not known in %s on line %d +Warning: gethostbyname(): php_network_getaddresses: getaddrinfo for asdfasdf failed: %s in %s on line %d bool(true) diff --git a/ext/standard/tests/network/gethostbyname_error006.phpt b/ext/standard/tests/network/gethostbyname_error006.phpt index 764f3fce5c5d1..9b667e63cbdda 100644 --- a/ext/standard/tests/network/gethostbyname_error006.phpt +++ b/ext/standard/tests/network/gethostbyname_error006.phpt @@ -7,5 +7,5 @@ gethostbyname() function - basic invalid parameter test var_dump(gethostbyname(".toto.toto.toto")); ?> --EXPECTF-- -Warning: gethostbyname(): php_network_getaddresses: getaddrinfo for .toto.toto.toto failed: nodename nor servname provided, or not known in %s on line %d +Warning: gethostbyname(): php_network_getaddresses: getaddrinfo for .toto.toto.toto failed: %s in %s on line %d string(15) ".toto.toto.toto" From c22fd2ce5be1f25c24f35a7a44971addb6f80a7e Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Fri, 23 Aug 2024 16:34:00 -0300 Subject: [PATCH 15/19] getaddrinfo emits warnings now for failures to resolve this borks curl tests, so supress warnings on it there --- ext/curl/tests/curl_basic_008.phpt | 2 +- ext/curl/tests/curl_basic_010.phpt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/curl/tests/curl_basic_008.phpt b/ext/curl/tests/curl_basic_008.phpt index d9601dfcac125..11e733fd1fa7c 100644 --- a/ext/curl/tests/curl_basic_008.phpt +++ b/ext/curl/tests/curl_basic_008.phpt @@ -7,7 +7,7 @@ curl --SKIPIF-- diff --git a/ext/curl/tests/curl_basic_010.phpt b/ext/curl/tests/curl_basic_010.phpt index 908886b2e8418..a94d3864314db 100644 --- a/ext/curl/tests/curl_basic_010.phpt +++ b/ext/curl/tests/curl_basic_010.phpt @@ -7,7 +7,7 @@ curl --SKIPIF-- From b4e3c0cad8aef44b8d7f80379efb99da0d15e43a Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Fri, 23 Aug 2024 21:56:44 -0300 Subject: [PATCH 16/19] Remove old file submodule dtor --- ext/standard/basic_functions.c | 1 - ext/standard/file.c | 15 +-------------- ext/standard/file.h | 1 - 3 files changed, 1 insertion(+), 16 deletions(-) diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index fbbeeb0b433f4..099c41e290967 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -371,7 +371,6 @@ PHP_MSHUTDOWN_FUNCTION(basic) /* {{{ */ BASIC_MSHUTDOWN_SUBMODULE(array) BASIC_MSHUTDOWN_SUBMODULE(assert) BASIC_MSHUTDOWN_SUBMODULE(url_scanner_ex) - BASIC_MSHUTDOWN_SUBMODULE(file) BASIC_MSHUTDOWN_SUBMODULE(standard_filters) #ifdef ZTS BASIC_MSHUTDOWN_SUBMODULE(localeconv) diff --git a/ext/standard/file.c b/ext/standard/file.c index c035ea091abf6..edf1857eb7298 100644 --- a/ext/standard/file.c +++ b/ext/standard/file.c @@ -131,10 +131,6 @@ static void file_globals_ctor(php_file_globals *file_globals_p) file_globals_p->def_chunk_size = PHP_SOCK_CHUNK_SIZE; } -static void file_globals_dtor(php_file_globals *file_globals_p) -{ -} - static PHP_INI_MH(OnUpdateAutoDetectLineEndings) { if (zend_ini_parse_bool(new_value)) { @@ -155,7 +151,7 @@ PHP_MINIT_FUNCTION(file) le_stream_context = zend_register_list_destructors_ex(file_context_dtor, NULL, "stream-context", module_number); #ifdef ZTS - ts_allocate_id(&file_globals_id, sizeof(php_file_globals), (ts_allocate_ctor) file_globals_ctor, (ts_allocate_dtor) file_globals_dtor); + ts_allocate_id(&file_globals_id, sizeof(php_file_globals), (ts_allocate_ctor) file_globals_ctor, (ts_allocate_dtor) NULL); #else file_globals_ctor(&file_globals); #endif @@ -168,15 +164,6 @@ PHP_MINIT_FUNCTION(file) } /* }}} */ -PHP_MSHUTDOWN_FUNCTION(file) /* {{{ */ -{ -#ifndef ZTS - file_globals_dtor(&file_globals); -#endif - return SUCCESS; -} -/* }}} */ - PHPAPI void php_flock_common(php_stream *stream, zend_long operation, uint32_t operation_arg_num, zval *wouldblock, zval *return_value) { diff --git a/ext/standard/file.h b/ext/standard/file.h index bc585eb6c659c..2f08f958b7ec3 100644 --- a/ext/standard/file.h +++ b/ext/standard/file.h @@ -20,7 +20,6 @@ #include "php_network.h" PHP_MINIT_FUNCTION(file); -PHP_MSHUTDOWN_FUNCTION(file); PHPAPI PHP_FUNCTION(fclose); PHPAPI PHP_FUNCTION(feof); From 1d4d3809f941583b50cac2d2d347e05c675a2a1a Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Sat, 24 Aug 2024 14:57:42 -0300 Subject: [PATCH 17/19] Actually use the returned value It seems on Unix, the behaviour is to convert integers into IP addresses, whereas Windows tries to resolve it. Regardless, it's silly to test what the type is, since it returns the unmodified hostname if it can't be resolved, so it'll always be a string, error or not. --- ext/standard/tests/network/gethostbyname_error002.phpt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/standard/tests/network/gethostbyname_error002.phpt b/ext/standard/tests/network/gethostbyname_error002.phpt index 8f586d02ba8b2..cd4d25625f7fc 100644 --- a/ext/standard/tests/network/gethostbyname_error002.phpt +++ b/ext/standard/tests/network/gethostbyname_error002.phpt @@ -4,7 +4,7 @@ gethostbyname() function - basic type return error test "Sylvain R." --FILE-- --EXPECT-- -bool(true) +string(12) "73.150.2.210" From acaa2ec3305e47b66fe8d918fdd8572dbf21ddbe Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Fri, 27 Mar 2026 12:06:26 -0300 Subject: [PATCH 18/19] Correct wrong var use (rebase casualty) --- main/network.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/main/network.c b/main/network.c index 5badbac7e5c34..cc92672d58472 100644 --- a/main/network.c +++ b/main/network.c @@ -193,11 +193,11 @@ PHPAPI size_t php_network_getaddresses_ex(const char *host, int socktype, int fa if ((ret = getaddrinfo(host, NULL, &hints, &res))) { # if defined(PHP_WIN32) - char *gai_error = php_win32_error_to_msg(n); + char *gai_error = php_win32_error_to_msg(ret); # elif defined(HAVE_GAI_STRERROR) - const char *gai_error = gai_strerror(n); + const char *gai_error = gai_strerror(ret); # else - const char *gai_error = php_gai_strerror(n) + const char *gai_error = php_gai_strerror(ret) # endif if (error_string) { /* free error string received during previous iteration (if any) */ From 47a1d970669337d292a7d3d506455341888cd29b Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Fri, 27 Mar 2026 12:29:40 -0300 Subject: [PATCH 19/19] Fix another type typo from rebase For some reason clang didn't warn me about this locally... --- ext/sockets/sockaddr_conv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/sockets/sockaddr_conv.c b/ext/sockets/sockaddr_conv.c index 773570a0ee62c..7ce3cd3278b45 100644 --- a/ext/sockets/sockaddr_conv.c +++ b/ext/sockets/sockaddr_conv.c @@ -67,7 +67,7 @@ bool php_set_common_addr(struct sockaddr *sin, int family, zend_string *string, #ifdef HAVE_IPV6 /* Sets addr by hostname, or by ip in string form (AF_INET6) */ -bool php_set_inet6_addr(struct sockaddr_in6 *sin6, char *string, php_socket *php_sock) /* {{{ */ +bool php_set_inet6_addr(struct sockaddr_in6 *sin6, zend_string *string, php_socket *php_sock) /* {{{ */ { char *scope = strchr(ZSTR_VAL(string), '%');