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