From: Wez Furlong Date: Thu, 19 Jun 2003 22:11:39 +0000 (+0000) Subject: Tidy up getaddrinfo() code so that it can handle broken ipv6 stacks and only X-Git-Tag: php-4.3.3RC2~270 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=b1f59b47036893e5d53a12569cc2e2fb5de535b3;p=php Tidy up getaddrinfo() code so that it can handle broken ipv6 stacks and only returns TCP addresses for TCP and UDP addresses for UDP requests respectively. Also, when a connect call "returns" EINPROGRESS and the full timeout limit has not yet been reached, retry the select with the remaining time interval. This is a potential fix for Bug #21653. # Note: atm, the fix is Linux specific; will do some more work to make this # portable at the weekend. --- diff --git a/main/network.c b/main/network.c index bad3cd6a43..fc568cae50 100644 --- a/main/network.c +++ b/main/network.c @@ -173,84 +173,92 @@ static void php_network_freeaddresses(struct sockaddr **sal) /* {{{ php_network_getaddresses * Returns number of addresses, 0 for none/error */ -static int php_network_getaddresses(const char *host, struct sockaddr ***sal TSRMLS_DC) +static int php_network_getaddresses(const char *host, int socktype, struct sockaddr ***sal TSRMLS_DC) { struct sockaddr **sap; int n; +#ifdef HAVE_GETADDRINFO + static int ipv6_borked = -1; /* the way this is used *is* thread safe */ + struct addrinfo hints, *res, *sai; +#else + struct hostent *host_info; + struct in_addr in; +#endif if (host == NULL) { return 0; } - { #ifdef HAVE_GETADDRINFO - struct addrinfo hints, *res, *sai; - - memset(&hints, '\0', sizeof(hints)); -# ifdef HAVE_IPV6 - hints.ai_family = AF_UNSPEC; -# else - hints.ai_family = AF_INET; -# endif - if ((n = getaddrinfo(host, NULL, &hints, &res))) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_network_getaddresses: getaddrinfo failed: %s" -# ifdef HAVE_IPV6 - " (is your IPV6 configuration correct? If this error happens all the time, try reconfiguring PHP using --disable-ipv6 option to configure)" -#endif - , PHP_GAI_STRERROR(n)); - return 0; - } else if (res == NULL) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_network_getaddresses: getaddrinfo failed (null result pointer)"); - return 0; + memset(&hints, '\0', sizeof(hints)); + + hints.ai_family = AF_INET; /* default to regular inet (see below) */ + hints.ai_socktype = socktype; + +# ifdef HAVE_IPV6 + /* probe for a working IPv6 stack; even if detected as having v6 at compile + * time, at runtime some stacks are slow to resolve or have other issues + * if they are not correctly configured. + * static variable use is safe here since simple store or fetch operations + * are atomic and because the actual probe process is not in danger of + * collisions or race conditions. */ + if (ipv6_borked == -1) { + int s; + + s = socket(PF_INET6, SOCK_DGRAM, 0); + if (s == SOCK_ERR) { + ipv6_borked = 1; + } else { + ipv6_borked = 0; + closesocket(s); } + } + hints.ai_family = ipv6_borked ? AF_INET : AF_UNSPEC; +# endif + + if ((n = getaddrinfo(host, NULL, &hints, &res))) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_network_getaddresses: getaddrinfo failed: %s", PHP_GAI_STRERROR(n)); + return 0; + } else if (res == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_network_getaddresses: getaddrinfo failed (null result pointer)"); + return 0; + } - sai = res; - for (n = 1; (sai = sai->ai_next) != NULL; n++); - *sal = safe_emalloc((n + 1), sizeof(*sal), 0); - sai = res; - sap = *sal; - do { - switch (sai->ai_family) { -# ifdef HAVE_IPV6 - case AF_INET6: - *sap = emalloc(sizeof(struct sockaddr_in6)); - *(struct sockaddr_in6 *)*sap = - *((struct sockaddr_in6 *)sai->ai_addr); - sap++; - break; -# endif - case AF_INET: - *sap = emalloc(sizeof(struct sockaddr_in)); - *(struct sockaddr_in *)*sap = - *((struct sockaddr_in *)sai->ai_addr); - sap++; - break; - } - } while ((sai = sai->ai_next) != NULL); - freeaddrinfo(res); + sai = res; + for (n = 1; (sai = sai->ai_next) != NULL; n++) + ; + + *sal = safe_emalloc((n + 1), sizeof(*sal), 0); + sai = res; + sap = *sal; + + do { + *sap = emalloc(sai->ai_addrlen); + memcpy(*sap, sai->ai_addr, sai->ai_addrlen); + sap++; + } while ((sai = sai->ai_next) != NULL); + + freeaddrinfo(res); #else - struct hostent *host_info; - struct in_addr in; - - if (!inet_aton(host, &in)) { - /* XXX NOT THREAD SAFE */ - host_info = gethostbyname(host); - if (host_info == NULL) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_network_getaddresses: gethostbyname failed"); - return 0; - } - in = *((struct in_addr *) host_info->h_addr); + if (!inet_aton(host, &in)) { + /* XXX NOT THREAD SAFE (is safe under win32) */ + host_info = gethostbyname(host); + if (host_info == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_network_getaddresses: gethostbyname failed"); + return 0; } + in = *((struct in_addr *) host_info->h_addr); + } - *sal = emalloc(2 * sizeof(*sal)); - sap = *sal; - *sap = emalloc(sizeof(struct sockaddr_in)); - (*sap)->sa_family = AF_INET; - ((struct sockaddr_in *)*sap)->sin_addr = in; - sap++; - n = 1; + *sal = emalloc(2 * sizeof(*sal)); + sap = *sal; + *sap = emalloc(sizeof(struct sockaddr_in)); + (*sap)->sa_family = AF_INET; + ((struct sockaddr_in *)*sap)->sin_addr = in; + sap++; + n = 1; #endif - } + *sap = NULL; return n; } @@ -297,6 +305,10 @@ PHPAPI int php_connect_nonb(int sockfd, goto ok; } +#ifdef __linux__ +retry_again: +#endif + FD_ZERO(&rset); FD_ZERO(&eset); FD_SET(sockfd, &rset); @@ -306,9 +318,7 @@ PHPAPI int php_connect_nonb(int sockfd, if ((n = select(sockfd + 1, &rset, &wset, &eset, timeout)) == 0) { error = ETIMEDOUT; - } - - if(FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) { + } else if ((FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset))) { len = sizeof(error); /* BSD-derived systems set errno correctly @@ -320,12 +330,24 @@ PHPAPI int php_connect_nonb(int sockfd, } else { /* whoops: sockfd has disappeared */ ret = -1; + error = errno; } +#ifdef __linux__ + /* this is a linux specific hack that only works since linux updates + * the timeout struct to reflect the time remaining from the original + * timeout value. One day, we should record the start time and calculate + * the remaining time ourselves for portability */ + if (ret == -1 && error == EINPROGRESS) { + error = 0; + goto retry_again; + } +#endif + ok: fcntl(sockfd, F_SETFL, flags); - if(error) { + if (error) { errno = error; ret = -1; } @@ -416,7 +438,7 @@ int php_hostconnect(const char *host, unsigned short port, int socktype, struct int set_timeout = 0; int err = 0; - n = php_network_getaddresses(host, &sal TSRMLS_CC); + n = php_network_getaddresses(host, socktype, &sal TSRMLS_CC); if (n == 0) return -1;