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