Annotation of embedaddon/php/main/network.c, revision 1.1.1.1
1.1 misho 1: /*
2: +----------------------------------------------------------------------+
3: | PHP Version 5 |
4: +----------------------------------------------------------------------+
5: | Copyright (c) 1997-2012 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: network.c 321634 2012-01-01 13:15:04Z felipe $ */
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: */
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: */
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: */
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 */
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: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>