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