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