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/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 for %s failed: %s", host, 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 for %s failed: %s", host, 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 for %s failed (null result pointer) errno=%d", host, 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 for %s failed (null result pointer)", host);
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 #ifdef HAVE_INET_PTON
241 if (!inet_pton(AF_INET, host, &in)) {
242 #else
243 if (!inet_aton(host, &in)) {
244 #endif
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 */
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 HAVE_GETADDRINFO && 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 int php_network_parse_network_address_with_port(const char *addr, zend_long addrlen, struct sockaddr *sa, socklen_t *sl)
512 {
513 char *colon;
514 char *tmp;
515 int 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 #if 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 #if HAVE_IPV6 && HAVE_INET_PTON
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 #ifdef HAVE_INET_PTON
558 if (inet_pton(AF_INET, tmp, &in4->sin_addr) > 0) {
559 #else
560 if (inet_aton(tmp, &in4->sin_addr) > 0) {
561 #endif
562 in4->sin_port = htons(port);
563 in4->sin_family = AF_INET;
564 *sl = sizeof(struct sockaddr_in);
565 ret = SUCCESS;
566 goto out;
567 }
568
569 /* looks like we'll need to resolve it */
570 n = php_network_getaddresses(tmp, SOCK_DGRAM, &psal, &errstr);
571
572 if (n == 0) {
573 if (errstr) {
574 php_error_docref(NULL, E_WARNING, "Failed to resolve `%s': %s", tmp, ZSTR_VAL(errstr));
575 zend_string_release_ex(errstr, 0);
576 }
577 goto out;
578 }
579
580 /* copy the details from the first item */
581 switch ((*psal)->sa_family) {
582 #if HAVE_GETADDRINFO && HAVE_IPV6
583 case AF_INET6:
584 *in6 = **(struct sockaddr_in6**)psal;
585 in6->sin6_port = htons(port);
586 *sl = sizeof(struct sockaddr_in6);
587 ret = SUCCESS;
588 break;
589 #endif
590 case AF_INET:
591 *in4 = **(struct sockaddr_in**)psal;
592 in4->sin_port = htons(port);
593 *sl = sizeof(struct sockaddr_in);
594 ret = SUCCESS;
595 break;
596 }
597
598 php_network_freeaddresses(psal);
599
600 out:
601 efree(tmp);
602 return ret;
603 }
604
605
606 PHPAPI void php_network_populate_name_from_sockaddr(
607 /* input address */
608 struct sockaddr *sa, socklen_t sl,
609 /* output readable address */
610 zend_string **textaddr,
611 /* output address */
612 struct sockaddr **addr,
613 socklen_t *addrlen
614 )
615 {
616 if (addr) {
617 *addr = emalloc(sl);
618 memcpy(*addr, sa, sl);
619 *addrlen = sl;
620 }
621
622 if (textaddr) {
623 #ifdef HAVE_INET_NTOP
624 char abuf[256];
625 #endif
626 const char *buf = NULL;
627
628 switch (sa->sa_family) {
629 case AF_INET:
630 /* generally not thread safe, but it *is* thread safe under win32 */
631 #ifdef HAVE_INET_NTOP
632 buf = inet_ntop(AF_INET, &((struct sockaddr_in*)sa)->sin_addr, (char *)&abuf, sizeof(abuf));
633 #else
634 buf = inet_ntoa(((struct sockaddr_in*)sa)->sin_addr);
635 #endif
636 if (buf) {
637 *textaddr = strpprintf(0, "%s:%d",
638 buf, ntohs(((struct sockaddr_in*)sa)->sin_port));
639 }
640
641 break;
642
643 #if HAVE_IPV6 && HAVE_INET_NTOP
644 case AF_INET6:
645 buf = (char*)inet_ntop(sa->sa_family, &((struct sockaddr_in6*)sa)->sin6_addr, (char *)&abuf, sizeof(abuf));
646 if (buf) {
647 *textaddr = strpprintf(0, "[%s]:%d",
648 buf, ntohs(((struct sockaddr_in6*)sa)->sin6_port));
649 }
650
651 break;
652 #endif
653 #ifdef AF_UNIX
654 case AF_UNIX:
655 {
656 struct sockaddr_un *ua = (struct sockaddr_un*)sa;
657
658 if (ua->sun_path[0] == '\0') {
659 /* abstract name */
660 int len = sl - sizeof(sa_family_t);
661 *textaddr = zend_string_init((char*)ua->sun_path, len, 0);
662 } else {
663 int len = strlen(ua->sun_path);
664 *textaddr = zend_string_init((char*)ua->sun_path, len, 0);
665 }
666 }
667 break;
668 #endif
669
670 }
671
672 }
673 }
674
675 PHPAPI int php_network_get_peer_name(php_socket_t sock,
676 zend_string **textaddr,
677 struct sockaddr **addr,
678 socklen_t *addrlen
679 )
680 {
681 php_sockaddr_storage sa;
682 socklen_t sl = sizeof(sa);
683 memset(&sa, 0, sizeof(sa));
684
685 if (getpeername(sock, (struct sockaddr*)&sa, &sl) == 0) {
686 php_network_populate_name_from_sockaddr((struct sockaddr*)&sa, sl,
687 textaddr,
688 addr, addrlen
689 );
690 return 0;
691 }
692 return -1;
693 }
694
695 PHPAPI int php_network_get_sock_name(php_socket_t sock,
696 zend_string **textaddr,
697 struct sockaddr **addr,
698 socklen_t *addrlen
699 )
700 {
701 php_sockaddr_storage sa;
702 socklen_t sl = sizeof(sa);
703 memset(&sa, 0, sizeof(sa));
704
705 if (getsockname(sock, (struct sockaddr*)&sa, &sl) == 0) {
706 php_network_populate_name_from_sockaddr((struct sockaddr*)&sa, sl,
707 textaddr,
708 addr, addrlen
709 );
710 return 0;
711 }
712 return -1;
713
714 }
715
716
717 /* Accept a client connection from a server socket,
718 * using an optional timeout.
719 * Returns the peer address in addr/addrlen (it will emalloc
720 * these, so be sure to efree the result).
721 * If you specify textaddr, a text-printable
722 * version of the address will be emalloc'd and returned.
723 * */
724
725 /* {{{ php_network_accept_incoming */
726 PHPAPI php_socket_t php_network_accept_incoming(php_socket_t srvsock,
727 zend_string **textaddr,
728 struct sockaddr **addr,
729 socklen_t *addrlen,
730 struct timeval *timeout,
731 zend_string **error_string,
732 int *error_code,
733 int tcp_nodelay
734 )
735 {
736 php_socket_t clisock = -1;
737 int error = 0, n;
738 php_sockaddr_storage sa;
739 socklen_t sl;
740
741 n = php_pollfd_for(srvsock, PHP_POLLREADABLE, timeout);
742
743 if (n == 0) {
744 error = PHP_TIMEOUT_ERROR_VALUE;
745 } else if (n == -1) {
746 error = php_socket_errno();
747 } else {
748 sl = sizeof(sa);
749
750 clisock = accept(srvsock, (struct sockaddr*)&sa, &sl);
751
752 if (clisock != SOCK_ERR) {
753 php_network_populate_name_from_sockaddr((struct sockaddr*)&sa, sl,
754 textaddr,
755 addr, addrlen
756 );
757 if (tcp_nodelay) {
758 #ifdef TCP_NODELAY
759 setsockopt(clisock, IPPROTO_TCP, TCP_NODELAY, (char*)&tcp_nodelay, sizeof(tcp_nodelay));
760 #endif
761 }
762 } else {
763 error = php_socket_errno();
764 }
765 }
766
767 if (error_code) {
768 *error_code = error;
769 }
770 if (error_string) {
771 *error_string = php_socket_error_str(error);
772 }
773
774 return clisock;
775 }
776 /* }}} */
777
778
779 /* Connect to a remote host using an interruptible connect with optional timeout.
780 * Optionally, the connect can be made asynchronously, which will implicitly
781 * enable non-blocking mode on the socket.
782 * Returns the connected (or connecting) socket, or -1 on failure.
783 * */
784
785 /* {{{ php_network_connect_socket_to_host */
786 php_socket_t php_network_connect_socket_to_host(const char *host, unsigned short port,
787 int socktype, int asynchronous, struct timeval *timeout, zend_string **error_string,
788 int *error_code, const char *bindto, unsigned short bindport, long sockopts
789 )
790 {
791 int num_addrs, n, fatal = 0;
792 php_socket_t sock;
793 struct sockaddr **sal, **psal, *sa;
794 struct timeval working_timeout;
795 socklen_t socklen;
796 #if HAVE_GETTIMEOFDAY
797 struct timeval limit_time, time_now;
798 #endif
799
800 num_addrs = php_network_getaddresses(host, socktype, &psal, error_string);
801
802 if (num_addrs == 0) {
803 /* could not resolve address(es) */
804 return -1;
805 }
806
807 if (timeout) {
808 memcpy(&working_timeout, timeout, sizeof(working_timeout));
809 #if HAVE_GETTIMEOFDAY
810 gettimeofday(&limit_time, NULL);
811 limit_time.tv_sec += working_timeout.tv_sec;
812 limit_time.tv_usec += working_timeout.tv_usec;
813 if (limit_time.tv_usec >= 1000000) {
814 limit_time.tv_usec -= 1000000;
815 limit_time.tv_sec++;
816 }
817 #endif
818 }
819
820 for (sal = psal; !fatal && *sal != NULL; sal++) {
821 sa = *sal;
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_port = htons(port);
828 socklen = sizeof(struct sockaddr_in6);
829 } else {
830 /* Expect IPV4 address, skip to the next */
831 continue;
832 }
833 break;
834 #endif
835 case AF_INET:
836 ((struct sockaddr_in *)sa)->sin_port = htons(port);
837 socklen = sizeof(struct sockaddr_in);
838 if (bindto && (strchr(bindto, ':') || !strcmp(bindto, "0"))) {
839 /* IPV4 sock can not bind to IPV6 address */
840 bindto = NULL;
841 }
842 break;
843 default:
844 /* Unsupported family, skip to the next */
845 continue;
846 }
847
848 /* create a socket for this address */
849 sock = socket(sa->sa_family, socktype, 0);
850
851 if (sock == SOCK_ERR) {
852 continue;
853 }
854
855 /* make a connection attempt */
856
857 if (bindto) {
858 union {
859 struct sockaddr common;
860 struct sockaddr_in in4;
861 #if HAVE_IPV6 && HAVE_INET_PTON
862 struct sockaddr_in6 in6;
863 #endif
864 } local_address;
865 int local_address_len = 0;
866
867 if (sa->sa_family == AF_INET) {
868 #ifdef HAVE_INET_PTON
869 if (inet_pton(AF_INET, bindto, &local_address.in4.sin_addr) == 1) {
870 #else
871 if (inet_aton(bindto, &local_address.in4.sin_addr)) {
872 #endif
873 local_address_len = sizeof(struct sockaddr_in);
874 local_address.in4.sin_family = sa->sa_family;
875 local_address.in4.sin_port = htons(bindport);
876 memset(&(local_address.in4.sin_zero), 0, sizeof(local_address.in4.sin_zero));
877 }
878 }
879 #if HAVE_IPV6 && HAVE_INET_PTON
880 else { /* IPV6 */
881 if (inet_pton(AF_INET6, bindto, &local_address.in6.sin6_addr) == 1) {
882 local_address_len = sizeof(struct sockaddr_in6);
883 local_address.in6.sin6_family = sa->sa_family;
884 local_address.in6.sin6_port = htons(bindport);
885 }
886 }
887 #endif
888 if (local_address_len == 0) {
889 php_error_docref(NULL, E_WARNING, "Invalid IP Address: %s", bindto);
890 } else if (bind(sock, &local_address.common, local_address_len)) {
891 php_error_docref(NULL, E_WARNING, "Failed to bind to '%s:%d', system said: %s", bindto, bindport, strerror(errno));
892 }
893 }
894 /* free error string received during previous iteration (if any) */
895 if (error_string && *error_string) {
896 zend_string_release_ex(*error_string, 0);
897 *error_string = NULL;
898 }
899
900 #ifdef SO_BROADCAST
901 {
902 int val = 1;
903 if (sockopts & STREAM_SOCKOP_SO_BROADCAST) {
904 setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char*)&val, sizeof(val));
905 }
906 }
907 #endif
908
909 #ifdef TCP_NODELAY
910 {
911 int val = 1;
912 if (sockopts & STREAM_SOCKOP_TCP_NODELAY) {
913 setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char*)&val, sizeof(val));
914 }
915 }
916 #endif
917 n = php_network_connect_socket(sock, sa, socklen, asynchronous,
918 timeout ? &working_timeout : NULL,
919 error_string, error_code);
920
921 if (n != -1) {
922 goto connected;
923 }
924
925 /* adjust timeout for next attempt */
926 #if HAVE_GETTIMEOFDAY
927 if (timeout) {
928 gettimeofday(&time_now, NULL);
929
930 if (!timercmp(&time_now, &limit_time, <)) {
931 /* time limit expired; don't attempt any further connections */
932 fatal = 1;
933 } else {
934 /* work out remaining time */
935 sub_times(limit_time, time_now, &working_timeout);
936 }
937 }
938 #else
939 if (error_code && *error_code == PHP_TIMEOUT_ERROR_VALUE) {
940 /* Don't even bother trying to connect to the next alternative;
941 * we have no way to determine how long we have already taken
942 * and it is quite likely that the next attempt will fail too. */
943 fatal = 1;
944 } else {
945 /* re-use the same initial timeout.
946 * Not the best thing, but in practice it should be good-enough */
947 if (timeout) {
948 memcpy(&working_timeout, timeout, sizeof(working_timeout));
949 }
950 }
951 #endif
952
953 closesocket(sock);
954 }
955 sock = -1;
956
957 connected:
958
959 php_network_freeaddresses(psal);
960
961 return sock;
962 }
963 /* }}} */
964
965 /* {{{ php_any_addr
966 * Fills the any (wildcard) address into php_sockaddr_storage
967 */
968 PHPAPI void php_any_addr(int family, php_sockaddr_storage *addr, unsigned short port)
969 {
970 memset(addr, 0, sizeof(php_sockaddr_storage));
971 switch (family) {
972 #if HAVE_IPV6
973 case AF_INET6: {
974 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) addr;
975 sin6->sin6_family = AF_INET6;
976 sin6->sin6_port = htons(port);
977 sin6->sin6_addr = in6addr_any;
978 break;
979 }
980 #endif
981 case AF_INET: {
982 struct sockaddr_in *sin = (struct sockaddr_in *) addr;
983 sin->sin_family = AF_INET;
984 sin->sin_port = htons(port);
985 sin->sin_addr.s_addr = htonl(INADDR_ANY);
986 break;
987 }
988 }
989 }
990 /* }}} */
991
992 /* {{{ php_sockaddr_size
993 * Returns the size of struct sockaddr_xx for the family
994 */
995 PHPAPI int php_sockaddr_size(php_sockaddr_storage *addr)
996 {
997 switch (((struct sockaddr *)addr)->sa_family) {
998 case AF_INET:
999 return sizeof(struct sockaddr_in);
1000 #if HAVE_IPV6
1001 case AF_INET6:
1002 return sizeof(struct sockaddr_in6);
1003 #endif
1004 #ifdef AF_UNIX
1005 case AF_UNIX:
1006 return sizeof(struct sockaddr_un);
1007 #endif
1008 default:
1009 return 0;
1010 }
1011 }
1012 /* }}} */
1013
1014 /* Given a socket error code, if buf == NULL:
1015 * emallocs storage for the error message and returns
1016 * else
1017 * sprintf message into provided buffer and returns buf
1018 */
1019 /* {{{ php_socket_strerror */
1020 PHPAPI char *php_socket_strerror(long err, char *buf, size_t bufsize)
1021 {
1022 #ifndef PHP_WIN32
1023 char *errstr;
1024
1025 errstr = strerror(err);
1026 if (buf == NULL) {
1027 buf = estrdup(errstr);
1028 } else {
1029 strncpy(buf, errstr, bufsize);
1030 buf[bufsize?(bufsize-1):0] = 0;
1031 }
1032 return buf;
1033 #else
1034 char *sysbuf = php_win32_error_to_msg(err);
1035 if (!sysbuf[0]) {
1036 sysbuf = "Unknown Error";
1037 }
1038
1039 if (buf == NULL) {
1040 buf = estrdup(sysbuf);
1041 } else {
1042 strncpy(buf, sysbuf, bufsize);
1043 buf[bufsize?(bufsize-1):0] = 0;
1044 }
1045
1046 php_win32_error_msg_free(sysbuf);
1047
1048 return buf;
1049 #endif
1050 }
1051 /* }}} */
1052
1053 /* {{{ php_socket_error_str */
1054 PHPAPI zend_string *php_socket_error_str(long err)
1055 {
1056 #ifndef PHP_WIN32
1057 char *errstr;
1058
1059 errstr = strerror(err);
1060 return zend_string_init(errstr, strlen(errstr), 0);
1061 #else
1062 zend_string *ret;
1063
1064 char *sysbuf = php_win32_error_to_msg(err);
1065 if (!sysbuf[0]) {
1066 sysbuf = "Unknown Error";
1067 }
1068
1069 ret = zend_string_init(sysbuf, strlen(sysbuf), 0);
1070
1071 php_win32_error_msg_free(sysbuf);
1072
1073 return ret;
1074 #endif
1075 }
1076 /* }}} */
1077
1078 /* deprecated */
1079 PHPAPI php_stream *_php_stream_sock_open_from_socket(php_socket_t socket, const char *persistent_id STREAMS_DC)
1080 {
1081 php_stream *stream;
1082 php_netstream_data_t *sock;
1083
1084 sock = pemalloc(sizeof(php_netstream_data_t), persistent_id ? 1 : 0);
1085 memset(sock, 0, sizeof(php_netstream_data_t));
1086
1087 sock->is_blocked = 1;
1088 sock->timeout.tv_sec = FG(default_socket_timeout);
1089 sock->timeout.tv_usec = 0;
1090 sock->socket = socket;
1091
1092 stream = php_stream_alloc_rel(&php_stream_generic_socket_ops, sock, persistent_id, "r+");
1093
1094 if (stream == NULL) {
1095 pefree(sock, persistent_id ? 1 : 0);
1096 } else {
1097 stream->flags |= PHP_STREAM_FLAG_AVOID_BLOCKING;
1098 }
1099
1100 return stream;
1101 }
1102
1103 PHPAPI php_stream *_php_stream_sock_open_host(const char *host, unsigned short port,
1104 int socktype, struct timeval *timeout, const char *persistent_id STREAMS_DC)
1105 {
1106 char *res;
1107 zend_long reslen;
1108 php_stream *stream;
1109
1110 reslen = spprintf(&res, 0, "tcp://%s:%d", host, port);
1111
1112 stream = php_stream_xport_create(res, reslen, REPORT_ERRORS,
1113 STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT, persistent_id, timeout, NULL, NULL, NULL);
1114
1115 efree(res);
1116
1117 return stream;
1118 }
1119
1120 PHPAPI int php_set_sock_blocking(php_socket_t socketd, int block)
1121 {
1122 int ret = SUCCESS;
1123
1124 #ifdef PHP_WIN32
1125 u_long flags;
1126
1127 /* with ioctlsocket, a non-zero sets nonblocking, a zero sets blocking */
1128 flags = !block;
1129 if (ioctlsocket(socketd, FIONBIO, &flags) == SOCKET_ERROR) {
1130 ret = FAILURE;
1131 }
1132 #else
1133 int myflag = 0;
1134 int flags = fcntl(socketd, F_GETFL);
1135
1136 #ifdef O_NONBLOCK
1137 myflag = O_NONBLOCK; /* POSIX version */
1138 #elif defined(O_NDELAY)
1139 myflag = O_NDELAY; /* old non-POSIX version */
1140 #endif
1141 if (!block) {
1142 flags |= myflag;
1143 } else {
1144 flags &= ~myflag;
1145 }
1146 if (fcntl(socketd, F_SETFL, flags) == -1) {
1147 ret = FAILURE;
1148 }
1149 #endif
1150 return ret;
1151 }
1152
1153 PHPAPI void _php_emit_fd_setsize_warning(int max_fd)
1154 {
1155
1156 #ifdef PHP_WIN32
1157 php_error_docref(NULL, E_WARNING,
1158 "PHP needs to be recompiled with a larger value of FD_SETSIZE.\n"
1159 "If this binary is from an official www.php.net package, file a bug report\n"
1160 "at http://bugs.php.net, including the following information:\n"
1161 "FD_SETSIZE=%d, but you are using %d.\n"
1162 " --enable-fd-setsize=%d is recommended, but you may want to set it\n"
1163 "to match to maximum number of sockets each script will work with at\n"
1164 "one time, in order to avoid seeing this error again at a later date.",
1165 FD_SETSIZE, max_fd, (max_fd + 128) & ~127);
1166 #else
1167 php_error_docref(NULL, E_WARNING,
1168 "You MUST recompile PHP with a larger value of FD_SETSIZE.\n"
1169 "It is set to %d, but you have descriptors numbered at least as high as %d.\n"
1170 " --enable-fd-setsize=%d is recommended, but you may want to set it\n"
1171 "to equal the maximum number of open files supported by your system,\n"
1172 "in order to avoid seeing this error again at a later date.",
1173 FD_SETSIZE, max_fd, (max_fd + 1024) & ~1023);
1174 #endif
1175 }
1176
1177 #if defined(PHP_USE_POLL_2_EMULATION)
1178
1179 /* emulate poll(2) using select(2), safely. */
1180
1181 PHPAPI int php_poll2(php_pollfd *ufds, unsigned int nfds, int timeout)
1182 {
1183 fd_set rset, wset, eset;
1184 php_socket_t max_fd = SOCK_ERR; /* effectively unused on Windows */
1185 unsigned int i;
1186 int n;
1187 struct timeval tv;
1188
1189 #ifndef PHP_WIN32
1190 /* check the highest numbered descriptor */
1191 for (i = 0; i < nfds; i++) {
1192 if (ufds[i].fd > max_fd)
1193 max_fd = ufds[i].fd;
1194 }
1195 #endif
1196
1197 if (!PHP_SAFE_MAX_FD(max_fd, nfds + 1)) {
1198 #ifdef PHP_WIN32
1199 WSASetLastError(WSAEINVAL);
1200 #else
1201 errno = ERANGE;
1202 #endif
1203 return -1;
1204 }
1205
1206 FD_ZERO(&rset);
1207 FD_ZERO(&wset);
1208 FD_ZERO(&eset);
1209
1210 for (i = 0; i < nfds; i++) {
1211 if (ufds[i].events & PHP_POLLREADABLE) {
1212 PHP_SAFE_FD_SET(ufds[i].fd, &rset);
1213 }
1214 if (ufds[i].events & POLLOUT) {
1215 PHP_SAFE_FD_SET(ufds[i].fd, &wset);
1216 }
1217 if (ufds[i].events & POLLPRI) {
1218 PHP_SAFE_FD_SET(ufds[i].fd, &eset);
1219 }
1220 }
1221
1222 if (timeout >= 0) {
1223 tv.tv_sec = timeout / 1000;
1224 tv.tv_usec = (timeout - (tv.tv_sec * 1000)) * 1000;
1225 }
1226 /* Resetting/initializing */
1227 #ifdef PHP_WIN32
1228 WSASetLastError(0);
1229 #else
1230 errno = 0;
1231 #endif
1232 n = select(max_fd + 1, &rset, &wset, &eset, timeout >= 0 ? &tv : NULL);
1233
1234 if (n >= 0) {
1235 for (i = 0; i < nfds; i++) {
1236 ufds[i].revents = 0;
1237
1238 if (PHP_SAFE_FD_ISSET(ufds[i].fd, &rset)) {
1239 /* could be POLLERR or POLLHUP but can't tell without probing */
1240 ufds[i].revents |= POLLIN;
1241 }
1242 if (PHP_SAFE_FD_ISSET(ufds[i].fd, &wset)) {
1243 ufds[i].revents |= POLLOUT;
1244 }
1245 if (PHP_SAFE_FD_ISSET(ufds[i].fd, &eset)) {
1246 ufds[i].revents |= POLLPRI;
1247 }
1248 }
1249 }
1250 return n;
1251 }
1252 #endif
1253
1254 #if defined(HAVE_GETHOSTBYNAME_R)
1255 #ifdef HAVE_FUNC_GETHOSTBYNAME_R_6
1256 struct hostent * gethostname_re (const char *host,struct hostent *hostbuf,char **tmphstbuf,size_t *hstbuflen)
1257 {
1258 struct hostent *hp;
1259 int herr,res;
1260
1261 if (*hstbuflen == 0) {
1262 *hstbuflen = 1024;
1263 *tmphstbuf = (char *)malloc (*hstbuflen);
1264 }
1265
1266 while (( res =
1267 gethostbyname_r(host,hostbuf,*tmphstbuf,*hstbuflen,&hp,&herr))
1268 && (errno == ERANGE)) {
1269 /* Enlarge the buffer. */
1270 *hstbuflen *= 2;
1271 *tmphstbuf = (char *)realloc (*tmphstbuf,*hstbuflen);
1272 }
1273
1274 if (res != SUCCESS) {
1275 return NULL;
1276 }
1277
1278 return hp;
1279 }
1280 #endif
1281 #ifdef HAVE_FUNC_GETHOSTBYNAME_R_5
1282 struct hostent * gethostname_re (const char *host,struct hostent *hostbuf,char **tmphstbuf,size_t *hstbuflen)
1283 {
1284 struct hostent *hp;
1285 int herr;
1286
1287 if (*hstbuflen == 0) {
1288 *hstbuflen = 1024;
1289 *tmphstbuf = (char *)malloc (*hstbuflen);
1290 }
1291
1292 while ((NULL == ( hp =
1293 gethostbyname_r(host,hostbuf,*tmphstbuf,*hstbuflen,&herr)))
1294 && (errno == ERANGE)) {
1295 /* Enlarge the buffer. */
1296 *hstbuflen *= 2;
1297 *tmphstbuf = (char *)realloc (*tmphstbuf,*hstbuflen);
1298 }
1299 return hp;
1300 }
1301 #endif
1302 #ifdef HAVE_FUNC_GETHOSTBYNAME_R_3
1303 struct hostent * gethostname_re (const char *host,struct hostent *hostbuf,char **tmphstbuf,size_t *hstbuflen)
1304 {
1305 if (*hstbuflen == 0) {
1306 *hstbuflen = sizeof(struct hostent_data);
1307 *tmphstbuf = (char *)malloc (*hstbuflen);
1308 } else {
1309 if (*hstbuflen < sizeof(struct hostent_data)) {
1310 *hstbuflen = sizeof(struct hostent_data);
1311 *tmphstbuf = (char *)realloc(*tmphstbuf, *hstbuflen);
1312 }
1313 }
1314 memset((void *)(*tmphstbuf),0,*hstbuflen);
1315
1316 if (SUCCESS != gethostbyname_r(host,hostbuf,(struct hostent_data *)*tmphstbuf)) {
1317 return NULL;
1318 }
1319
1320 return hostbuf;
1321 }
1322 #endif
1323 #endif
1324
1325 PHPAPI struct hostent* php_network_gethostbyname(const char *name) {
1326 #if !defined(HAVE_GETHOSTBYNAME_R)
1327 return gethostbyname(name);
1328 #else
1329 if (FG(tmp_host_buf)) {
1330 free(FG(tmp_host_buf));
1331 }
1332
1333 FG(tmp_host_buf) = NULL;
1334 FG(tmp_host_buf_len) = 0;
1335
1336 memset(&FG(tmp_host_info), 0, sizeof(struct hostent));
1337
1338 return gethostname_re(name, &FG(tmp_host_info), &FG(tmp_host_buf), &FG(tmp_host_buf_len));
1339 #endif
1340 }
1341