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