Back to index

php5  5.3.10
network.c
Go to the documentation of this file.
00001 /*
00002    +----------------------------------------------------------------------+
00003    | PHP Version 5                                                        |
00004    +----------------------------------------------------------------------+
00005    | Copyright (c) 1997-2012 The PHP Group                                |
00006    +----------------------------------------------------------------------+
00007    | This source file is subject to version 3.01 of the PHP license,      |
00008    | that is bundled with this package in the file LICENSE, and is        |
00009    | available through the world-wide-web at the following url:           |
00010    | http://www.php.net/license/3_01.txt                                  |
00011    | If you did not receive a copy of the PHP license and are unable to   |
00012    | obtain it through the world-wide-web, please send a note to          |
00013    | license@php.net so we can mail you a copy immediately.               |
00014    +----------------------------------------------------------------------+
00015    | Author: Stig Venaas <venaas@uninett.no>                              |
00016    | Streams work by Wez Furlong <wez@thebrainroom.com>                   |
00017    +----------------------------------------------------------------------+
00018  */
00019 
00020 /* $Id: network.c 321634 2012-01-01 13:15:04Z felipe $ */
00021 
00022 /*#define DEBUG_MAIN_NETWORK 1*/
00023 
00024 #include "php.h"
00025 
00026 #include <stddef.h>
00027 
00028 #ifdef PHP_WIN32
00029 # include "win32/inet.h"
00030 # define O_RDONLY _O_RDONLY
00031 # include "win32/param.h"
00032 #elif defined(NETWARE)
00033 #include <sys/timeval.h>
00034 #include <sys/param.h>
00035 #else
00036 #include <sys/param.h>
00037 #endif
00038 
00039 #include <sys/types.h>
00040 #if HAVE_SYS_SOCKET_H
00041 #include <sys/socket.h>
00042 #endif
00043 
00044 #ifndef _FCNTL_H
00045 #include <fcntl.h>
00046 #endif
00047 
00048 #ifdef HAVE_SYS_SELECT_H
00049 #include <sys/select.h>
00050 #endif
00051 #if HAVE_SYS_POLL_H
00052 #include <sys/poll.h>
00053 #endif
00054 
00055 #if defined(NETWARE)
00056 #ifdef USE_WINSOCK
00057 #include <novsock2.h>
00058 #else
00059 #include <arpa/inet.h>
00060 #include <netinet/in.h>
00061 #include <netdb.h>
00062 #include <sys/select.h>
00063 #include <sys/socket.h>
00064 #endif
00065 #elif !defined(PHP_WIN32)
00066 #include <netinet/in.h>
00067 #include <netdb.h>
00068 #if HAVE_ARPA_INET_H
00069 #include <arpa/inet.h>
00070 #endif
00071 #endif
00072 
00073 #ifndef HAVE_INET_ATON
00074 int inet_aton(const char *, struct in_addr *);
00075 #endif
00076 
00077 #include "php_network.h"
00078 
00079 #if defined(PHP_WIN32) || defined(__riscos__) || defined(NETWARE)
00080 #undef AF_UNIX
00081 #endif
00082 
00083 #if defined(AF_UNIX)
00084 #include <sys/un.h>
00085 #endif
00086 
00087 #include "ext/standard/file.h"
00088 
00089 #ifdef PHP_WIN32
00090 # include "win32/time.h"
00091 # define SOCK_ERR INVALID_SOCKET
00092 # define SOCK_CONN_ERR SOCKET_ERROR
00093 # define PHP_TIMEOUT_ERROR_VALUE          WSAETIMEDOUT
00094 
00095 #if HAVE_IPV6
00096 const struct in6_addr in6addr_any = {0}; /* IN6ADDR_ANY_INIT; */
00097 #endif
00098 
00099 #else
00100 # define SOCK_ERR -1
00101 # define SOCK_CONN_ERR -1
00102 # define PHP_TIMEOUT_ERROR_VALUE          ETIMEDOUT
00103 #endif
00104 
00105 #if HAVE_GETADDRINFO
00106 #ifdef HAVE_GAI_STRERROR
00107 #  define PHP_GAI_STRERROR(x) (gai_strerror(x))
00108 #else
00109 #  define PHP_GAI_STRERROR(x) (php_gai_strerror(x))
00110 /* {{{ php_gai_strerror
00111  */
00112 static const char *php_gai_strerror(int code)
00113 {
00114         static struct {
00115                 int code;
00116                 const char *msg;
00117         } values[] = {
00118 #  ifdef EAI_ADDRFAMILY
00119                 {EAI_ADDRFAMILY, "Address family for hostname not supported"},
00120 #  endif
00121                 {EAI_AGAIN, "Temporary failure in name resolution"},
00122                 {EAI_BADFLAGS, "Bad value for ai_flags"},
00123                 {EAI_FAIL, "Non-recoverable failure in name resolution"},
00124                 {EAI_FAMILY, "ai_family not supported"},
00125                 {EAI_MEMORY, "Memory allocation failure"},
00126 #  ifdef EAI_NODATA
00127                 {EAI_NODATA, "No address associated with hostname"},
00128 #  endif
00129                 {EAI_NONAME, "Name or service not known"},
00130                 {EAI_SERVICE, "Servname not supported for ai_socktype"},
00131                 {EAI_SOCKTYPE, "ai_socktype not supported"},
00132                 {EAI_SYSTEM, "System error"},
00133                 {0, NULL}
00134         };
00135         int i;
00136 
00137         for (i = 0; values[i].msg != NULL; i++) {
00138                 if (values[i].code == code) {
00139                         return (char *)values[i].msg;
00140                 }
00141         }
00142 
00143         return "Unknown error";
00144 }
00145 /* }}} */
00146 #endif
00147 #endif
00148 
00149 /* {{{ php_network_freeaddresses
00150  */
00151 static void php_network_freeaddresses(struct sockaddr **sal)
00152 {
00153        struct sockaddr **sap;
00154 
00155        if (sal == NULL)
00156               return;
00157        for (sap = sal; *sap != NULL; sap++)
00158               efree(*sap);
00159        efree(sal);
00160 }
00161 /* }}} */
00162 
00163 /* {{{ php_network_getaddresses
00164  * Returns number of addresses, 0 for none/error
00165  */
00166 static int php_network_getaddresses(const char *host, int socktype, struct sockaddr ***sal, char **error_string TSRMLS_DC)
00167 {
00168        struct sockaddr **sap;
00169        int n;
00170 #if HAVE_GETADDRINFO
00171 # if HAVE_IPV6
00172        static int ipv6_borked = -1; /* the way this is used *is* thread safe */
00173 # endif
00174        struct addrinfo hints, *res, *sai;
00175 #else
00176        struct hostent *host_info;
00177        struct in_addr in;
00178 #endif
00179 
00180        if (host == NULL) {
00181               return 0;
00182        }
00183 #if HAVE_GETADDRINFO
00184        memset(&hints, '\0', sizeof(hints));
00185 
00186        hints.ai_family = AF_INET; /* default to regular inet (see below) */
00187        hints.ai_socktype = socktype;
00188 
00189 # if HAVE_IPV6
00190        /* probe for a working IPv6 stack; even if detected as having v6 at compile
00191         * time, at runtime some stacks are slow to resolve or have other issues
00192         * if they are not correctly configured.
00193         * static variable use is safe here since simple store or fetch operations
00194         * are atomic and because the actual probe process is not in danger of
00195         * collisions or race conditions. */
00196        if (ipv6_borked == -1) {
00197               int s;
00198 
00199               s = socket(PF_INET6, SOCK_DGRAM, 0);
00200               if (s == SOCK_ERR) {
00201                      ipv6_borked = 1;
00202               } else {
00203                      ipv6_borked = 0;
00204                      closesocket(s);
00205               }
00206        }
00207        hints.ai_family = ipv6_borked ? AF_INET : AF_UNSPEC;
00208 # endif
00209 
00210        if ((n = getaddrinfo(host, NULL, &hints, &res))) {
00211               if (error_string) {
00212                      spprintf(error_string, 0, "php_network_getaddresses: getaddrinfo failed: %s", PHP_GAI_STRERROR(n));
00213                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", *error_string);
00214               } else {
00215                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_network_getaddresses: getaddrinfo failed: %s", PHP_GAI_STRERROR(n));
00216               }
00217               return 0;
00218        } else if (res == NULL) {
00219               if (error_string) {
00220                      spprintf(error_string, 0, "php_network_getaddresses: getaddrinfo failed (null result pointer) errno=%d", errno);
00221                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", *error_string);
00222               } else {
00223                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_network_getaddresses: getaddrinfo failed (null result pointer)");
00224               }
00225               return 0;
00226        }
00227 
00228        sai = res;
00229        for (n = 1; (sai = sai->ai_next) != NULL; n++)
00230               ;
00231 
00232        *sal = safe_emalloc((n + 1), sizeof(*sal), 0);
00233        sai = res;
00234        sap = *sal;
00235 
00236        do {
00237               *sap = emalloc(sai->ai_addrlen);
00238               memcpy(*sap, sai->ai_addr, sai->ai_addrlen);
00239               sap++;
00240        } while ((sai = sai->ai_next) != NULL);
00241 
00242        freeaddrinfo(res);
00243 #else
00244        if (!inet_aton(host, &in)) {
00245               /* XXX NOT THREAD SAFE (is safe under win32) */
00246               host_info = gethostbyname(host);
00247               if (host_info == NULL) {
00248                      if (error_string) {
00249                             spprintf(error_string, 0, "php_network_getaddresses: gethostbyname failed. errno=%d", errno);
00250                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", *error_string);
00251                      } else {
00252                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_network_getaddresses: gethostbyname failed");
00253                      }
00254                      return 0;
00255               }
00256               in = *((struct in_addr *) host_info->h_addr);
00257        }
00258 
00259        *sal = safe_emalloc(2, sizeof(*sal), 0);
00260        sap = *sal;
00261        *sap = emalloc(sizeof(struct sockaddr_in));
00262        (*sap)->sa_family = AF_INET;
00263        ((struct sockaddr_in *)*sap)->sin_addr = in;
00264        sap++;
00265        n = 1;
00266 #endif
00267 
00268        *sap = NULL;
00269        return n;
00270 }
00271 /* }}} */
00272 
00273 #ifndef O_NONBLOCK
00274 #define O_NONBLOCK O_NDELAY
00275 #endif
00276 
00277 #if !defined(__BEOS__)
00278 # define HAVE_NON_BLOCKING_CONNECT 1
00279 # ifdef PHP_WIN32
00280 typedef u_long php_non_blocking_flags_t;
00281 #  define SET_SOCKET_BLOCKING_MODE(sock, save) \
00282      save = TRUE; ioctlsocket(sock, FIONBIO, &save)
00283 #  define RESTORE_SOCKET_BLOCKING_MODE(sock, save) \
00284         ioctlsocket(sock, FIONBIO, &save)
00285 # else
00286 typedef int php_non_blocking_flags_t;
00287 #  define SET_SOCKET_BLOCKING_MODE(sock, save) \
00288         save = fcntl(sock, F_GETFL, 0); \
00289         fcntl(sock, F_SETFL, save | O_NONBLOCK)
00290 #  define RESTORE_SOCKET_BLOCKING_MODE(sock, save) \
00291         fcntl(sock, F_SETFL, save)
00292 # endif
00293 #endif
00294 
00295 /* Connect to a socket using an interruptible connect with optional timeout.
00296  * Optionally, the connect can be made asynchronously, which will implicitly
00297  * enable non-blocking mode on the socket.
00298  * */
00299 /* {{{ php_network_connect_socket */
00300 PHPAPI int php_network_connect_socket(php_socket_t sockfd,
00301               const struct sockaddr *addr,
00302               socklen_t addrlen,
00303               int asynchronous,
00304               struct timeval *timeout,
00305               char **error_string,
00306               int *error_code)
00307 {
00308 #if HAVE_NON_BLOCKING_CONNECT
00309        php_non_blocking_flags_t orig_flags;
00310        int n;
00311        int error = 0;
00312        socklen_t len;
00313        int ret = 0;
00314 
00315        SET_SOCKET_BLOCKING_MODE(sockfd, orig_flags);
00316 
00317        if ((n = connect(sockfd, addr, addrlen)) != 0) {
00318               error = php_socket_errno();
00319 
00320               if (error_code) {
00321                      *error_code = error;
00322               }
00323 
00324               if (error != EINPROGRESS) {
00325                      if (error_string) {
00326                             *error_string = php_socket_strerror(error, NULL, 0);
00327                      }
00328 
00329                      return -1;
00330               }
00331               if (asynchronous && error == EINPROGRESS) {
00332                      /* this is fine by us */
00333                      return 0;
00334               }
00335        }
00336 
00337        if (n == 0) {
00338               goto ok;
00339        }
00340 # ifdef PHP_WIN32
00341        /* The documentation for connect() says in case of non-blocking connections
00342         * the select function reports success in the writefds set and failure in
00343         * the exceptfds set. Indeed, using PHP_POLLREADABLE results in select
00344         * failing only due to the timeout and not immediately as would be
00345         * expected when a connection is actively refused. This way,
00346         * php_pollfd_for will return a mask with POLLOUT if the connection
00347         * is successful and with POLLPRI otherwise. */
00348        if ((n = php_pollfd_for(sockfd, POLLOUT|POLLPRI, timeout)) == 0) {
00349 #else
00350        if ((n = php_pollfd_for(sockfd, PHP_POLLREADABLE|POLLOUT, timeout)) == 0) {
00351 #endif
00352               error = PHP_TIMEOUT_ERROR_VALUE;
00353        }
00354 
00355        if (n > 0) {
00356               len = sizeof(error);
00357               /*
00358                  BSD-derived systems set errno correctly
00359                  Solaris returns -1 from getsockopt in case of error
00360                  */
00361               if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (char*)&error, &len) != 0) {
00362                      ret = -1;
00363               }
00364        } else {
00365               /* whoops: sockfd has disappeared */
00366               ret = -1;
00367        }
00368 
00369 ok:
00370        if (!asynchronous) {
00371               /* back to blocking mode */
00372               RESTORE_SOCKET_BLOCKING_MODE(sockfd, orig_flags);
00373        }
00374 
00375        if (error_code) {
00376               *error_code = error;
00377        }
00378 
00379        if (error) {
00380               ret = -1;
00381               if (error_string) {
00382                      *error_string = php_socket_strerror(error, NULL, 0);
00383               }
00384        }
00385        return ret;
00386 #else
00387        if (asynchronous) {
00388               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Asynchronous connect() not supported on this platform");
00389        }
00390        return (connect(sockfd, addr, addrlen) == 0) ? 0 : -1;
00391 #endif
00392 }
00393 /* }}} */
00394 
00395 /* {{{ sub_times */
00396 static inline void sub_times(struct timeval a, struct timeval b, struct timeval *result)
00397 {
00398        result->tv_usec = a.tv_usec - b.tv_usec;
00399        if (result->tv_usec < 0L) {
00400               a.tv_sec--;
00401               result->tv_usec += 1000000L;
00402        }
00403        result->tv_sec = a.tv_sec - b.tv_sec;
00404        if (result->tv_sec < 0L) {
00405               result->tv_sec++;
00406               result->tv_usec -= 1000000L;
00407        }
00408 }
00409 /* }}} */
00410 
00411 /* Bind to a local IP address.
00412  * Returns the bound socket, or -1 on failure.
00413  * */
00414 /* {{{ php_network_bind_socket_to_local_addr */
00415 php_socket_t php_network_bind_socket_to_local_addr(const char *host, unsigned port,
00416               int socktype, char **error_string, int *error_code
00417               TSRMLS_DC)
00418 {
00419        int num_addrs, n, err = 0;
00420        php_socket_t sock;
00421        struct sockaddr **sal, **psal, *sa;
00422        socklen_t socklen;
00423 
00424        num_addrs = php_network_getaddresses(host, socktype, &psal, error_string TSRMLS_CC);
00425 
00426        if (num_addrs == 0) {
00427               /* could not resolve address(es) */
00428               return -1;
00429        }
00430 
00431        for (sal = psal; *sal != NULL; sal++) {
00432               sa = *sal;
00433 
00434               /* create a socket for this address */
00435               sock = socket(sa->sa_family, socktype, 0);
00436 
00437               if (sock == SOCK_ERR) {
00438                      continue;
00439               }
00440 
00441               switch (sa->sa_family) {
00442 #if HAVE_GETADDRINFO && HAVE_IPV6
00443                      case AF_INET6:
00444                             ((struct sockaddr_in6 *)sa)->sin6_family = sa->sa_family;
00445                             ((struct sockaddr_in6 *)sa)->sin6_port = htons(port);
00446                             socklen = sizeof(struct sockaddr_in6);
00447                             break;
00448 #endif
00449                      case AF_INET:
00450                             ((struct sockaddr_in *)sa)->sin_family = sa->sa_family;
00451                             ((struct sockaddr_in *)sa)->sin_port = htons(port);
00452                             socklen = sizeof(struct sockaddr_in);
00453                             break;
00454                      default:
00455                             /* Unknown family */
00456                             socklen = 0;
00457                             sa = NULL;
00458               }
00459 
00460               if (sa) {
00461                      /* attempt to bind */
00462 
00463 #ifdef SO_REUSEADDR
00464                      {
00465                             int val = 1;
00466                             setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&val, sizeof(val));
00467                      }
00468 #endif
00469 
00470                      n = bind(sock, sa, socklen);
00471 
00472                      if (n != SOCK_CONN_ERR) {
00473                             goto bound;
00474                      }
00475 
00476                      err = php_socket_errno();
00477               }
00478 
00479               closesocket(sock);
00480        }
00481        sock = -1;
00482 
00483        if (error_code) {
00484               *error_code = err;
00485        }
00486        if (error_string) {
00487               *error_string = php_socket_strerror(err, NULL, 0);
00488        }
00489 
00490 bound:
00491 
00492        php_network_freeaddresses(psal);
00493 
00494        return sock;
00495 
00496 }
00497 /* }}} */
00498 
00499 PHPAPI int php_network_parse_network_address_with_port(const char *addr, long addrlen, struct sockaddr *sa, socklen_t *sl TSRMLS_DC)
00500 {
00501        char *colon;
00502        char *tmp;
00503        int ret = FAILURE;
00504        short port;
00505        struct sockaddr_in *in4 = (struct sockaddr_in*)sa;
00506        struct sockaddr **psal;
00507        int n;
00508        char *errstr = NULL;
00509 #if HAVE_IPV6
00510        struct sockaddr_in6 *in6 = (struct sockaddr_in6*)sa;
00511 #endif
00512 
00513        if (*addr == '[') {
00514               colon = memchr(addr + 1, ']', addrlen-1);
00515               if (!colon || colon[1] != ':') {
00516                      return FAILURE;
00517               }
00518               port = atoi(colon + 2);
00519               addr++;
00520        } else {
00521               colon = memchr(addr, ':', addrlen);
00522               if (!colon) {
00523                      return FAILURE;
00524               }
00525               port = atoi(colon + 1);
00526        }
00527 
00528        tmp = estrndup(addr, colon - addr);
00529 
00530        /* first, try interpreting the address as a numeric address */
00531 
00532 #if HAVE_IPV6 && HAVE_INET_PTON
00533        if (inet_pton(AF_INET6, tmp, &in6->sin6_addr) > 0) {
00534               in6->sin6_port = htons(port);
00535               in6->sin6_family = AF_INET6;
00536               *sl = sizeof(struct sockaddr_in6);
00537               ret = SUCCESS;
00538               goto out;
00539        }
00540 #endif
00541        if (inet_aton(tmp, &in4->sin_addr) > 0) {
00542               in4->sin_port = htons(port);
00543               in4->sin_family = AF_INET;
00544               *sl = sizeof(struct sockaddr_in);
00545               ret = SUCCESS;
00546               goto out;
00547        }
00548 
00549        /* looks like we'll need to resolve it */
00550        n = php_network_getaddresses(tmp, SOCK_DGRAM, &psal, &errstr TSRMLS_CC);
00551 
00552        if (n == 0) {
00553               if (errstr) {
00554                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to resolve `%s': %s", tmp, errstr);
00555                      STR_FREE(errstr);
00556               }
00557               goto out;
00558        }
00559 
00560        /* copy the details from the first item */
00561        switch ((*psal)->sa_family) {
00562 #if HAVE_GETADDRINFO && HAVE_IPV6
00563               case AF_INET6:
00564                      *in6 = **(struct sockaddr_in6**)psal;
00565                      in6->sin6_port = htons(port);
00566                      *sl = sizeof(struct sockaddr_in6);
00567                      ret = SUCCESS;
00568                      break;
00569 #endif
00570               case AF_INET:
00571                      *in4 = **(struct sockaddr_in**)psal;
00572                      in4->sin_port = htons(port);
00573                      *sl = sizeof(struct sockaddr_in);
00574                      ret = SUCCESS;
00575                      break;
00576        }
00577 
00578        php_network_freeaddresses(psal);
00579 
00580 out:
00581        STR_FREE(tmp);
00582        return ret;
00583 }
00584 
00585 
00586 PHPAPI void php_network_populate_name_from_sockaddr(
00587               /* input address */
00588               struct sockaddr *sa, socklen_t sl,
00589               /* output readable address */
00590               char **textaddr, long *textaddrlen,
00591               /* output address */
00592               struct sockaddr **addr,
00593               socklen_t *addrlen
00594               TSRMLS_DC)
00595 {
00596        if (addr) {
00597               *addr = emalloc(sl);
00598               memcpy(*addr, sa, sl);
00599               *addrlen = sl;
00600        }
00601 
00602        if (textaddr) {
00603 #if HAVE_IPV6 && HAVE_INET_NTOP
00604               char abuf[256];
00605 #endif
00606               char *buf = NULL;
00607 
00608               switch (sa->sa_family) {
00609                      case AF_INET:
00610                             /* generally not thread safe, but it *is* thread safe under win32 */
00611                             buf = inet_ntoa(((struct sockaddr_in*)sa)->sin_addr);
00612                             if (buf) {
00613                                    *textaddrlen = spprintf(textaddr, 0, "%s:%d",
00614                                           buf, ntohs(((struct sockaddr_in*)sa)->sin_port));
00615                             }
00616 
00617                             break;
00618 
00619 #if HAVE_IPV6 && HAVE_INET_NTOP
00620                      case AF_INET6:
00621                             buf = (char*)inet_ntop(sa->sa_family, &((struct sockaddr_in6*)sa)->sin6_addr, (char *)&abuf, sizeof(abuf));
00622                             if (buf) {
00623                                    *textaddrlen = spprintf(textaddr, 0, "%s:%d",
00624                                           buf, ntohs(((struct sockaddr_in6*)sa)->sin6_port));
00625                             }
00626 
00627                             break;
00628 #endif
00629 #ifdef AF_UNIX
00630                      case AF_UNIX:
00631                             {
00632                                    struct sockaddr_un *ua = (struct sockaddr_un*)sa;
00633 
00634                                    if (ua->sun_path[0] == '\0') {
00635                                           /* abstract name */
00636                                           int len = strlen(ua->sun_path + 1) + 1;
00637                                           *textaddrlen = len;
00638                                           *textaddr = emalloc(len + 1);
00639                                           memcpy(*textaddr, ua->sun_path, len);
00640                                           (*textaddr)[len] = '\0';
00641                                    } else {
00642                                           *textaddrlen = strlen(ua->sun_path);
00643                                           *textaddr = estrndup(ua->sun_path, *textaddrlen);
00644                                    }
00645                             }
00646                             break;
00647 #endif
00648 
00649               }
00650 
00651        }
00652 }
00653 
00654 PHPAPI int php_network_get_peer_name(php_socket_t sock,
00655               char **textaddr, long *textaddrlen,
00656               struct sockaddr **addr,
00657               socklen_t *addrlen
00658               TSRMLS_DC)
00659 {
00660        php_sockaddr_storage sa;
00661        socklen_t sl = sizeof(sa);
00662        memset(&sa, 0, sizeof(sa));
00663 
00664        if (getpeername(sock, (struct sockaddr*)&sa, &sl) == 0) {
00665               php_network_populate_name_from_sockaddr((struct sockaddr*)&sa, sl,
00666                             textaddr, textaddrlen,
00667                             addr, addrlen
00668                             TSRMLS_CC);
00669               return 0;
00670        }
00671        return -1;
00672 }
00673 
00674 PHPAPI int php_network_get_sock_name(php_socket_t sock,
00675               char **textaddr, long *textaddrlen,
00676               struct sockaddr **addr,
00677               socklen_t *addrlen
00678               TSRMLS_DC)
00679 {
00680        php_sockaddr_storage sa;
00681        socklen_t sl = sizeof(sa);
00682        memset(&sa, 0, sizeof(sa));
00683 
00684        if (getsockname(sock, (struct sockaddr*)&sa, &sl) == 0) {
00685               php_network_populate_name_from_sockaddr((struct sockaddr*)&sa, sl,
00686                             textaddr, textaddrlen,
00687                             addr, addrlen
00688                             TSRMLS_CC);
00689               return 0;
00690        }
00691        return -1;
00692 
00693 }
00694 
00695 
00696 /* Accept a client connection from a server socket,
00697  * using an optional timeout.
00698  * Returns the peer address in addr/addrlen (it will emalloc
00699  * these, so be sure to efree the result).
00700  * If you specify textaddr/textaddrlen, a text-printable
00701  * version of the address will be emalloc'd and returned.
00702  * */
00703 
00704 /* {{{ php_network_accept_incoming */
00705 PHPAPI php_socket_t php_network_accept_incoming(php_socket_t srvsock,
00706               char **textaddr, long *textaddrlen,
00707               struct sockaddr **addr,
00708               socklen_t *addrlen,
00709               struct timeval *timeout,
00710               char **error_string,
00711               int *error_code
00712               TSRMLS_DC)
00713 {
00714        php_socket_t clisock = -1;
00715        int error = 0, n;
00716        php_sockaddr_storage sa;
00717        socklen_t sl;
00718 
00719        n = php_pollfd_for(srvsock, PHP_POLLREADABLE, timeout);
00720 
00721        if (n == 0) {
00722               error = PHP_TIMEOUT_ERROR_VALUE;
00723        } else if (n == -1) {
00724               error = php_socket_errno();
00725        } else {
00726               sl = sizeof(sa);
00727 
00728               clisock = accept(srvsock, (struct sockaddr*)&sa, &sl);
00729 
00730               if (clisock != SOCK_ERR) {
00731                      php_network_populate_name_from_sockaddr((struct sockaddr*)&sa, sl,
00732                                    textaddr, textaddrlen,
00733                                    addr, addrlen
00734                                    TSRMLS_CC);
00735               } else {
00736                      error = php_socket_errno();
00737               }
00738        }
00739 
00740        if (error_code) {
00741               *error_code = error;
00742        }
00743        if (error_string) {
00744               *error_string = php_socket_strerror(error, NULL, 0);
00745        }
00746 
00747        return clisock;
00748 }
00749 /* }}} */
00750 
00751 
00752 
00753 /* Connect to a remote host using an interruptible connect with optional timeout.
00754  * Optionally, the connect can be made asynchronously, which will implicitly
00755  * enable non-blocking mode on the socket.
00756  * Returns the connected (or connecting) socket, or -1 on failure.
00757  * */
00758 
00759 /* {{{ php_network_connect_socket_to_host */
00760 php_socket_t php_network_connect_socket_to_host(const char *host, unsigned short port,
00761               int socktype, int asynchronous, struct timeval *timeout, char **error_string,
00762               int *error_code, char *bindto, unsigned short bindport
00763               TSRMLS_DC)
00764 {
00765        int num_addrs, n, fatal = 0;
00766        php_socket_t sock;
00767        struct sockaddr **sal, **psal, *sa;
00768        struct timeval working_timeout;
00769        socklen_t socklen;
00770 #if HAVE_GETTIMEOFDAY
00771        struct timeval limit_time, time_now;
00772 #endif
00773 
00774        num_addrs = php_network_getaddresses(host, socktype, &psal, error_string TSRMLS_CC);
00775 
00776        if (num_addrs == 0) {
00777               /* could not resolve address(es) */
00778               return -1;
00779        }
00780 
00781        if (timeout) {
00782               memcpy(&working_timeout, timeout, sizeof(working_timeout));
00783 #if HAVE_GETTIMEOFDAY
00784               gettimeofday(&limit_time, NULL);
00785               limit_time.tv_sec += working_timeout.tv_sec;
00786               limit_time.tv_usec += working_timeout.tv_usec;
00787               if (limit_time.tv_usec >= 1000000) {
00788                      limit_time.tv_usec -= 1000000;
00789                      limit_time.tv_sec++;
00790               }
00791 #endif
00792        }
00793 
00794        for (sal = psal; !fatal && *sal != NULL; sal++) {
00795               sa = *sal;
00796 
00797               /* create a socket for this address */
00798               sock = socket(sa->sa_family, socktype, 0);
00799 
00800               if (sock == SOCK_ERR) {
00801                      continue;
00802               }
00803 
00804               switch (sa->sa_family) {
00805 #if HAVE_GETADDRINFO && HAVE_IPV6
00806                      case AF_INET6:
00807                             if (!bindto || strchr(bindto, ':')) {
00808                                    ((struct sockaddr_in6 *)sa)->sin6_family = sa->sa_family;
00809                                    ((struct sockaddr_in6 *)sa)->sin6_port = htons(port);
00810                                    socklen = sizeof(struct sockaddr_in6);
00811                             } else {
00812                                    socklen = 0;
00813                                    sa = NULL;
00814                             }
00815                             break;
00816 #endif
00817                      case AF_INET:
00818                             ((struct sockaddr_in *)sa)->sin_family = sa->sa_family;
00819                             ((struct sockaddr_in *)sa)->sin_port = htons(port);
00820                             socklen = sizeof(struct sockaddr_in);
00821                             break;
00822                      default:
00823                             /* Unknown family */
00824                             socklen = 0;
00825                             sa = NULL;
00826               }
00827 
00828               if (sa) {
00829                      /* make a connection attempt */
00830 
00831                      if (bindto) {
00832                             struct sockaddr *local_address = NULL;
00833                             int local_address_len = 0;
00834 
00835                             if (sa->sa_family == AF_INET) {
00836                                    struct sockaddr_in *in4 = emalloc(sizeof(struct sockaddr_in));
00837 
00838                                    local_address = (struct sockaddr*)in4;
00839                                    local_address_len = sizeof(struct sockaddr_in);
00840 
00841                                    in4->sin_family = sa->sa_family;
00842                                    in4->sin_port = htons(bindport);
00843                                    if (!inet_aton(bindto, &in4->sin_addr)) {
00844                                           php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid IP Address: %s", bindto);
00845                                           goto skip_bind;
00846                                    }
00847                                    memset(&(in4->sin_zero), 0, sizeof(in4->sin_zero));
00848                             }
00849 #if HAVE_IPV6 && HAVE_INET_PTON
00850                              else { /* IPV6 */
00851                                    struct sockaddr_in6 *in6 = emalloc(sizeof(struct sockaddr_in6));
00852 
00853                                    local_address = (struct sockaddr*)in6;
00854                                    local_address_len = sizeof(struct sockaddr_in6);
00855 
00856                                    in6->sin6_family = sa->sa_family;
00857                                    in6->sin6_port = htons(bindport);
00858                                    if (inet_pton(AF_INET6, bindto, &in6->sin6_addr) < 1) {
00859                                           php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid IP Address: %s", bindto);
00860                                           goto skip_bind;
00861                                    }
00862                             }
00863 #endif
00864                             if (!local_address || bind(sock, local_address, local_address_len)) {
00865                                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to bind to '%s:%d', system said: %s", bindto, bindport, strerror(errno));
00866                             }
00867 skip_bind:
00868                             if (local_address) {
00869                                    efree(local_address);
00870                             }
00871                      }
00872                      /* free error string recieved during previous iteration (if any) */
00873                      if (error_string && *error_string) {
00874                             efree(*error_string);
00875                             *error_string = NULL;
00876                      }
00877 
00878                      n = php_network_connect_socket(sock, sa, socklen, asynchronous,
00879                                    timeout ? &working_timeout : NULL,
00880                                    error_string, error_code);
00881 
00882                      if (n != -1) {
00883                             goto connected;
00884                      }
00885 
00886                      /* adjust timeout for next attempt */
00887 #if HAVE_GETTIMEOFDAY
00888                      if (timeout) {
00889                             gettimeofday(&time_now, NULL);
00890 
00891                             if (timercmp(&time_now, &limit_time, >=)) {
00892                                    /* time limit expired; don't attempt any further connections */
00893                                    fatal = 1;
00894                             } else {
00895                                    /* work out remaining time */
00896                                    sub_times(limit_time, time_now, &working_timeout);
00897                             }
00898                      }
00899 #else
00900                      if (error_code && *error_code == PHP_TIMEOUT_ERROR_VALUE) {
00901                             /* Don't even bother trying to connect to the next alternative;
00902                              * we have no way to determine how long we have already taken
00903                              * and it is quite likely that the next attempt will fail too. */
00904                             fatal = 1;
00905                      } else {
00906                             /* re-use the same initial timeout.
00907                              * Not the best thing, but in practice it should be good-enough */
00908                             if (timeout) {
00909                                    memcpy(&working_timeout, timeout, sizeof(working_timeout));
00910                             }
00911                      }
00912 #endif
00913               }
00914 
00915               closesocket(sock);
00916        }
00917        sock = -1;
00918 
00919 connected:
00920 
00921        php_network_freeaddresses(psal);
00922 
00923        return sock;
00924 }
00925 /* }}} */
00926 
00927 /* {{{ php_any_addr
00928  * Fills the any (wildcard) address into php_sockaddr_storage
00929  */
00930 PHPAPI void php_any_addr(int family, php_sockaddr_storage *addr, unsigned short port)
00931 {
00932        memset(addr, 0, sizeof(php_sockaddr_storage));
00933        switch (family) {
00934 #if HAVE_IPV6
00935        case AF_INET6: {
00936               struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) addr;
00937               sin6->sin6_family = AF_INET6;
00938               sin6->sin6_port = htons(port);
00939               sin6->sin6_addr = in6addr_any;
00940               break;
00941        }
00942 #endif
00943        case AF_INET: {
00944               struct sockaddr_in *sin = (struct sockaddr_in *) addr;
00945               sin->sin_family = AF_INET;
00946               sin->sin_port = htons(port);
00947               sin->sin_addr.s_addr = htonl(INADDR_ANY);
00948               break;
00949        }
00950        }
00951 }
00952 /* }}} */
00953 
00954 /* {{{ php_sockaddr_size
00955  * Returns the size of struct sockaddr_xx for the family
00956  */
00957 PHPAPI int php_sockaddr_size(php_sockaddr_storage *addr)
00958 {
00959        switch (((struct sockaddr *)addr)->sa_family) {
00960        case AF_INET:
00961               return sizeof(struct sockaddr_in);
00962 #if HAVE_IPV6
00963        case AF_INET6:
00964               return sizeof(struct sockaddr_in6);
00965 #endif
00966 #ifdef AF_UNIX
00967        case AF_UNIX:
00968               return sizeof(struct sockaddr_un);
00969 #endif
00970        default:
00971               return 0;
00972        }
00973 }
00974 /* }}} */
00975 
00976 /* Given a socket error code, if buf == NULL:
00977  *   emallocs storage for the error message and returns
00978  * else
00979  *   sprintf message into provided buffer and returns buf
00980  */
00981 /* {{{ php_socket_strerror */
00982 PHPAPI char *php_socket_strerror(long err, char *buf, size_t bufsize)
00983 {
00984 #ifndef PHP_WIN32
00985        char *errstr;
00986 
00987        errstr = strerror(err);
00988        if (buf == NULL) {
00989               buf = estrdup(errstr);
00990        } else {
00991               strncpy(buf, errstr, bufsize);
00992        }
00993        return buf;
00994 #else
00995        char *sysbuf;
00996        int free_it = 1;
00997 
00998        if (!FormatMessage(
00999                             FORMAT_MESSAGE_ALLOCATE_BUFFER |
01000                             FORMAT_MESSAGE_FROM_SYSTEM |
01001                             FORMAT_MESSAGE_IGNORE_INSERTS,
01002                             NULL,
01003                             err,
01004                             MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
01005                             (LPTSTR)&sysbuf,
01006                             0,
01007                             NULL)) {
01008               free_it = 0;
01009               sysbuf = "Unknown Error";
01010        }
01011 
01012        if (buf == NULL) {
01013               buf = estrdup(sysbuf);
01014        } else {
01015               strncpy(buf, sysbuf, bufsize);
01016        }
01017 
01018        if (free_it) {
01019               LocalFree(sysbuf);
01020        }
01021 
01022        return buf;
01023 #endif
01024 }
01025 /* }}} */
01026 
01027 /* deprecated */
01028 PHPAPI php_stream *_php_stream_sock_open_from_socket(php_socket_t socket, const char *persistent_id STREAMS_DC TSRMLS_DC)
01029 {
01030        php_stream *stream;
01031        php_netstream_data_t *sock;
01032 
01033        sock = pemalloc(sizeof(php_netstream_data_t), persistent_id ? 1 : 0);
01034        memset(sock, 0, sizeof(php_netstream_data_t));
01035 
01036        sock->is_blocked = 1;
01037        sock->timeout.tv_sec = FG(default_socket_timeout);
01038        sock->timeout.tv_usec = 0;
01039        sock->socket = socket;
01040 
01041        stream = php_stream_alloc_rel(&php_stream_generic_socket_ops, sock, persistent_id, "r+");
01042 
01043        if (stream == NULL) {
01044               pefree(sock, persistent_id ? 1 : 0);
01045        } else {
01046               stream->flags |= PHP_STREAM_FLAG_AVOID_BLOCKING;
01047        }
01048 
01049        return stream;
01050 }
01051 
01052 PHPAPI php_stream *_php_stream_sock_open_host(const char *host, unsigned short port,
01053               int socktype, struct timeval *timeout, const char *persistent_id STREAMS_DC TSRMLS_DC)
01054 {
01055        char *res;
01056        long reslen;
01057        php_stream *stream;
01058 
01059        reslen = spprintf(&res, 0, "tcp://%s:%d", host, port);
01060 
01061        stream = php_stream_xport_create(res, reslen, ENFORCE_SAFE_MODE | REPORT_ERRORS,
01062                      STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT, persistent_id, timeout, NULL, NULL, NULL);
01063 
01064        efree(res);
01065 
01066        return stream;
01067 }
01068 
01069 PHPAPI int php_set_sock_blocking(int socketd, int block TSRMLS_DC)
01070 {
01071        int ret = SUCCESS;
01072        int flags;
01073        int myflag = 0;
01074 
01075 #ifdef PHP_WIN32
01076        /* with ioctlsocket, a non-zero sets nonblocking, a zero sets blocking */
01077        flags = !block;
01078        if (ioctlsocket(socketd, FIONBIO, &flags) == SOCKET_ERROR) {
01079               char *error_string;
01080 
01081               error_string = php_socket_strerror(WSAGetLastError(), NULL, 0);
01082               php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", error_string);
01083               efree(error_string);
01084               ret = FAILURE;
01085        }
01086 #else
01087        flags = fcntl(socketd, F_GETFL);
01088 #ifdef O_NONBLOCK
01089        myflag = O_NONBLOCK; /* POSIX version */
01090 #elif defined(O_NDELAY)
01091        myflag = O_NDELAY;   /* old non-POSIX version */
01092 #endif
01093        if (!block) {
01094               flags |= myflag;
01095        } else {
01096               flags &= ~myflag;
01097        }
01098        if (fcntl(socketd, F_SETFL, flags) == -1) {
01099               ret = FAILURE;
01100        }
01101 #endif
01102        return ret;
01103 }
01104 
01105 PHPAPI void _php_emit_fd_setsize_warning(int max_fd)
01106 {
01107        TSRMLS_FETCH();
01108 
01109 #ifdef PHP_WIN32
01110        php_error_docref(NULL TSRMLS_CC, E_WARNING,
01111               "PHP needs to be recompiled with a larger value of FD_SETSIZE.\n"
01112               "If this binary is from an official www.php.net package, file a bug report\n"
01113               "at http://bugs.php.net, including the following information:\n"
01114               "FD_SETSIZE=%d, but you are using %d.\n"
01115               " --enable-fd-setsize=%d is recommended, but you may want to set it\n"
01116               "to match to maximum number of sockets each script will work with at\n"
01117               "one time, in order to avoid seeing this error again at a later date.",
01118               FD_SETSIZE, max_fd, (max_fd + 128) & ~127);
01119 #else
01120        php_error_docref(NULL TSRMLS_CC, E_WARNING,
01121               "You MUST recompile PHP with a larger value of FD_SETSIZE.\n"
01122               "It is set to %d, but you have descriptors numbered at least as high as %d.\n"
01123               " --enable-fd-setsize=%d is recommended, but you may want to set it\n"
01124               "to equal the maximum number of open files supported by your system,\n"
01125               "in order to avoid seeing this error again at a later date.",
01126               FD_SETSIZE, max_fd, (max_fd + 1024) & ~1023);
01127 #endif
01128 }
01129 
01130 #if defined(PHP_USE_POLL_2_EMULATION)
01131 
01132 /* emulate poll(2) using select(2), safely. */
01133 
01134 PHPAPI int php_poll2(php_pollfd *ufds, unsigned int nfds, int timeout)
01135 {
01136        fd_set rset, wset, eset;
01137        php_socket_t max_fd = SOCK_ERR;
01138        unsigned int i;
01139        int n;
01140        struct timeval tv;
01141 
01142        /* check the highest numbered descriptor */
01143        for (i = 0; i < nfds; i++) {
01144               if (ufds[i].fd > max_fd)
01145                      max_fd = ufds[i].fd;
01146        }
01147 
01148        PHP_SAFE_MAX_FD(max_fd, nfds + 1);
01149 
01150        FD_ZERO(&rset);
01151        FD_ZERO(&wset);
01152        FD_ZERO(&eset);
01153 
01154        for (i = 0; i < nfds; i++) {
01155               if (ufds[i].events & PHP_POLLREADABLE) {
01156                      PHP_SAFE_FD_SET(ufds[i].fd, &rset);
01157               }
01158               if (ufds[i].events & POLLOUT) {
01159                      PHP_SAFE_FD_SET(ufds[i].fd, &wset);
01160               }
01161               if (ufds[i].events & POLLPRI) {
01162                      PHP_SAFE_FD_SET(ufds[i].fd, &eset);
01163               }
01164        }
01165 
01166        if (timeout >= 0) {
01167               tv.tv_sec = timeout / 1000;
01168               tv.tv_usec = (timeout - (tv.tv_sec * 1000)) * 1000;
01169        }
01170 /* Reseting/initializing */
01171 #ifdef PHP_WIN32
01172        WSASetLastError(0);
01173 #else
01174        errno = 0;
01175 #endif
01176        n = select(max_fd + 1, &rset, &wset, &eset, timeout >= 0 ? &tv : NULL);
01177 
01178        if (n >= 0) {
01179               for (i = 0; i < nfds; i++) {
01180                      ufds[i].revents = 0;
01181 
01182                      if (PHP_SAFE_FD_ISSET(ufds[i].fd, &rset)) {
01183                             /* could be POLLERR or POLLHUP but can't tell without probing */
01184                             ufds[i].revents |= POLLIN;
01185                      }
01186                      if (PHP_SAFE_FD_ISSET(ufds[i].fd, &wset)) {
01187                             ufds[i].revents |= POLLOUT;
01188                      }
01189                      if (PHP_SAFE_FD_ISSET(ufds[i].fd, &eset)) {
01190                             ufds[i].revents |= POLLPRI;
01191                      }
01192               }
01193        }
01194        return n;
01195 }
01196 
01197 #endif
01198 
01199 
01200 /*
01201  * Local variables:
01202  * tab-width: 8
01203  * c-basic-offset: 8
01204  * End:
01205  * vim600: sw=4 ts=4 fdm=marker
01206  * vim<600: sw=4 ts=4
01207  */