Skip to content

getaddrinfo conversion #15555

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 34 additions & 40 deletions ext/sockets/sockaddr_conv.c
Original file line number Diff line number Diff line change
Expand Up @@ -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, char *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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it might be possible to factorise it further via macro to avoid this double assignation and the family conditions below, wdyt ?

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(string, '%');

if (inet_pton(AF_INET6, 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
Expand All @@ -43,21 +43,37 @@ int php_set_inet6_addr(struct sockaddr_in6 *sin6, char *string, php_socket *php_
#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) {
Expand Down Expand Up @@ -86,29 +102,7 @@ int php_set_inet6_addr(struct sockaddr_in6 *sin6, char *string, php_socket *php_
/* Sets addr by hostname, or by ip in string form (AF_INET) */
int php_set_inet_addr(struct sockaddr_in *sin, char *string, php_socket *php_sock) /* {{{ */
{
struct in_addr tmp;
struct hostent *host_entry;

if (inet_pton(AF_INET, string, &tmp)) {
sin->sin_addr.s_addr = tmp.s_addr;
} else {
if (strlen(string) > MAXFQDNLEN || ! (host_entry = php_network_gethostbyname(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);
}
/* }}} */

Expand Down
11 changes: 6 additions & 5 deletions ext/sockets/sockets.c
Original file line number Diff line number Diff line change
Expand Up @@ -220,18 +220,19 @@ ZEND_GET_MODULE(sockets)
static bool php_open_listen_sock(php_socket *sock, int port, int backlog) /* {{{ */
{
struct sockaddr_in la = {0};
struct hostent *hp;
php_sockaddr_storage resolved;

#ifndef PHP_WIN32
if ((hp = php_network_gethostbyname("0.0.0.0")) == NULL) {
const char *hostname = "0.0.0.0";
#else
if ((hp = php_network_gethostbyname("localhost")) == NULL) {
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((char *) &la.sin_addr, hp->h_addr, hp->h_length);
la.sin_family = hp->h_addrtype;
memcpy(&la, &resolved, sizeof(struct sockaddr_in));
la.sin_port = htons((unsigned short) port);

sock->bsd_socket = socket(PF_INET, SOCK_STREAM, 0);
Expand Down
85 changes: 38 additions & 47 deletions ext/standard/dns.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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)
Expand All @@ -232,11 +232,28 @@ PHP_FUNCTION(gethostbyname)
RETURN_STRINGL(hostname, hostname_len);
}

if (!(ipaddr = php_gethostbyname(hostname))) {
php_sockaddr_storage resolved;
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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated optimization, but if we would use Z_PARAM_PATH_STR to get a zend_string we could just return a copy here.

}

/*
* Future behaviour change: This function is documented as only returning IPv4
* addresses. We should change this to return IPv6 addresses as well.
*/
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);
}

if (ipaddr_zs == NULL) {
php_error_docref(NULL, E_WARNING, "Host name to ip failed %s", hostname);
RETURN_STRINGL(hostname, hostname_len);
} else {
RETURN_STR(ipaddr);
RETURN_STR(ipaddr_zs);
}
}
/* }}} */
Expand All @@ -246,9 +263,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)
Expand All @@ -261,61 +275,38 @@ PHP_FUNCTION(gethostbynamel)
RETURN_FALSE;
}

hp = php_network_gethostbyname(hostname);
if (!hp) {
struct sockaddr **addresses = NULL;
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;
}

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;
} else {
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);
}
/* }}} */

Expand Down
3 changes: 2 additions & 1 deletion ext/standard/tests/network/gethostbyname_error003.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ gethostbyname() function - basic type return error test
<?php
var_dump(is_string(gethostbyname("asdfasdf")));
?>
--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)
3 changes: 2 additions & 1 deletion ext/standard/tests/network/gethostbyname_error006.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ gethostbyname() function - basic invalid parameter test
<?php
var_dump(gethostbyname(".toto.toto.toto"));
?>
--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"
16 changes: 9 additions & 7 deletions main/fastcgi.c
Original file line number Diff line number Diff line change
Expand Up @@ -682,21 +682,23 @@ 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;
php_sockaddr_storage resolved;
int address_count;

if(strlen(host) > MAXFQDNLEN) {
hep = NULL;
if (strlen(host) > MAXFQDNLEN) {
address_count = 0;
} else {
hep = php_network_gethostbyname(host);
address_count = php_network_getaddress(&resolved, host, 0, AF_INET, 0, NULL);
}
if (!hep || hep->h_addrtype != AF_INET || !hep->h_addr_list[0]) {
if (address_count == 0 || resolved.ss_family != AF_INET) {
fcgi_log(FCGI_ERROR, "Cannot resolve host name '%s'!\n", host);
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);
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*)&resolved;
sa.sa_inet.sin_addr.s_addr = address4->sin_addr.s_addr;
}
}
} else {
Expand Down
43 changes: 40 additions & 3 deletions main/network.c
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,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;
Expand All @@ -168,6 +168,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
Expand All @@ -187,7 +188,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))) {
Expand Down Expand Up @@ -269,6 +270,42 @@ 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);
}
/* }}} */

/* {{{ 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);

php_network_freeaddresses(addresses);
return address_count;
}
/* }}} */

#ifndef O_NONBLOCK
#define O_NONBLOCK O_NDELAY
#endif
Expand Down
Loading
Loading