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