wez             Thu Jun 19 18:11:39 2003 EDT

  Modified files:              (Branch: PHP_4_3)
    /php4/main  network.c 
  Log:
  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.
  
  
  
Index: php4/main/network.c
diff -u php4/main/network.c:1.83.2.15 php4/main/network.c:1.83.2.16
--- php4/main/network.c:1.83.2.15       Fri May 23 23:15:53 2003
+++ php4/main/network.c Thu Jun 19 18:11:39 2003
@@ -16,7 +16,7 @@
    | Streams work by Wez Furlong <[EMAIL PROTECTED]>                   |
    +----------------------------------------------------------------------+
  */
-/* $Id: network.c,v 1.83.2.15 2003/05/24 03:15:53 wez Exp $ */
+/* $Id: network.c,v 1.83.2.16 2003/06/19 22:11:39 wez Exp $ */
 
 /*#define DEBUG_MAIN_NETWORK 1*/
 
@@ -173,84 +173,92 @@
 /* {{{ 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 @@
                goto ok;
        }
 
+#ifdef __linux__
+retry_again:
+#endif
+       
        FD_ZERO(&rset);
        FD_ZERO(&eset);
        FD_SET(sockfd, &rset);
@@ -306,9 +318,7 @@
 
        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 @@
        } 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 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;



-- 
PHP CVS Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to