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