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