xref: /PHP-5.3/main/network.c (revision a2045ff3)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 5                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2013 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$ */
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  */
php_gai_strerror(int code)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  */
php_network_freeaddresses(struct sockaddr ** sal)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  */
php_network_getaddresses(const char * host,int socktype,struct sockaddr *** sal,char ** error_string TSRMLS_DC)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 */
php_network_connect_socket(php_socket_t sockfd,const struct sockaddr * addr,socklen_t addrlen,int asynchronous,struct timeval * timeout,char ** error_string,int * error_code)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  */
1208