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