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