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