File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / php / main / network.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue May 29 12:34:35 2012 UTC (12 years, 1 month ago) by misho
Branches: php, MAIN
CVS tags: v5_4_3elwix, v5_4_17p0, HEAD
php 5.4.3+patches

    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,v 1.1.1.2 2012/05/29 12:34:35 misho Exp $ */
   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: PHPAPI 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: PHPAPI 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, 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>