Annotation of embedaddon/php/main/network.c, revision 1.1
1.1 ! misho 1: /*
! 2: +----------------------------------------------------------------------+
! 3: | PHP Version 5 |
! 4: +----------------------------------------------------------------------+
! 5: | Copyright (c) 1997-2012 The PHP Group |
! 6: +----------------------------------------------------------------------+
! 7: | This source file is subject to version 3.01 of the PHP license, |
! 8: | that is bundled with this package in the file LICENSE, and is |
! 9: | available through the world-wide-web at the following url: |
! 10: | http://www.php.net/license/3_01.txt |
! 11: | If you did not receive a copy of the PHP license and are unable to |
! 12: | obtain it through the world-wide-web, please send a note to |
! 13: | license@php.net so we can mail you a copy immediately. |
! 14: +----------------------------------------------------------------------+
! 15: | Author: Stig Venaas <venaas@uninett.no> |
! 16: | Streams work by Wez Furlong <wez@thebrainroom.com> |
! 17: +----------------------------------------------------------------------+
! 18: */
! 19:
! 20: /* $Id: network.c 321634 2012-01-01 13:15:04Z felipe $ */
! 21:
! 22: /*#define DEBUG_MAIN_NETWORK 1*/
! 23:
! 24: #include "php.h"
! 25:
! 26: #include <stddef.h>
! 27:
! 28: #ifdef PHP_WIN32
! 29: # include "win32/inet.h"
! 30: # define O_RDONLY _O_RDONLY
! 31: # include "win32/param.h"
! 32: #elif defined(NETWARE)
! 33: #include <sys/timeval.h>
! 34: #include <sys/param.h>
! 35: #else
! 36: #include <sys/param.h>
! 37: #endif
! 38:
! 39: #include <sys/types.h>
! 40: #if HAVE_SYS_SOCKET_H
! 41: #include <sys/socket.h>
! 42: #endif
! 43:
! 44: #ifndef _FCNTL_H
! 45: #include <fcntl.h>
! 46: #endif
! 47:
! 48: #ifdef HAVE_SYS_SELECT_H
! 49: #include <sys/select.h>
! 50: #endif
! 51: #if HAVE_SYS_POLL_H
! 52: #include <sys/poll.h>
! 53: #endif
! 54:
! 55: #if defined(NETWARE)
! 56: #ifdef USE_WINSOCK
! 57: #include <novsock2.h>
! 58: #else
! 59: #include <arpa/inet.h>
! 60: #include <netinet/in.h>
! 61: #include <netdb.h>
! 62: #include <sys/select.h>
! 63: #include <sys/socket.h>
! 64: #endif
! 65: #elif !defined(PHP_WIN32)
! 66: #include <netinet/in.h>
! 67: #include <netdb.h>
! 68: #if HAVE_ARPA_INET_H
! 69: #include <arpa/inet.h>
! 70: #endif
! 71: #endif
! 72:
! 73: #ifndef HAVE_INET_ATON
! 74: int inet_aton(const char *, struct in_addr *);
! 75: #endif
! 76:
! 77: #include "php_network.h"
! 78:
! 79: #if defined(PHP_WIN32) || defined(__riscos__) || defined(NETWARE)
! 80: #undef AF_UNIX
! 81: #endif
! 82:
! 83: #if defined(AF_UNIX)
! 84: #include <sys/un.h>
! 85: #endif
! 86:
! 87: #include "ext/standard/file.h"
! 88:
! 89: #ifdef PHP_WIN32
! 90: # include "win32/time.h"
! 91: # define SOCK_ERR INVALID_SOCKET
! 92: # define SOCK_CONN_ERR SOCKET_ERROR
! 93: # define PHP_TIMEOUT_ERROR_VALUE WSAETIMEDOUT
! 94:
! 95: #if HAVE_IPV6
! 96: const struct in6_addr in6addr_any = {0}; /* IN6ADDR_ANY_INIT; */
! 97: #endif
! 98:
! 99: #else
! 100: # define SOCK_ERR -1
! 101: # define SOCK_CONN_ERR -1
! 102: # define PHP_TIMEOUT_ERROR_VALUE ETIMEDOUT
! 103: #endif
! 104:
! 105: #if HAVE_GETADDRINFO
! 106: #ifdef HAVE_GAI_STRERROR
! 107: # define PHP_GAI_STRERROR(x) (gai_strerror(x))
! 108: #else
! 109: # define PHP_GAI_STRERROR(x) (php_gai_strerror(x))
! 110: /* {{{ php_gai_strerror
! 111: */
! 112: static const char *php_gai_strerror(int code)
! 113: {
! 114: static struct {
! 115: int code;
! 116: const char *msg;
! 117: } values[] = {
! 118: # ifdef EAI_ADDRFAMILY
! 119: {EAI_ADDRFAMILY, "Address family for hostname not supported"},
! 120: # endif
! 121: {EAI_AGAIN, "Temporary failure in name resolution"},
! 122: {EAI_BADFLAGS, "Bad value for ai_flags"},
! 123: {EAI_FAIL, "Non-recoverable failure in name resolution"},
! 124: {EAI_FAMILY, "ai_family not supported"},
! 125: {EAI_MEMORY, "Memory allocation failure"},
! 126: # ifdef EAI_NODATA
! 127: {EAI_NODATA, "No address associated with hostname"},
! 128: # endif
! 129: {EAI_NONAME, "Name or service not known"},
! 130: {EAI_SERVICE, "Servname not supported for ai_socktype"},
! 131: {EAI_SOCKTYPE, "ai_socktype not supported"},
! 132: {EAI_SYSTEM, "System error"},
! 133: {0, NULL}
! 134: };
! 135: int i;
! 136:
! 137: for (i = 0; values[i].msg != NULL; i++) {
! 138: if (values[i].code == code) {
! 139: return (char *)values[i].msg;
! 140: }
! 141: }
! 142:
! 143: return "Unknown error";
! 144: }
! 145: /* }}} */
! 146: #endif
! 147: #endif
! 148:
! 149: /* {{{ php_network_freeaddresses
! 150: */
! 151: static void php_network_freeaddresses(struct sockaddr **sal)
! 152: {
! 153: struct sockaddr **sap;
! 154:
! 155: if (sal == NULL)
! 156: return;
! 157: for (sap = sal; *sap != NULL; sap++)
! 158: efree(*sap);
! 159: efree(sal);
! 160: }
! 161: /* }}} */
! 162:
! 163: /* {{{ php_network_getaddresses
! 164: * Returns number of addresses, 0 for none/error
! 165: */
! 166: static int php_network_getaddresses(const char *host, int socktype, struct sockaddr ***sal, char **error_string TSRMLS_DC)
! 167: {
! 168: struct sockaddr **sap;
! 169: int n;
! 170: #if HAVE_GETADDRINFO
! 171: # if HAVE_IPV6
! 172: static int ipv6_borked = -1; /* the way this is used *is* thread safe */
! 173: # endif
! 174: struct addrinfo hints, *res, *sai;
! 175: #else
! 176: struct hostent *host_info;
! 177: struct in_addr in;
! 178: #endif
! 179:
! 180: if (host == NULL) {
! 181: return 0;
! 182: }
! 183: #if HAVE_GETADDRINFO
! 184: memset(&hints, '\0', sizeof(hints));
! 185:
! 186: hints.ai_family = AF_INET; /* default to regular inet (see below) */
! 187: hints.ai_socktype = socktype;
! 188:
! 189: # if HAVE_IPV6
! 190: /* probe for a working IPv6 stack; even if detected as having v6 at compile
! 191: * time, at runtime some stacks are slow to resolve or have other issues
! 192: * if they are not correctly configured.
! 193: * static variable use is safe here since simple store or fetch operations
! 194: * are atomic and because the actual probe process is not in danger of
! 195: * collisions or race conditions. */
! 196: if (ipv6_borked == -1) {
! 197: int s;
! 198:
! 199: s = socket(PF_INET6, SOCK_DGRAM, 0);
! 200: if (s == SOCK_ERR) {
! 201: ipv6_borked = 1;
! 202: } else {
! 203: ipv6_borked = 0;
! 204: closesocket(s);
! 205: }
! 206: }
! 207: hints.ai_family = ipv6_borked ? AF_INET : AF_UNSPEC;
! 208: # endif
! 209:
! 210: if ((n = getaddrinfo(host, NULL, &hints, &res))) {
! 211: if (error_string) {
! 212: spprintf(error_string, 0, "php_network_getaddresses: getaddrinfo failed: %s", PHP_GAI_STRERROR(n));
! 213: php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", *error_string);
! 214: } else {
! 215: php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_network_getaddresses: getaddrinfo failed: %s", PHP_GAI_STRERROR(n));
! 216: }
! 217: return 0;
! 218: } else if (res == NULL) {
! 219: if (error_string) {
! 220: spprintf(error_string, 0, "php_network_getaddresses: getaddrinfo failed (null result pointer) errno=%d", errno);
! 221: php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", *error_string);
! 222: } else {
! 223: php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_network_getaddresses: getaddrinfo failed (null result pointer)");
! 224: }
! 225: return 0;
! 226: }
! 227:
! 228: sai = res;
! 229: for (n = 1; (sai = sai->ai_next) != NULL; n++)
! 230: ;
! 231:
! 232: *sal = safe_emalloc((n + 1), sizeof(*sal), 0);
! 233: sai = res;
! 234: sap = *sal;
! 235:
! 236: do {
! 237: *sap = emalloc(sai->ai_addrlen);
! 238: memcpy(*sap, sai->ai_addr, sai->ai_addrlen);
! 239: sap++;
! 240: } while ((sai = sai->ai_next) != NULL);
! 241:
! 242: freeaddrinfo(res);
! 243: #else
! 244: if (!inet_aton(host, &in)) {
! 245: /* XXX NOT THREAD SAFE (is safe under win32) */
! 246: host_info = gethostbyname(host);
! 247: if (host_info == NULL) {
! 248: if (error_string) {
! 249: spprintf(error_string, 0, "php_network_getaddresses: gethostbyname failed. errno=%d", errno);
! 250: php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", *error_string);
! 251: } else {
! 252: php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_network_getaddresses: gethostbyname failed");
! 253: }
! 254: return 0;
! 255: }
! 256: in = *((struct in_addr *) host_info->h_addr);
! 257: }
! 258:
! 259: *sal = safe_emalloc(2, sizeof(*sal), 0);
! 260: sap = *sal;
! 261: *sap = emalloc(sizeof(struct sockaddr_in));
! 262: (*sap)->sa_family = AF_INET;
! 263: ((struct sockaddr_in *)*sap)->sin_addr = in;
! 264: sap++;
! 265: n = 1;
! 266: #endif
! 267:
! 268: *sap = NULL;
! 269: return n;
! 270: }
! 271: /* }}} */
! 272:
! 273: #ifndef O_NONBLOCK
! 274: #define O_NONBLOCK O_NDELAY
! 275: #endif
! 276:
! 277: #if !defined(__BEOS__)
! 278: # define HAVE_NON_BLOCKING_CONNECT 1
! 279: # ifdef PHP_WIN32
! 280: typedef u_long php_non_blocking_flags_t;
! 281: # define SET_SOCKET_BLOCKING_MODE(sock, save) \
! 282: save = TRUE; ioctlsocket(sock, FIONBIO, &save)
! 283: # define RESTORE_SOCKET_BLOCKING_MODE(sock, save) \
! 284: ioctlsocket(sock, FIONBIO, &save)
! 285: # else
! 286: typedef int php_non_blocking_flags_t;
! 287: # define SET_SOCKET_BLOCKING_MODE(sock, save) \
! 288: save = fcntl(sock, F_GETFL, 0); \
! 289: fcntl(sock, F_SETFL, save | O_NONBLOCK)
! 290: # define RESTORE_SOCKET_BLOCKING_MODE(sock, save) \
! 291: fcntl(sock, F_SETFL, save)
! 292: # endif
! 293: #endif
! 294:
! 295: /* Connect to a socket using an interruptible connect with optional timeout.
! 296: * Optionally, the connect can be made asynchronously, which will implicitly
! 297: * enable non-blocking mode on the socket.
! 298: * */
! 299: /* {{{ php_network_connect_socket */
! 300: PHPAPI int php_network_connect_socket(php_socket_t sockfd,
! 301: const struct sockaddr *addr,
! 302: socklen_t addrlen,
! 303: int asynchronous,
! 304: struct timeval *timeout,
! 305: char **error_string,
! 306: int *error_code)
! 307: {
! 308: #if HAVE_NON_BLOCKING_CONNECT
! 309: php_non_blocking_flags_t orig_flags;
! 310: int n;
! 311: int error = 0;
! 312: socklen_t len;
! 313: int ret = 0;
! 314:
! 315: SET_SOCKET_BLOCKING_MODE(sockfd, orig_flags);
! 316:
! 317: if ((n = connect(sockfd, addr, addrlen)) != 0) {
! 318: error = php_socket_errno();
! 319:
! 320: if (error_code) {
! 321: *error_code = error;
! 322: }
! 323:
! 324: if (error != EINPROGRESS) {
! 325: if (error_string) {
! 326: *error_string = php_socket_strerror(error, NULL, 0);
! 327: }
! 328:
! 329: return -1;
! 330: }
! 331: if (asynchronous && error == EINPROGRESS) {
! 332: /* this is fine by us */
! 333: return 0;
! 334: }
! 335: }
! 336:
! 337: if (n == 0) {
! 338: goto ok;
! 339: }
! 340: # ifdef PHP_WIN32
! 341: /* The documentation for connect() says in case of non-blocking connections
! 342: * the select function reports success in the writefds set and failure in
! 343: * the exceptfds set. Indeed, using PHP_POLLREADABLE results in select
! 344: * failing only due to the timeout and not immediately as would be
! 345: * expected when a connection is actively refused. This way,
! 346: * php_pollfd_for will return a mask with POLLOUT if the connection
! 347: * is successful and with POLLPRI otherwise. */
! 348: if ((n = php_pollfd_for(sockfd, POLLOUT|POLLPRI, timeout)) == 0) {
! 349: #else
! 350: if ((n = php_pollfd_for(sockfd, PHP_POLLREADABLE|POLLOUT, timeout)) == 0) {
! 351: #endif
! 352: error = PHP_TIMEOUT_ERROR_VALUE;
! 353: }
! 354:
! 355: if (n > 0) {
! 356: len = sizeof(error);
! 357: /*
! 358: BSD-derived systems set errno correctly
! 359: Solaris returns -1 from getsockopt in case of error
! 360: */
! 361: if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (char*)&error, &len) != 0) {
! 362: ret = -1;
! 363: }
! 364: } else {
! 365: /* whoops: sockfd has disappeared */
! 366: ret = -1;
! 367: }
! 368:
! 369: ok:
! 370: if (!asynchronous) {
! 371: /* back to blocking mode */
! 372: RESTORE_SOCKET_BLOCKING_MODE(sockfd, orig_flags);
! 373: }
! 374:
! 375: if (error_code) {
! 376: *error_code = error;
! 377: }
! 378:
! 379: if (error) {
! 380: ret = -1;
! 381: if (error_string) {
! 382: *error_string = php_socket_strerror(error, NULL, 0);
! 383: }
! 384: }
! 385: return ret;
! 386: #else
! 387: if (asynchronous) {
! 388: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Asynchronous connect() not supported on this platform");
! 389: }
! 390: return (connect(sockfd, addr, addrlen) == 0) ? 0 : -1;
! 391: #endif
! 392: }
! 393: /* }}} */
! 394:
! 395: /* {{{ sub_times */
! 396: static inline void sub_times(struct timeval a, struct timeval b, struct timeval *result)
! 397: {
! 398: result->tv_usec = a.tv_usec - b.tv_usec;
! 399: if (result->tv_usec < 0L) {
! 400: a.tv_sec--;
! 401: result->tv_usec += 1000000L;
! 402: }
! 403: result->tv_sec = a.tv_sec - b.tv_sec;
! 404: if (result->tv_sec < 0L) {
! 405: result->tv_sec++;
! 406: result->tv_usec -= 1000000L;
! 407: }
! 408: }
! 409: /* }}} */
! 410:
! 411: /* Bind to a local IP address.
! 412: * Returns the bound socket, or -1 on failure.
! 413: * */
! 414: /* {{{ php_network_bind_socket_to_local_addr */
! 415: php_socket_t php_network_bind_socket_to_local_addr(const char *host, unsigned port,
! 416: int socktype, char **error_string, int *error_code
! 417: TSRMLS_DC)
! 418: {
! 419: int num_addrs, n, err = 0;
! 420: php_socket_t sock;
! 421: struct sockaddr **sal, **psal, *sa;
! 422: socklen_t socklen;
! 423:
! 424: num_addrs = php_network_getaddresses(host, socktype, &psal, error_string TSRMLS_CC);
! 425:
! 426: if (num_addrs == 0) {
! 427: /* could not resolve address(es) */
! 428: return -1;
! 429: }
! 430:
! 431: for (sal = psal; *sal != NULL; sal++) {
! 432: sa = *sal;
! 433:
! 434: /* create a socket for this address */
! 435: sock = socket(sa->sa_family, socktype, 0);
! 436:
! 437: if (sock == SOCK_ERR) {
! 438: continue;
! 439: }
! 440:
! 441: switch (sa->sa_family) {
! 442: #if HAVE_GETADDRINFO && HAVE_IPV6
! 443: case AF_INET6:
! 444: ((struct sockaddr_in6 *)sa)->sin6_family = sa->sa_family;
! 445: ((struct sockaddr_in6 *)sa)->sin6_port = htons(port);
! 446: socklen = sizeof(struct sockaddr_in6);
! 447: break;
! 448: #endif
! 449: case AF_INET:
! 450: ((struct sockaddr_in *)sa)->sin_family = sa->sa_family;
! 451: ((struct sockaddr_in *)sa)->sin_port = htons(port);
! 452: socklen = sizeof(struct sockaddr_in);
! 453: break;
! 454: default:
! 455: /* Unknown family */
! 456: socklen = 0;
! 457: sa = NULL;
! 458: }
! 459:
! 460: if (sa) {
! 461: /* attempt to bind */
! 462:
! 463: #ifdef SO_REUSEADDR
! 464: {
! 465: int val = 1;
! 466: setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&val, sizeof(val));
! 467: }
! 468: #endif
! 469:
! 470: n = bind(sock, sa, socklen);
! 471:
! 472: if (n != SOCK_CONN_ERR) {
! 473: goto bound;
! 474: }
! 475:
! 476: err = php_socket_errno();
! 477: }
! 478:
! 479: closesocket(sock);
! 480: }
! 481: sock = -1;
! 482:
! 483: if (error_code) {
! 484: *error_code = err;
! 485: }
! 486: if (error_string) {
! 487: *error_string = php_socket_strerror(err, NULL, 0);
! 488: }
! 489:
! 490: bound:
! 491:
! 492: php_network_freeaddresses(psal);
! 493:
! 494: return sock;
! 495:
! 496: }
! 497: /* }}} */
! 498:
! 499: PHPAPI int php_network_parse_network_address_with_port(const char *addr, long addrlen, struct sockaddr *sa, socklen_t *sl TSRMLS_DC)
! 500: {
! 501: char *colon;
! 502: char *tmp;
! 503: int ret = FAILURE;
! 504: short port;
! 505: struct sockaddr_in *in4 = (struct sockaddr_in*)sa;
! 506: struct sockaddr **psal;
! 507: int n;
! 508: char *errstr = NULL;
! 509: #if HAVE_IPV6
! 510: struct sockaddr_in6 *in6 = (struct sockaddr_in6*)sa;
! 511: #endif
! 512:
! 513: if (*addr == '[') {
! 514: colon = memchr(addr + 1, ']', addrlen-1);
! 515: if (!colon || colon[1] != ':') {
! 516: return FAILURE;
! 517: }
! 518: port = atoi(colon + 2);
! 519: addr++;
! 520: } else {
! 521: colon = memchr(addr, ':', addrlen);
! 522: if (!colon) {
! 523: return FAILURE;
! 524: }
! 525: port = atoi(colon + 1);
! 526: }
! 527:
! 528: tmp = estrndup(addr, colon - addr);
! 529:
! 530: /* first, try interpreting the address as a numeric address */
! 531:
! 532: #if HAVE_IPV6 && HAVE_INET_PTON
! 533: if (inet_pton(AF_INET6, tmp, &in6->sin6_addr) > 0) {
! 534: in6->sin6_port = htons(port);
! 535: in6->sin6_family = AF_INET6;
! 536: *sl = sizeof(struct sockaddr_in6);
! 537: ret = SUCCESS;
! 538: goto out;
! 539: }
! 540: #endif
! 541: if (inet_aton(tmp, &in4->sin_addr) > 0) {
! 542: in4->sin_port = htons(port);
! 543: in4->sin_family = AF_INET;
! 544: *sl = sizeof(struct sockaddr_in);
! 545: ret = SUCCESS;
! 546: goto out;
! 547: }
! 548:
! 549: /* looks like we'll need to resolve it */
! 550: n = php_network_getaddresses(tmp, SOCK_DGRAM, &psal, &errstr TSRMLS_CC);
! 551:
! 552: if (n == 0) {
! 553: if (errstr) {
! 554: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to resolve `%s': %s", tmp, errstr);
! 555: STR_FREE(errstr);
! 556: }
! 557: goto out;
! 558: }
! 559:
! 560: /* copy the details from the first item */
! 561: switch ((*psal)->sa_family) {
! 562: #if HAVE_GETADDRINFO && HAVE_IPV6
! 563: case AF_INET6:
! 564: *in6 = **(struct sockaddr_in6**)psal;
! 565: in6->sin6_port = htons(port);
! 566: *sl = sizeof(struct sockaddr_in6);
! 567: ret = SUCCESS;
! 568: break;
! 569: #endif
! 570: case AF_INET:
! 571: *in4 = **(struct sockaddr_in**)psal;
! 572: in4->sin_port = htons(port);
! 573: *sl = sizeof(struct sockaddr_in);
! 574: ret = SUCCESS;
! 575: break;
! 576: }
! 577:
! 578: php_network_freeaddresses(psal);
! 579:
! 580: out:
! 581: STR_FREE(tmp);
! 582: return ret;
! 583: }
! 584:
! 585:
! 586: PHPAPI void php_network_populate_name_from_sockaddr(
! 587: /* input address */
! 588: struct sockaddr *sa, socklen_t sl,
! 589: /* output readable address */
! 590: char **textaddr, long *textaddrlen,
! 591: /* output address */
! 592: struct sockaddr **addr,
! 593: socklen_t *addrlen
! 594: TSRMLS_DC)
! 595: {
! 596: if (addr) {
! 597: *addr = emalloc(sl);
! 598: memcpy(*addr, sa, sl);
! 599: *addrlen = sl;
! 600: }
! 601:
! 602: if (textaddr) {
! 603: #if HAVE_IPV6 && HAVE_INET_NTOP
! 604: char abuf[256];
! 605: #endif
! 606: char *buf = NULL;
! 607:
! 608: switch (sa->sa_family) {
! 609: case AF_INET:
! 610: /* generally not thread safe, but it *is* thread safe under win32 */
! 611: buf = inet_ntoa(((struct sockaddr_in*)sa)->sin_addr);
! 612: if (buf) {
! 613: *textaddrlen = spprintf(textaddr, 0, "%s:%d",
! 614: buf, ntohs(((struct sockaddr_in*)sa)->sin_port));
! 615: }
! 616:
! 617: break;
! 618:
! 619: #if HAVE_IPV6 && HAVE_INET_NTOP
! 620: case AF_INET6:
! 621: buf = (char*)inet_ntop(sa->sa_family, &((struct sockaddr_in6*)sa)->sin6_addr, (char *)&abuf, sizeof(abuf));
! 622: if (buf) {
! 623: *textaddrlen = spprintf(textaddr, 0, "%s:%d",
! 624: buf, ntohs(((struct sockaddr_in6*)sa)->sin6_port));
! 625: }
! 626:
! 627: break;
! 628: #endif
! 629: #ifdef AF_UNIX
! 630: case AF_UNIX:
! 631: {
! 632: struct sockaddr_un *ua = (struct sockaddr_un*)sa;
! 633:
! 634: if (ua->sun_path[0] == '\0') {
! 635: /* abstract name */
! 636: int len = strlen(ua->sun_path + 1) + 1;
! 637: *textaddrlen = len;
! 638: *textaddr = emalloc(len + 1);
! 639: memcpy(*textaddr, ua->sun_path, len);
! 640: (*textaddr)[len] = '\0';
! 641: } else {
! 642: *textaddrlen = strlen(ua->sun_path);
! 643: *textaddr = estrndup(ua->sun_path, *textaddrlen);
! 644: }
! 645: }
! 646: break;
! 647: #endif
! 648:
! 649: }
! 650:
! 651: }
! 652: }
! 653:
! 654: PHPAPI int php_network_get_peer_name(php_socket_t sock,
! 655: char **textaddr, long *textaddrlen,
! 656: struct sockaddr **addr,
! 657: socklen_t *addrlen
! 658: TSRMLS_DC)
! 659: {
! 660: php_sockaddr_storage sa;
! 661: socklen_t sl = sizeof(sa);
! 662: memset(&sa, 0, sizeof(sa));
! 663:
! 664: if (getpeername(sock, (struct sockaddr*)&sa, &sl) == 0) {
! 665: php_network_populate_name_from_sockaddr((struct sockaddr*)&sa, sl,
! 666: textaddr, textaddrlen,
! 667: addr, addrlen
! 668: TSRMLS_CC);
! 669: return 0;
! 670: }
! 671: return -1;
! 672: }
! 673:
! 674: PHPAPI int php_network_get_sock_name(php_socket_t sock,
! 675: char **textaddr, long *textaddrlen,
! 676: struct sockaddr **addr,
! 677: socklen_t *addrlen
! 678: TSRMLS_DC)
! 679: {
! 680: php_sockaddr_storage sa;
! 681: socklen_t sl = sizeof(sa);
! 682: memset(&sa, 0, sizeof(sa));
! 683:
! 684: if (getsockname(sock, (struct sockaddr*)&sa, &sl) == 0) {
! 685: php_network_populate_name_from_sockaddr((struct sockaddr*)&sa, sl,
! 686: textaddr, textaddrlen,
! 687: addr, addrlen
! 688: TSRMLS_CC);
! 689: return 0;
! 690: }
! 691: return -1;
! 692:
! 693: }
! 694:
! 695:
! 696: /* Accept a client connection from a server socket,
! 697: * using an optional timeout.
! 698: * Returns the peer address in addr/addrlen (it will emalloc
! 699: * these, so be sure to efree the result).
! 700: * If you specify textaddr/textaddrlen, a text-printable
! 701: * version of the address will be emalloc'd and returned.
! 702: * */
! 703:
! 704: /* {{{ php_network_accept_incoming */
! 705: PHPAPI php_socket_t php_network_accept_incoming(php_socket_t srvsock,
! 706: char **textaddr, long *textaddrlen,
! 707: struct sockaddr **addr,
! 708: socklen_t *addrlen,
! 709: struct timeval *timeout,
! 710: char **error_string,
! 711: int *error_code
! 712: TSRMLS_DC)
! 713: {
! 714: php_socket_t clisock = -1;
! 715: int error = 0, n;
! 716: php_sockaddr_storage sa;
! 717: socklen_t sl;
! 718:
! 719: n = php_pollfd_for(srvsock, PHP_POLLREADABLE, timeout);
! 720:
! 721: if (n == 0) {
! 722: error = PHP_TIMEOUT_ERROR_VALUE;
! 723: } else if (n == -1) {
! 724: error = php_socket_errno();
! 725: } else {
! 726: sl = sizeof(sa);
! 727:
! 728: clisock = accept(srvsock, (struct sockaddr*)&sa, &sl);
! 729:
! 730: if (clisock != SOCK_ERR) {
! 731: php_network_populate_name_from_sockaddr((struct sockaddr*)&sa, sl,
! 732: textaddr, textaddrlen,
! 733: addr, addrlen
! 734: TSRMLS_CC);
! 735: } else {
! 736: error = php_socket_errno();
! 737: }
! 738: }
! 739:
! 740: if (error_code) {
! 741: *error_code = error;
! 742: }
! 743: if (error_string) {
! 744: *error_string = php_socket_strerror(error, NULL, 0);
! 745: }
! 746:
! 747: return clisock;
! 748: }
! 749: /* }}} */
! 750:
! 751:
! 752:
! 753: /* Connect to a remote host using an interruptible connect with optional timeout.
! 754: * Optionally, the connect can be made asynchronously, which will implicitly
! 755: * enable non-blocking mode on the socket.
! 756: * Returns the connected (or connecting) socket, or -1 on failure.
! 757: * */
! 758:
! 759: /* {{{ php_network_connect_socket_to_host */
! 760: php_socket_t php_network_connect_socket_to_host(const char *host, unsigned short port,
! 761: int socktype, int asynchronous, struct timeval *timeout, char **error_string,
! 762: int *error_code, char *bindto, unsigned short bindport
! 763: TSRMLS_DC)
! 764: {
! 765: int num_addrs, n, fatal = 0;
! 766: php_socket_t sock;
! 767: struct sockaddr **sal, **psal, *sa;
! 768: struct timeval working_timeout;
! 769: socklen_t socklen;
! 770: #if HAVE_GETTIMEOFDAY
! 771: struct timeval limit_time, time_now;
! 772: #endif
! 773:
! 774: num_addrs = php_network_getaddresses(host, socktype, &psal, error_string TSRMLS_CC);
! 775:
! 776: if (num_addrs == 0) {
! 777: /* could not resolve address(es) */
! 778: return -1;
! 779: }
! 780:
! 781: if (timeout) {
! 782: memcpy(&working_timeout, timeout, sizeof(working_timeout));
! 783: #if HAVE_GETTIMEOFDAY
! 784: gettimeofday(&limit_time, NULL);
! 785: limit_time.tv_sec += working_timeout.tv_sec;
! 786: limit_time.tv_usec += working_timeout.tv_usec;
! 787: if (limit_time.tv_usec >= 1000000) {
! 788: limit_time.tv_usec -= 1000000;
! 789: limit_time.tv_sec++;
! 790: }
! 791: #endif
! 792: }
! 793:
! 794: for (sal = psal; !fatal && *sal != NULL; sal++) {
! 795: sa = *sal;
! 796:
! 797: /* create a socket for this address */
! 798: sock = socket(sa->sa_family, socktype, 0);
! 799:
! 800: if (sock == SOCK_ERR) {
! 801: continue;
! 802: }
! 803:
! 804: switch (sa->sa_family) {
! 805: #if HAVE_GETADDRINFO && HAVE_IPV6
! 806: case AF_INET6:
! 807: if (!bindto || strchr(bindto, ':')) {
! 808: ((struct sockaddr_in6 *)sa)->sin6_family = sa->sa_family;
! 809: ((struct sockaddr_in6 *)sa)->sin6_port = htons(port);
! 810: socklen = sizeof(struct sockaddr_in6);
! 811: } else {
! 812: socklen = 0;
! 813: sa = NULL;
! 814: }
! 815: break;
! 816: #endif
! 817: case AF_INET:
! 818: ((struct sockaddr_in *)sa)->sin_family = sa->sa_family;
! 819: ((struct sockaddr_in *)sa)->sin_port = htons(port);
! 820: socklen = sizeof(struct sockaddr_in);
! 821: break;
! 822: default:
! 823: /* Unknown family */
! 824: socklen = 0;
! 825: sa = NULL;
! 826: }
! 827:
! 828: if (sa) {
! 829: /* make a connection attempt */
! 830:
! 831: if (bindto) {
! 832: struct sockaddr *local_address = NULL;
! 833: int local_address_len = 0;
! 834:
! 835: if (sa->sa_family == AF_INET) {
! 836: struct sockaddr_in *in4 = emalloc(sizeof(struct sockaddr_in));
! 837:
! 838: local_address = (struct sockaddr*)in4;
! 839: local_address_len = sizeof(struct sockaddr_in);
! 840:
! 841: in4->sin_family = sa->sa_family;
! 842: in4->sin_port = htons(bindport);
! 843: if (!inet_aton(bindto, &in4->sin_addr)) {
! 844: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid IP Address: %s", bindto);
! 845: goto skip_bind;
! 846: }
! 847: memset(&(in4->sin_zero), 0, sizeof(in4->sin_zero));
! 848: }
! 849: #if HAVE_IPV6 && HAVE_INET_PTON
! 850: else { /* IPV6 */
! 851: struct sockaddr_in6 *in6 = emalloc(sizeof(struct sockaddr_in6));
! 852:
! 853: local_address = (struct sockaddr*)in6;
! 854: local_address_len = sizeof(struct sockaddr_in6);
! 855:
! 856: in6->sin6_family = sa->sa_family;
! 857: in6->sin6_port = htons(bindport);
! 858: if (inet_pton(AF_INET6, bindto, &in6->sin6_addr) < 1) {
! 859: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid IP Address: %s", bindto);
! 860: goto skip_bind;
! 861: }
! 862: }
! 863: #endif
! 864: if (!local_address || bind(sock, local_address, local_address_len)) {
! 865: php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to bind to '%s:%d', system said: %s", bindto, bindport, strerror(errno));
! 866: }
! 867: skip_bind:
! 868: if (local_address) {
! 869: efree(local_address);
! 870: }
! 871: }
! 872: /* free error string recieved during previous iteration (if any) */
! 873: if (error_string && *error_string) {
! 874: efree(*error_string);
! 875: *error_string = NULL;
! 876: }
! 877:
! 878: n = php_network_connect_socket(sock, sa, socklen, asynchronous,
! 879: timeout ? &working_timeout : NULL,
! 880: error_string, error_code);
! 881:
! 882: if (n != -1) {
! 883: goto connected;
! 884: }
! 885:
! 886: /* adjust timeout for next attempt */
! 887: #if HAVE_GETTIMEOFDAY
! 888: if (timeout) {
! 889: gettimeofday(&time_now, NULL);
! 890:
! 891: if (timercmp(&time_now, &limit_time, >=)) {
! 892: /* time limit expired; don't attempt any further connections */
! 893: fatal = 1;
! 894: } else {
! 895: /* work out remaining time */
! 896: sub_times(limit_time, time_now, &working_timeout);
! 897: }
! 898: }
! 899: #else
! 900: if (error_code && *error_code == PHP_TIMEOUT_ERROR_VALUE) {
! 901: /* Don't even bother trying to connect to the next alternative;
! 902: * we have no way to determine how long we have already taken
! 903: * and it is quite likely that the next attempt will fail too. */
! 904: fatal = 1;
! 905: } else {
! 906: /* re-use the same initial timeout.
! 907: * Not the best thing, but in practice it should be good-enough */
! 908: if (timeout) {
! 909: memcpy(&working_timeout, timeout, sizeof(working_timeout));
! 910: }
! 911: }
! 912: #endif
! 913: }
! 914:
! 915: closesocket(sock);
! 916: }
! 917: sock = -1;
! 918:
! 919: connected:
! 920:
! 921: php_network_freeaddresses(psal);
! 922:
! 923: return sock;
! 924: }
! 925: /* }}} */
! 926:
! 927: /* {{{ php_any_addr
! 928: * Fills the any (wildcard) address into php_sockaddr_storage
! 929: */
! 930: PHPAPI void php_any_addr(int family, php_sockaddr_storage *addr, unsigned short port)
! 931: {
! 932: memset(addr, 0, sizeof(php_sockaddr_storage));
! 933: switch (family) {
! 934: #if HAVE_IPV6
! 935: case AF_INET6: {
! 936: struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) addr;
! 937: sin6->sin6_family = AF_INET6;
! 938: sin6->sin6_port = htons(port);
! 939: sin6->sin6_addr = in6addr_any;
! 940: break;
! 941: }
! 942: #endif
! 943: case AF_INET: {
! 944: struct sockaddr_in *sin = (struct sockaddr_in *) addr;
! 945: sin->sin_family = AF_INET;
! 946: sin->sin_port = htons(port);
! 947: sin->sin_addr.s_addr = htonl(INADDR_ANY);
! 948: break;
! 949: }
! 950: }
! 951: }
! 952: /* }}} */
! 953:
! 954: /* {{{ php_sockaddr_size
! 955: * Returns the size of struct sockaddr_xx for the family
! 956: */
! 957: PHPAPI int php_sockaddr_size(php_sockaddr_storage *addr)
! 958: {
! 959: switch (((struct sockaddr *)addr)->sa_family) {
! 960: case AF_INET:
! 961: return sizeof(struct sockaddr_in);
! 962: #if HAVE_IPV6
! 963: case AF_INET6:
! 964: return sizeof(struct sockaddr_in6);
! 965: #endif
! 966: #ifdef AF_UNIX
! 967: case AF_UNIX:
! 968: return sizeof(struct sockaddr_un);
! 969: #endif
! 970: default:
! 971: return 0;
! 972: }
! 973: }
! 974: /* }}} */
! 975:
! 976: /* Given a socket error code, if buf == NULL:
! 977: * emallocs storage for the error message and returns
! 978: * else
! 979: * sprintf message into provided buffer and returns buf
! 980: */
! 981: /* {{{ php_socket_strerror */
! 982: PHPAPI char *php_socket_strerror(long err, char *buf, size_t bufsize)
! 983: {
! 984: #ifndef PHP_WIN32
! 985: char *errstr;
! 986:
! 987: errstr = strerror(err);
! 988: if (buf == NULL) {
! 989: buf = estrdup(errstr);
! 990: } else {
! 991: strncpy(buf, errstr, bufsize);
! 992: }
! 993: return buf;
! 994: #else
! 995: char *sysbuf;
! 996: int free_it = 1;
! 997:
! 998: if (!FormatMessage(
! 999: FORMAT_MESSAGE_ALLOCATE_BUFFER |
! 1000: FORMAT_MESSAGE_FROM_SYSTEM |
! 1001: FORMAT_MESSAGE_IGNORE_INSERTS,
! 1002: NULL,
! 1003: err,
! 1004: MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
! 1005: (LPTSTR)&sysbuf,
! 1006: 0,
! 1007: NULL)) {
! 1008: free_it = 0;
! 1009: sysbuf = "Unknown Error";
! 1010: }
! 1011:
! 1012: if (buf == NULL) {
! 1013: buf = estrdup(sysbuf);
! 1014: } else {
! 1015: strncpy(buf, sysbuf, bufsize);
! 1016: }
! 1017:
! 1018: if (free_it) {
! 1019: LocalFree(sysbuf);
! 1020: }
! 1021:
! 1022: return buf;
! 1023: #endif
! 1024: }
! 1025: /* }}} */
! 1026:
! 1027: /* deprecated */
! 1028: PHPAPI php_stream *_php_stream_sock_open_from_socket(php_socket_t socket, const char *persistent_id STREAMS_DC TSRMLS_DC)
! 1029: {
! 1030: php_stream *stream;
! 1031: php_netstream_data_t *sock;
! 1032:
! 1033: sock = pemalloc(sizeof(php_netstream_data_t), persistent_id ? 1 : 0);
! 1034: memset(sock, 0, sizeof(php_netstream_data_t));
! 1035:
! 1036: sock->is_blocked = 1;
! 1037: sock->timeout.tv_sec = FG(default_socket_timeout);
! 1038: sock->timeout.tv_usec = 0;
! 1039: sock->socket = socket;
! 1040:
! 1041: stream = php_stream_alloc_rel(&php_stream_generic_socket_ops, sock, persistent_id, "r+");
! 1042:
! 1043: if (stream == NULL) {
! 1044: pefree(sock, persistent_id ? 1 : 0);
! 1045: } else {
! 1046: stream->flags |= PHP_STREAM_FLAG_AVOID_BLOCKING;
! 1047: }
! 1048:
! 1049: return stream;
! 1050: }
! 1051:
! 1052: PHPAPI php_stream *_php_stream_sock_open_host(const char *host, unsigned short port,
! 1053: int socktype, struct timeval *timeout, const char *persistent_id STREAMS_DC TSRMLS_DC)
! 1054: {
! 1055: char *res;
! 1056: long reslen;
! 1057: php_stream *stream;
! 1058:
! 1059: reslen = spprintf(&res, 0, "tcp://%s:%d", host, port);
! 1060:
! 1061: stream = php_stream_xport_create(res, reslen, ENFORCE_SAFE_MODE | REPORT_ERRORS,
! 1062: STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT, persistent_id, timeout, NULL, NULL, NULL);
! 1063:
! 1064: efree(res);
! 1065:
! 1066: return stream;
! 1067: }
! 1068:
! 1069: PHPAPI int php_set_sock_blocking(int socketd, int block TSRMLS_DC)
! 1070: {
! 1071: int ret = SUCCESS;
! 1072: int flags;
! 1073: int myflag = 0;
! 1074:
! 1075: #ifdef PHP_WIN32
! 1076: /* with ioctlsocket, a non-zero sets nonblocking, a zero sets blocking */
! 1077: flags = !block;
! 1078: if (ioctlsocket(socketd, FIONBIO, &flags) == SOCKET_ERROR) {
! 1079: char *error_string;
! 1080:
! 1081: error_string = php_socket_strerror(WSAGetLastError(), NULL, 0);
! 1082: php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", error_string);
! 1083: efree(error_string);
! 1084: ret = FAILURE;
! 1085: }
! 1086: #else
! 1087: flags = fcntl(socketd, F_GETFL);
! 1088: #ifdef O_NONBLOCK
! 1089: myflag = O_NONBLOCK; /* POSIX version */
! 1090: #elif defined(O_NDELAY)
! 1091: myflag = O_NDELAY; /* old non-POSIX version */
! 1092: #endif
! 1093: if (!block) {
! 1094: flags |= myflag;
! 1095: } else {
! 1096: flags &= ~myflag;
! 1097: }
! 1098: if (fcntl(socketd, F_SETFL, flags) == -1) {
! 1099: ret = FAILURE;
! 1100: }
! 1101: #endif
! 1102: return ret;
! 1103: }
! 1104:
! 1105: PHPAPI void _php_emit_fd_setsize_warning(int max_fd)
! 1106: {
! 1107: TSRMLS_FETCH();
! 1108:
! 1109: #ifdef PHP_WIN32
! 1110: php_error_docref(NULL TSRMLS_CC, E_WARNING,
! 1111: "PHP needs to be recompiled with a larger value of FD_SETSIZE.\n"
! 1112: "If this binary is from an official www.php.net package, file a bug report\n"
! 1113: "at http://bugs.php.net, including the following information:\n"
! 1114: "FD_SETSIZE=%d, but you are using %d.\n"
! 1115: " --enable-fd-setsize=%d is recommended, but you may want to set it\n"
! 1116: "to match to maximum number of sockets each script will work with at\n"
! 1117: "one time, in order to avoid seeing this error again at a later date.",
! 1118: FD_SETSIZE, max_fd, (max_fd + 128) & ~127);
! 1119: #else
! 1120: php_error_docref(NULL TSRMLS_CC, E_WARNING,
! 1121: "You MUST recompile PHP with a larger value of FD_SETSIZE.\n"
! 1122: "It is set to %d, but you have descriptors numbered at least as high as %d.\n"
! 1123: " --enable-fd-setsize=%d is recommended, but you may want to set it\n"
! 1124: "to equal the maximum number of open files supported by your system,\n"
! 1125: "in order to avoid seeing this error again at a later date.",
! 1126: FD_SETSIZE, max_fd, (max_fd + 1024) & ~1023);
! 1127: #endif
! 1128: }
! 1129:
! 1130: #if defined(PHP_USE_POLL_2_EMULATION)
! 1131:
! 1132: /* emulate poll(2) using select(2), safely. */
! 1133:
! 1134: PHPAPI int php_poll2(php_pollfd *ufds, unsigned int nfds, int timeout)
! 1135: {
! 1136: fd_set rset, wset, eset;
! 1137: php_socket_t max_fd = SOCK_ERR;
! 1138: unsigned int i;
! 1139: int n;
! 1140: struct timeval tv;
! 1141:
! 1142: /* check the highest numbered descriptor */
! 1143: for (i = 0; i < nfds; i++) {
! 1144: if (ufds[i].fd > max_fd)
! 1145: max_fd = ufds[i].fd;
! 1146: }
! 1147:
! 1148: PHP_SAFE_MAX_FD(max_fd, nfds + 1);
! 1149:
! 1150: FD_ZERO(&rset);
! 1151: FD_ZERO(&wset);
! 1152: FD_ZERO(&eset);
! 1153:
! 1154: for (i = 0; i < nfds; i++) {
! 1155: if (ufds[i].events & PHP_POLLREADABLE) {
! 1156: PHP_SAFE_FD_SET(ufds[i].fd, &rset);
! 1157: }
! 1158: if (ufds[i].events & POLLOUT) {
! 1159: PHP_SAFE_FD_SET(ufds[i].fd, &wset);
! 1160: }
! 1161: if (ufds[i].events & POLLPRI) {
! 1162: PHP_SAFE_FD_SET(ufds[i].fd, &eset);
! 1163: }
! 1164: }
! 1165:
! 1166: if (timeout >= 0) {
! 1167: tv.tv_sec = timeout / 1000;
! 1168: tv.tv_usec = (timeout - (tv.tv_sec * 1000)) * 1000;
! 1169: }
! 1170: /* Reseting/initializing */
! 1171: #ifdef PHP_WIN32
! 1172: WSASetLastError(0);
! 1173: #else
! 1174: errno = 0;
! 1175: #endif
! 1176: n = select(max_fd + 1, &rset, &wset, &eset, timeout >= 0 ? &tv : NULL);
! 1177:
! 1178: if (n >= 0) {
! 1179: for (i = 0; i < nfds; i++) {
! 1180: ufds[i].revents = 0;
! 1181:
! 1182: if (PHP_SAFE_FD_ISSET(ufds[i].fd, &rset)) {
! 1183: /* could be POLLERR or POLLHUP but can't tell without probing */
! 1184: ufds[i].revents |= POLLIN;
! 1185: }
! 1186: if (PHP_SAFE_FD_ISSET(ufds[i].fd, &wset)) {
! 1187: ufds[i].revents |= POLLOUT;
! 1188: }
! 1189: if (PHP_SAFE_FD_ISSET(ufds[i].fd, &eset)) {
! 1190: ufds[i].revents |= POLLPRI;
! 1191: }
! 1192: }
! 1193: }
! 1194: return n;
! 1195: }
! 1196:
! 1197: #endif
! 1198:
! 1199:
! 1200: /*
! 1201: * Local variables:
! 1202: * tab-width: 8
! 1203: * c-basic-offset: 8
! 1204: * End:
! 1205: * vim600: sw=4 ts=4 fdm=marker
! 1206: * vim<600: sw=4 ts=4
! 1207: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>