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