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