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