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