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