xref: /php-src/ext/sockets/sockets.c (revision c4bb6e6c)
1 /*
2    +----------------------------------------------------------------------+
3    | Copyright (c) The PHP Group                                          |
4    +----------------------------------------------------------------------+
5    | This source file is subject to version 3.01 of the PHP license,      |
6    | that is bundled with this package in the file LICENSE, and is        |
7    | available through the world-wide-web at the following url:           |
8    | https://www.php.net/license/3_01.txt                                 |
9    | If you did not receive a copy of the PHP license and are unable to   |
10    | obtain it through the world-wide-web, please send a note to          |
11    | license@php.net so we can mail you a copy immediately.               |
12    +----------------------------------------------------------------------+
13    | Authors: Chris Vandomelen <chrisv@b0rked.dhs.org>                    |
14    |          Sterling Hughes  <sterling@php.net>                         |
15    |          Jason Greene     <jason@php.net>                            |
16    |          Gustavo Lopes    <cataphract@php.net>                       |
17    | WinSock: Daniel Beulshausen <daniel@php4win.de>                      |
18    +----------------------------------------------------------------------+
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 
25 #include "php.h"
26 
27 #include "php_network.h"
28 #include "ext/standard/file.h"
29 #include "ext/standard/info.h"
30 #include "php_ini.h"
31 #ifdef PHP_WIN32
32 # include "windows_common.h"
33 # include <windows.h>
34 # include <Ws2tcpip.h>
35 # include "php_sockets.h"
36 # include <win32/sockets.h>
37 # include <win32/winutil.h>
38 #else
39 # include <sys/types.h>
40 # include <sys/socket.h>
41 # include <netdb.h>
42 # include <netinet/in.h>
43 # include <netinet/tcp.h>
44 # include <sys/un.h>
45 # include <arpa/inet.h>
46 # include <sys/time.h>
47 # include <unistd.h>
48 # include <errno.h>
49 # include <fcntl.h>
50 # include <signal.h>
51 # include <sys/uio.h>
52 # define set_errno(a) (errno = a)
53 # include "php_sockets.h"
54 # ifdef HAVE_IF_NAMETOINDEX
55 #  include <net/if.h>
56 # endif
57 # if defined(HAVE_LINUX_SOCK_DIAG_H)
58 #  include <linux/sock_diag.h>
59 # else
60 #  undef SO_MEMINFO
61 # endif
62 # if defined(HAVE_LINUX_FILTER_H)
63 #  include <linux/filter.h>
64 # else
65 #  undef SO_BPF_EXTENSIONS
66 # endif
67 #endif
68 
69 #include <stddef.h>
70 
71 #include "sockaddr_conv.h"
72 #include "multicast.h"
73 #include "sendrecvmsg.h"
74 #include "sockets_arginfo.h"
75 
76 ZEND_DECLARE_MODULE_GLOBALS(sockets)
77 
78 #define SUN_LEN_NO_UB(su) (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
79 /* The SUN_LEN macro does pointer arithmetics on NULL which triggers errors in the Clang UBSAN build */
80 #ifdef __has_feature
81 # if __has_feature(undefined_behavior_sanitizer)
82 #  undef SUN_LEN
83 #  define SUN_LEN(su) SUN_LEN_NO_UB(su)
84 # endif
85 #endif
86 #ifndef SUN_LEN
87 # define SUN_LEN(su) SUN_LEN_NO_UB(su)
88 #endif
89 
90 #ifndef PF_INET
91 #define PF_INET AF_INET
92 #endif
93 
94 static PHP_GINIT_FUNCTION(sockets);
95 static PHP_GSHUTDOWN_FUNCTION(sockets);
96 static PHP_MINIT_FUNCTION(sockets);
97 static PHP_MSHUTDOWN_FUNCTION(sockets);
98 static PHP_MINFO_FUNCTION(sockets);
99 static PHP_RSHUTDOWN_FUNCTION(sockets);
100 
101 /* Socket class */
102 
103 zend_class_entry *socket_ce;
104 static zend_object_handlers socket_object_handlers;
105 
socket_create_object(zend_class_entry * class_type)106 static zend_object *socket_create_object(zend_class_entry *class_type) {
107 	php_socket *intern = zend_object_alloc(sizeof(php_socket), class_type);
108 
109 	zend_object_std_init(&intern->std, class_type);
110 	object_properties_init(&intern->std, class_type);
111 
112 	intern->bsd_socket = -1; /* invalid socket */
113 	intern->type		 = PF_UNSPEC;
114 	intern->error		 = 0;
115 	intern->blocking	 = 1;
116 	ZVAL_UNDEF(&intern->zstream);
117 
118 	return &intern->std;
119 }
120 
socket_get_constructor(zend_object * object)121 static zend_function *socket_get_constructor(zend_object *object) {
122 	zend_throw_error(NULL, "Cannot directly construct Socket, use socket_create() instead");
123 	return NULL;
124 }
125 
socket_free_obj(zend_object * object)126 static void socket_free_obj(zend_object *object)
127 {
128 	php_socket *socket = socket_from_obj(object);
129 
130 	if (Z_ISUNDEF(socket->zstream)) {
131 		if (!IS_INVALID_SOCKET(socket)) {
132 			close(socket->bsd_socket);
133 		}
134 	} else {
135 		zval_ptr_dtor(&socket->zstream);
136 	}
137 
138 	zend_object_std_dtor(&socket->std);
139 }
140 
socket_get_gc(zend_object * object,zval ** table,int * n)141 static HashTable *socket_get_gc(zend_object *object, zval **table, int *n)
142 {
143 	php_socket *socket = socket_from_obj(object);
144 
145 	*table = &socket->zstream;
146 	*n = 1;
147 
148 	return zend_std_get_properties(object);
149 }
150 
151 /* AddressInfo class */
152 
153 typedef struct {
154 	struct addrinfo addrinfo;
155 	zend_object std;
156 } php_addrinfo;
157 
158 zend_class_entry *address_info_ce;
159 static zend_object_handlers address_info_object_handlers;
160 
address_info_from_obj(zend_object * obj)161 static inline php_addrinfo *address_info_from_obj(zend_object *obj) {
162 	return (php_addrinfo *)((char *)(obj) - XtOffsetOf(php_addrinfo, std));
163 }
164 
165 #define Z_ADDRESS_INFO_P(zv) address_info_from_obj(Z_OBJ_P(zv))
166 
address_info_create_object(zend_class_entry * class_type)167 static zend_object *address_info_create_object(zend_class_entry *class_type) {
168 	php_addrinfo *intern = zend_object_alloc(sizeof(php_addrinfo), class_type);
169 
170 	zend_object_std_init(&intern->std, class_type);
171 	object_properties_init(&intern->std, class_type);
172 
173 	return &intern->std;
174 }
175 
address_info_get_constructor(zend_object * object)176 static zend_function *address_info_get_constructor(zend_object *object) {
177 	zend_throw_error(NULL, "Cannot directly construct AddressInfo, use socket_addrinfo_lookup() instead");
178 	return NULL;
179 }
180 
address_info_free_obj(zend_object * object)181 static void address_info_free_obj(zend_object *object)
182 {
183 	php_addrinfo *address_info = address_info_from_obj(object);
184 
185 	if (address_info->addrinfo.ai_canonname != NULL) {
186 		efree(address_info->addrinfo.ai_canonname);
187 	}
188 	efree(address_info->addrinfo.ai_addr);
189 
190 	zend_object_std_dtor(&address_info->std);
191 }
192 
193 /* Module registration */
194 
195 zend_module_entry sockets_module_entry = {
196 	STANDARD_MODULE_HEADER,
197 	"sockets",
198 	ext_functions,
199 	PHP_MINIT(sockets),
200 	PHP_MSHUTDOWN(sockets),
201 	NULL,
202 	PHP_RSHUTDOWN(sockets),
203 	PHP_MINFO(sockets),
204 	PHP_SOCKETS_VERSION,
205 	PHP_MODULE_GLOBALS(sockets),
206 	PHP_GINIT(sockets),
207 	PHP_GSHUTDOWN(sockets),
208 	NULL,
209 	STANDARD_MODULE_PROPERTIES_EX
210 };
211 
212 
213 #ifdef COMPILE_DL_SOCKETS
214 #ifdef ZTS
215 	ZEND_TSRMLS_CACHE_DEFINE()
216 #endif
ZEND_GET_MODULE(sockets)217 ZEND_GET_MODULE(sockets)
218 #endif
219 
220 static bool php_open_listen_sock(php_socket *sock, int port, int backlog) /* {{{ */
221 {
222 	struct sockaddr_in  la = {0};
223 	struct hostent	    *hp;
224 
225 #ifndef PHP_WIN32
226 	if ((hp = php_network_gethostbyname("0.0.0.0")) == NULL) {
227 #else
228 	if ((hp = php_network_gethostbyname("localhost")) == NULL) {
229 #endif
230 		return 0;
231 	}
232 
233 	memcpy((char *) &la.sin_addr, hp->h_addr, hp->h_length);
234 	la.sin_family = hp->h_addrtype;
235 	la.sin_port = htons((unsigned short) port);
236 
237 	sock->bsd_socket = socket(PF_INET, SOCK_STREAM, 0);
238 	sock->blocking = 1;
239 
240 	if (IS_INVALID_SOCKET(sock)) {
241 		PHP_SOCKET_ERROR(sock, "unable to create listening socket", errno);
242 		return 0;
243 	}
244 
245 	sock->type = PF_INET;
246 
247 	if (bind(sock->bsd_socket, (struct sockaddr *)&la, sizeof(la)) != 0) {
248 		PHP_SOCKET_ERROR(sock, "unable to bind to given address", errno);
249 		close(sock->bsd_socket);
250 		return 0;
251 	}
252 
253 	if (listen(sock->bsd_socket, backlog) != 0) {
254 		PHP_SOCKET_ERROR(sock, "unable to listen on socket", errno);
255 		close(sock->bsd_socket);
256 		return 0;
257 	}
258 
259 	return 1;
260 }
261 /* }}} */
262 
263 static bool php_accept_connect(php_socket *in_sock, php_socket *out_sock, struct sockaddr *la, socklen_t *la_len) /* {{{ */
264 {
265 	out_sock->bsd_socket = accept(in_sock->bsd_socket, la, la_len);
266 
267 	if (IS_INVALID_SOCKET(out_sock)) {
268 		PHP_SOCKET_ERROR(out_sock, "unable to accept incoming connection", errno);
269 		return 0;
270 	}
271 
272 #if !defined(PHP_WIN32)
273 	/**
274 	 * accept4 could had been used but not all platforms support it (e.g. Haiku, solaris < 11.4, ...)
275 	 * win32, not having any concept of child process, has no need to address it.
276 	 */
277 	int mode;
278 
279 	if ((mode = fcntl(out_sock->bsd_socket, F_GETFD)) < 0) {
280 		PHP_SOCKET_ERROR(out_sock, "unable to get fcntl mode on the socket", errno);
281 		return 0;
282 	}
283 
284 	int cloexec = (mode | FD_CLOEXEC);
285 
286 	if (mode != cloexec) {
287 		if (fcntl(out_sock->bsd_socket, F_SETFD, cloexec) < 0) {
288 			PHP_SOCKET_ERROR(out_sock, "unable to set cloexec mode on the socket", errno);
289 			return 0;
290 		}
291 	}
292 #endif
293 
294 	out_sock->error = 0;
295 	out_sock->blocking = 1;
296 	out_sock->type = la->sa_family;
297 
298 	return 1;
299 }
300 /* }}} */
301 
302 /* {{{ php_read -- wrapper around read() so that it only reads to a \r or \n. */
303 static int php_read(php_socket *sock, void *buf, size_t maxlen, int flags)
304 {
305 	int m = 0;
306 	size_t n = 0;
307 	int no_read = 0;
308 	int nonblock = 0;
309 	char *t = (char *) buf;
310 
311 #ifndef PHP_WIN32
312 	m = fcntl(sock->bsd_socket, F_GETFL);
313 	if (m < 0) {
314 		return m;
315 	}
316 	nonblock = (m & O_NONBLOCK);
317 	m = 0;
318 #else
319 	nonblock = !sock->blocking;
320 #endif
321 	set_errno(0);
322 
323 	*t = '\0';
324 	while (*t != '\n' && *t != '\r' && n < maxlen) {
325 		if (m > 0) {
326 			t++;
327 			n++;
328 		} else if (m == 0) {
329 			no_read++;
330 			if (nonblock && no_read >= 2) {
331 				return n;
332 				/* The first pass, m always is 0, so no_read becomes 1
333 				 * in the first pass. no_read becomes 2 in the second pass,
334 				 * and if this is nonblocking, we should return.. */
335 			}
336 
337 			if (no_read > 200) {
338 				set_errno(ECONNRESET);
339 				return -1;
340 			}
341 		}
342 
343 		if (n < maxlen) {
344 			m = recv(sock->bsd_socket, (void *) t, 1, flags);
345 		}
346 
347 		if (errno != 0 && errno != ESPIPE && errno != EAGAIN) {
348 			return -1;
349 		}
350 
351 		set_errno(0);
352 	}
353 
354 	if (n < maxlen) {
355 		n++;
356 		/* The only reasons it makes it to here is
357 		 * if '\n' or '\r' are encountered. So, increase
358 		 * the return by 1 to make up for the lack of the
359 		 * '\n' or '\r' in the count (since read() takes
360 		 * place at the end of the loop..) */
361 	}
362 
363 	return n;
364 }
365 /* }}} */
366 
367 char *sockets_strerror(int error) /* {{{ */
368 {
369 	const char *buf;
370 
371 #ifndef PHP_WIN32
372 	if (error < -10000) {
373 		if (error == INT_MIN) {
374 			error = 2147473648;
375 		} else {
376 			error = -error - 10000;
377 		}
378 
379 #ifdef HAVE_HSTRERROR
380 		buf = hstrerror(error);
381 #else
382 		{
383 			if (SOCKETS_G(strerror_buf)) {
384 				efree(SOCKETS_G(strerror_buf));
385 			}
386 
387 			spprintf(&(SOCKETS_G(strerror_buf)), 0, "Host lookup error %d", error);
388 			buf = SOCKETS_G(strerror_buf);
389 		}
390 #endif
391 	} else {
392 		buf = strerror(error);
393 	}
394 #else
395 	{
396 		char *tmp = php_win32_error_to_msg(error);
397 		buf = NULL;
398 
399 		if (tmp[0]) {
400 			if (SOCKETS_G(strerror_buf)) {
401 				efree(SOCKETS_G(strerror_buf));
402 			}
403 
404 			SOCKETS_G(strerror_buf) = estrdup(tmp);
405 			free(tmp);
406 
407 			buf = SOCKETS_G(strerror_buf);
408 		}
409 	}
410 #endif
411 
412 	return (buf ? (char *) buf : "");
413 }
414 /* }}} */
415 
416 #ifdef PHP_WIN32
417 static void sockets_destroy_wsa_info(zval *data)
418 {/*{{{*/
419 	HANDLE h = (HANDLE)Z_PTR_P(data);
420 	CloseHandle(h);
421 }/*}}}*/
422 #endif
423 
424 /* {{{ PHP_GINIT_FUNCTION */
425 static PHP_GINIT_FUNCTION(sockets)
426 {
427 #if defined(COMPILE_DL_SOCKETS) && defined(ZTS)
428 	ZEND_TSRMLS_CACHE_UPDATE();
429 #endif
430 	sockets_globals->last_error = 0;
431 	sockets_globals->strerror_buf = NULL;
432 #ifdef PHP_WIN32
433 	sockets_globals->wsa_child_count = 0;
434 	zend_hash_init(&sockets_globals->wsa_info, 0, NULL, sockets_destroy_wsa_info, 1);
435 #endif
436 }
437 /* }}} */
438 
439 /* {{{ PHP_GSHUTDOWN_FUNCTION */
440 static PHP_GSHUTDOWN_FUNCTION(sockets)
441 {
442 #ifdef PHP_WIN32
443 	zend_hash_destroy(&sockets_globals->wsa_info);
444 #endif
445 }
446 /* }}} */
447 
448 /* {{{ PHP_MINIT_FUNCTION */
449 static PHP_MINIT_FUNCTION(sockets)
450 {
451 #if defined(COMPILE_DL_SOCKETS) && defined(ZTS)
452 	ZEND_TSRMLS_CACHE_UPDATE();
453 #endif
454 
455 	socket_ce = register_class_Socket();
456 	socket_ce->create_object = socket_create_object;
457 	socket_ce->default_object_handlers = &socket_object_handlers;
458 
459 	memcpy(&socket_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
460 	socket_object_handlers.offset = XtOffsetOf(php_socket, std);
461 	socket_object_handlers.free_obj = socket_free_obj;
462 	socket_object_handlers.get_constructor = socket_get_constructor;
463 	socket_object_handlers.clone_obj = NULL;
464 	socket_object_handlers.get_gc = socket_get_gc;
465 	socket_object_handlers.compare = zend_objects_not_comparable;
466 
467 	address_info_ce = register_class_AddressInfo();
468 	address_info_ce->create_object = address_info_create_object;
469 	address_info_ce->default_object_handlers = &address_info_object_handlers;
470 
471 	memcpy(&address_info_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
472 	address_info_object_handlers.offset = XtOffsetOf(php_addrinfo, std);
473 	address_info_object_handlers.free_obj = address_info_free_obj;
474 	address_info_object_handlers.get_constructor = address_info_get_constructor;
475 	address_info_object_handlers.clone_obj = NULL;
476 	address_info_object_handlers.compare = zend_objects_not_comparable;
477 
478 	register_sockets_symbols(module_number);
479 
480 	php_socket_sendrecvmsg_init(INIT_FUNC_ARGS_PASSTHRU);
481 
482 	return SUCCESS;
483 }
484 /* }}} */
485 
486 /* {{{ PHP_MSHUTDOWN_FUNCTION */
487 static PHP_MSHUTDOWN_FUNCTION(sockets)
488 {
489 	php_socket_sendrecvmsg_shutdown(SHUTDOWN_FUNC_ARGS_PASSTHRU);
490 
491 	return SUCCESS;
492 }
493 /* }}} */
494 
495 /* {{{ PHP_MINFO_FUNCTION */
496 static PHP_MINFO_FUNCTION(sockets)
497 {
498 	php_info_print_table_start();
499 	php_info_print_table_row(2, "Sockets Support", "enabled");
500 	php_info_print_table_end();
501 }
502 /* }}} */
503 
504 /* {{{ PHP_RSHUTDOWN_FUNCTION */
505 static PHP_RSHUTDOWN_FUNCTION(sockets)
506 {
507 	if (SOCKETS_G(strerror_buf)) {
508 		efree(SOCKETS_G(strerror_buf));
509 		SOCKETS_G(strerror_buf) = NULL;
510 	}
511 
512 	return SUCCESS;
513 }
514 /* }}} */
515 
516 static int php_sock_array_to_fd_set(uint32_t arg_num, zval *sock_array, fd_set *fds, PHP_SOCKET *max_fd) /* {{{ */
517 {
518 	zval		*element;
519 	php_socket	*php_sock;
520 	int			num = 0;
521 
522 	if (Z_TYPE_P(sock_array) != IS_ARRAY) return 0;
523 
524 	ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(sock_array), element) {
525 		ZVAL_DEREF(element);
526 
527 		if (Z_TYPE_P(element) != IS_OBJECT || Z_OBJCE_P(element) != socket_ce) {
528 			zend_argument_type_error(arg_num, "must only have elements of type Socket, %s given", zend_zval_value_name(element));
529 			return -1;
530 		}
531 
532 		php_sock = Z_SOCKET_P(element);
533 		if (IS_INVALID_SOCKET(php_sock)) {
534 			zend_argument_type_error(arg_num, "contains a closed socket");
535 			return -1;
536 		}
537 
538 		PHP_SAFE_FD_SET(php_sock->bsd_socket, fds);
539 		if (php_sock->bsd_socket > *max_fd) {
540 			*max_fd = php_sock->bsd_socket;
541 		}
542 		num++;
543 	} ZEND_HASH_FOREACH_END();
544 
545 	return num ? 1 : 0;
546 }
547 /* }}} */
548 
549 static void php_sock_array_from_fd_set(zval *sock_array, fd_set *fds) /* {{{ */
550 {
551 	zval		*element;
552 	zval		*dest_element;
553 	php_socket	*php_sock;
554 	zval		new_hash;
555 	zend_ulong  num_key;
556 	zend_string *key;
557 
558 	ZEND_ASSERT(Z_TYPE_P(sock_array) == IS_ARRAY);
559 
560 	array_init(&new_hash);
561 	ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(sock_array), num_key, key, element) {
562 		ZVAL_DEREF(element);
563 
564 		php_sock = Z_SOCKET_P(element);
565 		ZEND_ASSERT(php_sock); /* element is supposed to be Socket object */
566 		ZEND_ASSERT(!IS_INVALID_SOCKET(php_sock));
567 
568 		if (PHP_SAFE_FD_ISSET(php_sock->bsd_socket, fds)) {
569 			/* Add fd to new array */
570 			if (key) {
571 				dest_element = zend_hash_add(Z_ARRVAL(new_hash), key, element);
572 			} else {
573 				dest_element = zend_hash_index_update(Z_ARRVAL(new_hash), num_key, element);
574 			}
575 			if (dest_element) {
576 				Z_ADDREF_P(dest_element);
577 			}
578 		}
579 	} ZEND_HASH_FOREACH_END();
580 
581 	/* Destroy old array, add new one */
582 	zval_ptr_dtor(sock_array);
583 
584 	ZVAL_COPY_VALUE(sock_array, &new_hash);
585 }
586 /* }}} */
587 
588 /* {{{ Runs the select() system call on the sets mentioned with a timeout specified by tv_sec and tv_usec */
589 PHP_FUNCTION(socket_select)
590 {
591 	zval			*r_array, *w_array, *e_array;
592 	struct timeval	tv;
593 	struct timeval *tv_p = NULL;
594 	fd_set			rfds, wfds, efds;
595 	PHP_SOCKET		max_fd = 0;
596 	int				retval, sets = 0;
597 	zend_long		sec, usec = 0;
598 	bool		sec_is_null = 0;
599 
600 	ZEND_PARSE_PARAMETERS_START(4, 5)
601 		Z_PARAM_ARRAY_EX2(r_array, 1, 1, 0)
602 		Z_PARAM_ARRAY_EX2(w_array, 1, 1, 0)
603 		Z_PARAM_ARRAY_EX2(e_array, 1, 1, 0)
604 		Z_PARAM_LONG_OR_NULL(sec, sec_is_null)
605 		Z_PARAM_OPTIONAL
606 		Z_PARAM_LONG(usec)
607 	ZEND_PARSE_PARAMETERS_END();
608 
609 	FD_ZERO(&rfds);
610 	FD_ZERO(&wfds);
611 	FD_ZERO(&efds);
612 
613 	if (r_array != NULL) {
614 		sets += retval = php_sock_array_to_fd_set(1, r_array, &rfds, &max_fd);
615 		if (retval == -1) {
616 			RETURN_THROWS();
617 		}
618 	}
619 	if (w_array != NULL) {
620 		sets += retval = php_sock_array_to_fd_set(2, w_array, &wfds, &max_fd);
621 		if (retval == -1) {
622 			RETURN_THROWS();
623 		}
624 	}
625 	if (e_array != NULL) {
626 		sets += retval = php_sock_array_to_fd_set(3, e_array, &efds, &max_fd);
627 		if (retval == -1) {
628 			RETURN_THROWS();
629 		}
630 	}
631 
632 	if (!sets) {
633 		zend_value_error("socket_select(): At least one array argument must be passed");
634 		RETURN_THROWS();
635 	}
636 
637 	if (!PHP_SAFE_MAX_FD(max_fd, 0)) {
638 		RETURN_FALSE;
639 	}
640 
641 	/* If seconds is not set to null, build the timeval, else we wait indefinitely */
642 	if (!sec_is_null) {
643 		/* Solaris + BSD do not like microsecond values which are >= 1 sec */
644 		if (usec > 999999) {
645 			tv.tv_sec = sec + (usec / 1000000);
646 			tv.tv_usec = usec % 1000000;
647 		} else {
648 			tv.tv_sec = sec;
649 			tv.tv_usec = usec;
650 		}
651 
652 		tv_p = &tv;
653 	}
654 
655 	retval = select(max_fd+1, &rfds, &wfds, &efds, tv_p);
656 
657 	if (retval == -1) {
658 		SOCKETS_G(last_error) = errno;
659 		php_error_docref(NULL, E_WARNING, "Unable to select [%d]: %s", errno, sockets_strerror(errno));
660 		RETURN_FALSE;
661 	}
662 
663 	if (r_array != NULL) php_sock_array_from_fd_set(r_array, &rfds);
664 	if (w_array != NULL) php_sock_array_from_fd_set(w_array, &wfds);
665 	if (e_array != NULL) php_sock_array_from_fd_set(e_array, &efds);
666 
667 	RETURN_LONG(retval);
668 }
669 /* }}} */
670 
671 /* {{{ Opens a socket on port to accept connections */
672 PHP_FUNCTION(socket_create_listen)
673 {
674 	php_socket	*php_sock;
675 	zend_long	port, backlog = SOMAXCONN;
676 
677 	ZEND_PARSE_PARAMETERS_START(1, 2)
678 		Z_PARAM_LONG(port)
679 		Z_PARAM_OPTIONAL
680 		Z_PARAM_LONG(backlog)
681 	ZEND_PARSE_PARAMETERS_END();
682 
683 	object_init_ex(return_value, socket_ce);
684 	php_sock = Z_SOCKET_P(return_value);
685 
686 	if (!php_open_listen_sock(php_sock, port, backlog)) {
687 		zval_ptr_dtor(return_value);
688 		RETURN_FALSE;
689 	}
690 
691 	php_sock->error = 0;
692 	php_sock->blocking = 1;
693 }
694 /* }}} */
695 
696 /* {{{ Accepts a connection on the listening socket fd */
697 PHP_FUNCTION(socket_accept)
698 {
699 	zval			 *arg1;
700 	php_socket			 *php_sock, *new_sock;
701 	php_sockaddr_storage sa;
702 	socklen_t			 php_sa_len = sizeof(sa);
703 
704 	ZEND_PARSE_PARAMETERS_START(1, 1)
705 		Z_PARAM_OBJECT_OF_CLASS(arg1, socket_ce)
706 	ZEND_PARSE_PARAMETERS_END();
707 
708 	php_sock = Z_SOCKET_P(arg1);
709 	ENSURE_SOCKET_VALID(php_sock);
710 
711 	object_init_ex(return_value, socket_ce);
712 	new_sock = Z_SOCKET_P(return_value);
713 
714 	if (!php_accept_connect(php_sock, new_sock, (struct sockaddr*)&sa, &php_sa_len)) {
715 		zval_ptr_dtor(return_value);
716 		RETURN_FALSE;
717 	}
718 }
719 /* }}} */
720 
721 /* {{{ Sets nonblocking mode on a socket resource */
722 PHP_FUNCTION(socket_set_nonblock)
723 {
724 	zval		*arg1;
725 	php_socket	*php_sock;
726 
727 	ZEND_PARSE_PARAMETERS_START(1, 1)
728 		Z_PARAM_OBJECT_OF_CLASS(arg1, socket_ce)
729 	ZEND_PARSE_PARAMETERS_END();
730 
731 	php_sock = Z_SOCKET_P(arg1);
732 	ENSURE_SOCKET_VALID(php_sock);
733 
734 	if (!Z_ISUNDEF(php_sock->zstream)) {
735 		php_stream *stream;
736 		/* omit notice if resource doesn't exist anymore */
737 		stream = zend_fetch_resource2_ex(&php_sock->zstream, NULL, php_file_le_stream(), php_file_le_pstream());
738 		if (stream != NULL) {
739 			if (php_stream_set_option(stream, PHP_STREAM_OPTION_BLOCKING, 0,
740 					NULL) != -1) {
741 				php_sock->blocking = 0;
742 				RETURN_TRUE;
743 			}
744 		}
745 	}
746 
747 	if (php_set_sock_blocking(php_sock->bsd_socket, 0) == SUCCESS) {
748 		php_sock->blocking = 0;
749 		RETURN_TRUE;
750 	} else {
751 		PHP_SOCKET_ERROR(php_sock, "unable to set nonblocking mode", errno);
752 		RETURN_FALSE;
753 	}
754 }
755 /* }}} */
756 
757 /* {{{ Sets blocking mode on a socket resource */
758 PHP_FUNCTION(socket_set_block)
759 {
760 	zval		*arg1;
761 	php_socket	*php_sock;
762 
763 	ZEND_PARSE_PARAMETERS_START(1, 1)
764 		Z_PARAM_OBJECT_OF_CLASS(arg1, socket_ce)
765 	ZEND_PARSE_PARAMETERS_END();
766 
767 	php_sock = Z_SOCKET_P(arg1);
768 	ENSURE_SOCKET_VALID(php_sock);
769 
770 	/* if socket was created from a stream, give the stream a chance to take
771 	 * care of the operation itself, thereby allowing it to update its internal
772 	 * state */
773 	if (!Z_ISUNDEF(php_sock->zstream)) {
774 		php_stream *stream;
775 		stream = zend_fetch_resource2_ex(&php_sock->zstream, NULL, php_file_le_stream(), php_file_le_pstream());
776 		if (stream != NULL) {
777 			if (php_stream_set_option(stream, PHP_STREAM_OPTION_BLOCKING, 1,
778 					NULL) != -1) {
779 				php_sock->blocking = 1;
780 				RETURN_TRUE;
781 			}
782 		}
783 	}
784 
785 	if (php_set_sock_blocking(php_sock->bsd_socket, 1) == SUCCESS) {
786 		php_sock->blocking = 1;
787 		RETURN_TRUE;
788 	} else {
789 		PHP_SOCKET_ERROR(php_sock, "unable to set blocking mode", errno);
790 		RETURN_FALSE;
791 	}
792 }
793 /* }}} */
794 
795 /* {{{ Sets the maximum number of connections allowed to be waited for on the socket specified by fd */
796 PHP_FUNCTION(socket_listen)
797 {
798 	zval		*arg1;
799 	php_socket	*php_sock;
800 	zend_long	backlog = 0;
801 
802 	ZEND_PARSE_PARAMETERS_START(1, 2)
803 		Z_PARAM_OBJECT_OF_CLASS(arg1, socket_ce)
804 		Z_PARAM_OPTIONAL
805 		Z_PARAM_LONG(backlog)
806 	ZEND_PARSE_PARAMETERS_END();
807 
808 	php_sock = Z_SOCKET_P(arg1);
809 	ENSURE_SOCKET_VALID(php_sock);
810 
811 	if (listen(php_sock->bsd_socket, backlog) != 0) {
812 		PHP_SOCKET_ERROR(php_sock, "unable to listen on socket", errno);
813 		RETURN_FALSE;
814 	}
815 	RETURN_TRUE;
816 }
817 /* }}} */
818 
819 /* {{{ Closes a file descriptor */
820 PHP_FUNCTION(socket_close)
821 {
822 	zval *arg1;
823 	php_socket *php_socket;
824 
825 	ZEND_PARSE_PARAMETERS_START(1, 1)
826 		Z_PARAM_OBJECT_OF_CLASS(arg1, socket_ce)
827 	ZEND_PARSE_PARAMETERS_END();
828 
829 	php_socket = Z_SOCKET_P(arg1);
830 	ENSURE_SOCKET_VALID(php_socket);
831 
832 	if (!Z_ISUNDEF(php_socket->zstream)) {
833 		php_stream *stream = NULL;
834 		php_stream_from_zval_no_verify(stream, &php_socket->zstream);
835 		if (stream != NULL) {
836 			/* close & destroy stream, incl. removing it from the rsrc list;
837 			 * resource stored in php_sock->zstream will become invalid */
838 			php_stream_free(stream,
839 					PHP_STREAM_FREE_KEEP_RSRC | PHP_STREAM_FREE_CLOSE |
840 					(stream->is_persistent?PHP_STREAM_FREE_CLOSE_PERSISTENT:0));
841 		}
842 	} else {
843 		if (!IS_INVALID_SOCKET(php_socket)) {
844 			close(php_socket->bsd_socket);
845 		}
846 	}
847 
848 	ZVAL_UNDEF(&php_socket->zstream);
849 	php_socket->bsd_socket = -1;
850 }
851 /* }}} */
852 
853 /* {{{ Writes the buffer to the socket resource, length is optional */
854 PHP_FUNCTION(socket_write)
855 {
856 	zval		*arg1;
857 	php_socket	*php_sock;
858 	int			retval;
859 	size_t      str_len;
860 	zend_long	length = 0;
861 	bool   length_is_null = 1;
862 	char		*str;
863 
864 	ZEND_PARSE_PARAMETERS_START(2, 3)
865 		Z_PARAM_OBJECT_OF_CLASS(arg1, socket_ce)
866 		Z_PARAM_STRING(str, str_len)
867 		Z_PARAM_OPTIONAL
868 		Z_PARAM_LONG_OR_NULL(length, length_is_null)
869 	ZEND_PARSE_PARAMETERS_END();
870 
871 	php_sock = Z_SOCKET_P(arg1);
872 	ENSURE_SOCKET_VALID(php_sock);
873 
874 	if (length < 0) {
875 		zend_argument_value_error(3, "must be greater than or equal to 0");
876 		RETURN_THROWS();
877 	}
878 
879 	if (length_is_null) {
880 		length = str_len;
881 	}
882 
883 #ifndef PHP_WIN32
884 	retval = write(php_sock->bsd_socket, str, MIN(length, str_len));
885 #else
886 	retval = send(php_sock->bsd_socket, str, min(length, str_len), 0);
887 #endif
888 
889 	if (retval < 0) {
890 		PHP_SOCKET_ERROR(php_sock, "unable to write to socket", errno);
891 		RETURN_FALSE;
892 	}
893 
894 	RETURN_LONG(retval);
895 }
896 /* }}} */
897 
898 /* {{{ Reads a maximum of length bytes from socket */
899 PHP_FUNCTION(socket_read)
900 {
901 	zval		*arg1;
902 	php_socket	*php_sock;
903 	zend_string	*tmpbuf;
904 	int			retval;
905 	zend_long		length, type = PHP_BINARY_READ;
906 
907 	ZEND_PARSE_PARAMETERS_START(2, 3)
908 		Z_PARAM_OBJECT_OF_CLASS(arg1, socket_ce)
909 		Z_PARAM_LONG(length)
910 		Z_PARAM_OPTIONAL
911 		Z_PARAM_LONG(type)
912 	ZEND_PARSE_PARAMETERS_END();
913 
914 	php_sock = Z_SOCKET_P(arg1);
915 	ENSURE_SOCKET_VALID(php_sock);
916 
917 	/* overflow check */
918 	if ((length + 1) < 2) {
919 		RETURN_FALSE;
920 	}
921 
922 	tmpbuf = zend_string_alloc(length, 0);
923 
924 	if (type == PHP_NORMAL_READ) {
925 		retval = php_read(php_sock, ZSTR_VAL(tmpbuf), length, 0);
926 	} else {
927 		retval = recv(php_sock->bsd_socket, ZSTR_VAL(tmpbuf), length, 0);
928 	}
929 
930 	if (retval == -1) {
931 		/* if the socket is in non-blocking mode and there's no data to read,
932 		don't output any error, as this is a normal situation, and not an error */
933 		if (PHP_IS_TRANSIENT_ERROR(errno)) {
934 			php_sock->error = errno;
935 			SOCKETS_G(last_error) = errno;
936 		} else {
937 			PHP_SOCKET_ERROR(php_sock, "unable to read from socket", errno);
938 		}
939 
940 		zend_string_efree(tmpbuf);
941 		RETURN_FALSE;
942 	} else if (!retval) {
943 		zend_string_efree(tmpbuf);
944 		RETURN_EMPTY_STRING();
945 	}
946 
947 	tmpbuf = zend_string_truncate(tmpbuf, retval, 0);
948 	ZSTR_LEN(tmpbuf) = retval;
949 	ZSTR_VAL(tmpbuf)[ZSTR_LEN(tmpbuf)] = '\0' ;
950 
951 	RETURN_NEW_STR(tmpbuf);
952 }
953 /* }}} */
954 
955 /* {{{ Queries the remote side of the given socket which may either result in host/port or in a UNIX filesystem path, dependent on its type. */
956 PHP_FUNCTION(socket_getsockname)
957 {
958 	zval					*arg1, *addr, *port = NULL;
959 	php_sockaddr_storage	sa_storage = {0};
960 	php_socket				*php_sock;
961 	struct sockaddr			*sa;
962 	struct sockaddr_in		*sin;
963 #ifdef HAVE_IPV6
964 	struct sockaddr_in6		*sin6;
965 #endif
966 	char					addrbuf[INET6_ADDRSTRLEN];
967 	struct sockaddr_un		*s_un;
968 	const char				*addr_string;
969 	socklen_t				salen = sizeof(php_sockaddr_storage);
970 
971 	ZEND_PARSE_PARAMETERS_START(2, 3)
972 		Z_PARAM_OBJECT_OF_CLASS(arg1, socket_ce)
973 		Z_PARAM_ZVAL(addr)
974 		Z_PARAM_OPTIONAL
975 		Z_PARAM_ZVAL(port)
976 	ZEND_PARSE_PARAMETERS_END();
977 
978 	php_sock = Z_SOCKET_P(arg1);
979 	ENSURE_SOCKET_VALID(php_sock);
980 
981 	sa = (struct sockaddr *) &sa_storage;
982 
983 	if (getsockname(php_sock->bsd_socket, sa, &salen) != 0) {
984 		PHP_SOCKET_ERROR(php_sock, "unable to retrieve socket name", errno);
985 		RETURN_FALSE;
986 	}
987 
988 	switch (sa->sa_family) {
989 #ifdef HAVE_IPV6
990 		case AF_INET6:
991 			sin6 = (struct sockaddr_in6 *) sa;
992 			inet_ntop(AF_INET6, &sin6->sin6_addr,  addrbuf, sizeof(addrbuf));
993 			ZEND_TRY_ASSIGN_REF_STRING(addr, addrbuf);
994 
995 			if (port != NULL) {
996 				ZEND_TRY_ASSIGN_REF_LONG(port, htons(sin6->sin6_port));
997 			}
998 			RETURN_TRUE;
999 			break;
1000 #endif
1001 		case AF_INET:
1002 			sin = (struct sockaddr_in *) sa;
1003 			addr_string = inet_ntop(AF_INET, &sin->sin_addr, addrbuf, sizeof(addrbuf));
1004 			ZEND_TRY_ASSIGN_REF_STRING(addr, addr_string);
1005 
1006 			if (port != NULL) {
1007 				ZEND_TRY_ASSIGN_REF_LONG(port, htons(sin->sin_port));
1008 			}
1009 			RETURN_TRUE;
1010 			break;
1011 
1012 		case AF_UNIX:
1013 			s_un = (struct sockaddr_un *) sa;
1014 
1015 			ZEND_TRY_ASSIGN_REF_STRING(addr, s_un->sun_path);
1016 			RETURN_TRUE;
1017 			break;
1018 
1019 		default:
1020 			zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET, or AF_INET6");
1021 			RETURN_THROWS();
1022 	}
1023 }
1024 /* }}} */
1025 
1026 /* {{{ Queries the remote side of the given socket which may either result in host/port or in a UNIX filesystem path, dependent on its type. */
1027 PHP_FUNCTION(socket_getpeername)
1028 {
1029 	zval					*arg1, *arg2, *arg3 = NULL;
1030 	php_sockaddr_storage	sa_storage = {0};
1031 	php_socket				*php_sock;
1032 	struct sockaddr			*sa;
1033 	struct sockaddr_in		*sin;
1034 #ifdef HAVE_IPV6
1035 	struct sockaddr_in6		*sin6;
1036 #endif
1037 	char					addrbuf[INET6_ADDRSTRLEN];
1038 	struct sockaddr_un		*s_un;
1039 	const char				*addr_string;
1040 	socklen_t				salen = sizeof(php_sockaddr_storage);
1041 
1042 	ZEND_PARSE_PARAMETERS_START(2, 3)
1043 		Z_PARAM_OBJECT_OF_CLASS(arg1, socket_ce)
1044 		Z_PARAM_ZVAL(arg2)
1045 		Z_PARAM_OPTIONAL
1046 		Z_PARAM_ZVAL(arg3)
1047 	ZEND_PARSE_PARAMETERS_END();
1048 
1049 	php_sock = Z_SOCKET_P(arg1);
1050 	ENSURE_SOCKET_VALID(php_sock);
1051 
1052 	sa = (struct sockaddr *) &sa_storage;
1053 
1054 	if (getpeername(php_sock->bsd_socket, sa, &salen) < 0) {
1055 		PHP_SOCKET_ERROR(php_sock, "unable to retrieve peer name", errno);
1056 		RETURN_FALSE;
1057 	}
1058 
1059 	switch (sa->sa_family) {
1060 #ifdef HAVE_IPV6
1061 		case AF_INET6:
1062 			sin6 = (struct sockaddr_in6 *) sa;
1063 			inet_ntop(AF_INET6, &sin6->sin6_addr, addrbuf, sizeof(addrbuf));
1064 
1065 			ZEND_TRY_ASSIGN_REF_STRING(arg2, addrbuf);
1066 
1067 			if (arg3 != NULL) {
1068 				ZEND_TRY_ASSIGN_REF_LONG(arg3, htons(sin6->sin6_port));
1069 			}
1070 
1071 			RETURN_TRUE;
1072 			break;
1073 #endif
1074 		case AF_INET:
1075 			sin = (struct sockaddr_in *) sa;
1076 			addr_string = inet_ntop(AF_INET, &sin->sin_addr, addrbuf, sizeof(addrbuf));
1077 			ZEND_TRY_ASSIGN_REF_STRING(arg2, addr_string);
1078 
1079 			if (arg3 != NULL) {
1080 				ZEND_TRY_ASSIGN_REF_LONG(arg3, htons(sin->sin_port));
1081 			}
1082 
1083 			RETURN_TRUE;
1084 			break;
1085 
1086 		case AF_UNIX:
1087 			s_un = (struct sockaddr_un *) sa;
1088 
1089 			ZEND_TRY_ASSIGN_REF_STRING(arg2, s_un->sun_path);
1090 			RETURN_TRUE;
1091 			break;
1092 
1093 		default:
1094 			zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET, or AF_INET6");
1095 			RETURN_THROWS();
1096 	}
1097 }
1098 /* }}} */
1099 
1100 /* {{{ Creates an endpoint for communication in the domain specified by domain, of type specified by type */
1101 PHP_FUNCTION(socket_create)
1102 {
1103 	zend_long	domain, type, checktype, protocol;
1104 	php_socket	*php_sock;
1105 
1106 	ZEND_PARSE_PARAMETERS_START(3, 3)
1107 		Z_PARAM_LONG(domain)
1108 		Z_PARAM_LONG(type)
1109 		Z_PARAM_LONG(protocol)
1110 	ZEND_PARSE_PARAMETERS_END();
1111 
1112 	if (domain != AF_UNIX
1113 #ifdef HAVE_IPV6
1114 		&& domain != AF_INET6
1115 #endif
1116 		&& domain != AF_INET) {
1117 		zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET6, or AF_INET");
1118 		RETURN_THROWS();
1119 	}
1120 
1121 	checktype = type;
1122 #ifdef SOCK_NONBLOCK
1123 	checktype &= ~(SOCK_CLOEXEC | SOCK_NONBLOCK);
1124 #endif
1125 
1126 	if (checktype > 10) {
1127 		zend_argument_value_error(2, "must be one of SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET,"
1128 			" SOCK_RAW, or SOCK_RDM"
1129 #ifdef SOCK_NONBLOCK
1130 			" optionally OR'ed with SOCK_CLOEXEC, SOCK_NONBLOCK"
1131 #endif
1132 		);
1133 		RETURN_THROWS();
1134 	}
1135 
1136 	object_init_ex(return_value, socket_ce);
1137 	php_sock = Z_SOCKET_P(return_value);
1138 
1139 	php_sock->bsd_socket = socket(domain, type, protocol);
1140 	php_sock->type = domain;
1141 
1142 	if (IS_INVALID_SOCKET(php_sock)) {
1143 		SOCKETS_G(last_error) = errno;
1144 		php_error_docref(NULL, E_WARNING, "Unable to create socket [%d]: %s", errno, sockets_strerror(errno));
1145 		zval_ptr_dtor(return_value);
1146 		RETURN_FALSE;
1147 	}
1148 
1149 	php_sock->error = 0;
1150 	php_sock->blocking = 1;
1151 }
1152 /* }}} */
1153 
1154 /* {{{ Opens a connection to addr:port on the socket specified by socket */
1155 PHP_FUNCTION(socket_connect)
1156 {
1157 	zval				*resource_socket;
1158 	php_socket			*php_sock;
1159 	char				*addr;
1160 	int					retval;
1161 	size_t              addr_len;
1162 	zend_long				port;
1163 	bool				port_is_null = 1;
1164 
1165 	ZEND_PARSE_PARAMETERS_START(2, 3)
1166 		Z_PARAM_OBJECT_OF_CLASS(resource_socket, socket_ce)
1167 		Z_PARAM_STRING(addr, addr_len)
1168 		Z_PARAM_OPTIONAL
1169 		Z_PARAM_LONG_OR_NULL(port, port_is_null)
1170 	ZEND_PARSE_PARAMETERS_END();
1171 
1172 	php_sock = Z_SOCKET_P(resource_socket);
1173 	ENSURE_SOCKET_VALID(php_sock);
1174 
1175 	switch(php_sock->type) {
1176 #ifdef HAVE_IPV6
1177 		case AF_INET6: {
1178 			struct sockaddr_in6 sin6 = {0};
1179 
1180 			if (port_is_null) {
1181 				zend_argument_value_error(3, "cannot be null when the socket type is AF_INET6");
1182 				RETURN_THROWS();
1183 			}
1184 
1185 			memset(&sin6, 0, sizeof(struct sockaddr_in6));
1186 
1187 			sin6.sin6_family = AF_INET6;
1188 			sin6.sin6_port   = htons((unsigned short int)port);
1189 
1190 			if (! php_set_inet6_addr(&sin6, addr, php_sock)) {
1191 				RETURN_FALSE;
1192 			}
1193 
1194 			retval = connect(php_sock->bsd_socket, (struct sockaddr *)&sin6, sizeof(struct sockaddr_in6));
1195 			break;
1196 		}
1197 #endif
1198 		case AF_INET: {
1199 			struct sockaddr_in sin = {0};
1200 
1201 			if (port_is_null) {
1202 				zend_argument_value_error(3, "cannot be null when the socket type is AF_INET");
1203 				RETURN_THROWS();
1204 			}
1205 
1206 			sin.sin_family = AF_INET;
1207 			sin.sin_port   = htons((unsigned short int)port);
1208 
1209 			if (! php_set_inet_addr(&sin, addr, php_sock)) {
1210 				RETURN_FALSE;
1211 			}
1212 
1213 			retval = connect(php_sock->bsd_socket, (struct sockaddr *)&sin, sizeof(struct sockaddr_in));
1214 			break;
1215 		}
1216 
1217 		case AF_UNIX: {
1218 			struct sockaddr_un s_un = {0};
1219 
1220 			if (addr_len >= sizeof(s_un.sun_path)) {
1221 				zend_argument_value_error(2, "must be less than %d", sizeof(s_un.sun_path));
1222 				RETURN_THROWS();
1223 			}
1224 
1225 			s_un.sun_family = AF_UNIX;
1226 			memcpy(&s_un.sun_path, addr, addr_len);
1227 			retval = connect(php_sock->bsd_socket, (struct sockaddr *) &s_un,
1228 				(socklen_t)(XtOffsetOf(struct sockaddr_un, sun_path) + addr_len));
1229 			break;
1230 		}
1231 
1232 		default:
1233 			zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET, or AF_INET6");
1234 			RETURN_THROWS();
1235 		}
1236 
1237 	if (retval != 0) {
1238 		PHP_SOCKET_ERROR(php_sock, "unable to connect", errno);
1239 		RETURN_FALSE;
1240 	}
1241 
1242 	RETURN_TRUE;
1243 }
1244 /* }}} */
1245 
1246 /* {{{ Returns a string describing an error */
1247 PHP_FUNCTION(socket_strerror)
1248 {
1249 	zend_long	arg1;
1250 
1251 	ZEND_PARSE_PARAMETERS_START(1, 1)
1252 		Z_PARAM_LONG(arg1)
1253 	ZEND_PARSE_PARAMETERS_END();
1254 
1255 	if (ZEND_LONG_EXCEEDS_INT(arg1)) {
1256 		zend_argument_value_error(1, "must be between %d and %d", INT_MIN, INT_MAX);
1257 		RETURN_THROWS();
1258 	}
1259 
1260 	RETURN_STRING(sockets_strerror(arg1));
1261 }
1262 /* }}} */
1263 
1264 /* {{{ Binds an open socket to a listening port, port is only specified in AF_INET family. */
1265 PHP_FUNCTION(socket_bind)
1266 {
1267 	zval					*arg1;
1268 	php_sockaddr_storage	sa_storage = {0};
1269 	struct sockaddr			*sock_type = (struct sockaddr*) &sa_storage;
1270 	php_socket				*php_sock;
1271 	char					*addr;
1272 	size_t						addr_len;
1273 	zend_long					port = 0;
1274 	zend_long					retval = 0;
1275 
1276 	ZEND_PARSE_PARAMETERS_START(2, 3)
1277 		Z_PARAM_OBJECT_OF_CLASS(arg1, socket_ce)
1278 		Z_PARAM_STRING(addr, addr_len)
1279 		Z_PARAM_OPTIONAL
1280 		Z_PARAM_LONG(port)
1281 	ZEND_PARSE_PARAMETERS_END();
1282 
1283 	php_sock = Z_SOCKET_P(arg1);
1284 	ENSURE_SOCKET_VALID(php_sock);
1285 
1286 	switch(php_sock->type) {
1287 		case AF_UNIX:
1288 			{
1289 				struct sockaddr_un *sa = (struct sockaddr_un *) sock_type;
1290 
1291 				sa->sun_family = AF_UNIX;
1292 
1293 				if (addr_len >= sizeof(sa->sun_path)) {
1294 					zend_argument_value_error(2, "must be less than %d", sizeof(sa->sun_path));
1295 					RETURN_THROWS();
1296 				}
1297 				memcpy(&sa->sun_path, addr, addr_len);
1298 
1299 				retval = bind(php_sock->bsd_socket, (struct sockaddr *) sa,
1300 						offsetof(struct sockaddr_un, sun_path) + addr_len);
1301 				break;
1302 			}
1303 
1304 		case AF_INET:
1305 			{
1306 				struct sockaddr_in *sa = (struct sockaddr_in *) sock_type;
1307 
1308 				sa->sin_family = AF_INET;
1309 				sa->sin_port = htons((unsigned short) port);
1310 
1311 				if (! php_set_inet_addr(sa, addr, php_sock)) {
1312 					RETURN_FALSE;
1313 				}
1314 
1315 				retval = bind(php_sock->bsd_socket, (struct sockaddr *)sa, sizeof(struct sockaddr_in));
1316 				break;
1317 			}
1318 #ifdef HAVE_IPV6
1319 		case AF_INET6:
1320 			{
1321 				struct sockaddr_in6 *sa = (struct sockaddr_in6 *) sock_type;
1322 
1323 				sa->sin6_family = AF_INET6;
1324 				sa->sin6_port = htons((unsigned short) port);
1325 
1326 				if (! php_set_inet6_addr(sa, addr, php_sock)) {
1327 					RETURN_FALSE;
1328 				}
1329 
1330 				retval = bind(php_sock->bsd_socket, (struct sockaddr *)sa, sizeof(struct sockaddr_in6));
1331 				break;
1332 			}
1333 #endif
1334 		default:
1335 			zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET, or AF_INET6");
1336 			RETURN_THROWS();
1337 	}
1338 
1339 	if (retval != 0) {
1340 		PHP_SOCKET_ERROR(php_sock, "Unable to bind address", errno);
1341 		RETURN_FALSE;
1342 	}
1343 
1344 	RETURN_TRUE;
1345 }
1346 /* }}} */
1347 
1348 /* {{{ Receives data from a connected socket */
1349 PHP_FUNCTION(socket_recv)
1350 {
1351 	zval		*php_sock_res, *buf;
1352 	zend_string	*recv_buf;
1353 	php_socket	*php_sock;
1354 	int			retval;
1355 	zend_long		len, flags;
1356 
1357 	ZEND_PARSE_PARAMETERS_START(4, 4)
1358 		Z_PARAM_OBJECT_OF_CLASS(php_sock_res, socket_ce)
1359 		Z_PARAM_ZVAL(buf)
1360 		Z_PARAM_LONG(len)
1361 		Z_PARAM_LONG(flags)
1362 	ZEND_PARSE_PARAMETERS_END();
1363 
1364 	php_sock = Z_SOCKET_P(php_sock_res);
1365 	ENSURE_SOCKET_VALID(php_sock);
1366 
1367 	/* overflow check */
1368 	if ((len + 1) < 2) {
1369 		RETURN_FALSE;
1370 	}
1371 
1372 	recv_buf = zend_string_alloc(len, 0);
1373 
1374 	if ((retval = recv(php_sock->bsd_socket, ZSTR_VAL(recv_buf), len, flags)) < 1) {
1375 		zend_string_efree(recv_buf);
1376 		ZEND_TRY_ASSIGN_REF_NULL(buf);
1377 	} else {
1378 		ZSTR_LEN(recv_buf) = retval;
1379 		ZSTR_VAL(recv_buf)[ZSTR_LEN(recv_buf)] = '\0';
1380 		ZEND_TRY_ASSIGN_REF_NEW_STR(buf, recv_buf);
1381 	}
1382 
1383 	if (retval == -1) {
1384 		PHP_SOCKET_ERROR(php_sock, "Unable to read from socket", errno);
1385 		RETURN_FALSE;
1386 	}
1387 
1388 	RETURN_LONG(retval);
1389 }
1390 /* }}} */
1391 
1392 /* {{{ Sends data to a connected socket */
1393 PHP_FUNCTION(socket_send)
1394 {
1395 	zval		*arg1;
1396 	php_socket	*php_sock;
1397 	size_t			buf_len, retval;
1398 	zend_long		len, flags;
1399 	char		*buf;
1400 
1401 	ZEND_PARSE_PARAMETERS_START(4, 4)
1402 		Z_PARAM_OBJECT_OF_CLASS(arg1, socket_ce)
1403 		Z_PARAM_STRING(buf, buf_len)
1404 		Z_PARAM_LONG(len)
1405 		Z_PARAM_LONG(flags)
1406 	ZEND_PARSE_PARAMETERS_END();
1407 
1408 	php_sock = Z_SOCKET_P(arg1);
1409 	ENSURE_SOCKET_VALID(php_sock);
1410 
1411 	if (len < 0) {
1412 		zend_argument_value_error(3, "must be greater than or equal to 0");
1413 		RETURN_THROWS();
1414 	}
1415 
1416 	retval = send(php_sock->bsd_socket, buf, (buf_len < (size_t)len ? buf_len : (size_t)len), flags);
1417 
1418 	if (retval == (size_t)-1) {
1419 		PHP_SOCKET_ERROR(php_sock, "Unable to write to socket", errno);
1420 		RETURN_FALSE;
1421 	}
1422 
1423 	RETURN_LONG(retval);
1424 }
1425 /* }}} */
1426 
1427 /* {{{ Receives data from a socket, connected or not */
1428 PHP_FUNCTION(socket_recvfrom)
1429 {
1430 	zval				*arg1, *arg2, *arg5, *arg6 = NULL;
1431 	php_socket			*php_sock;
1432 	struct sockaddr_un	s_un;
1433 	struct sockaddr_in	sin;
1434 #ifdef HAVE_IPV6
1435 	struct sockaddr_in6	sin6;
1436 #endif
1437 	char				addrbuf[INET6_ADDRSTRLEN];
1438 	socklen_t			slen;
1439 	int					retval;
1440 	zend_long				arg3, arg4;
1441 	const char			*address;
1442 	zend_string			*recv_buf;
1443 
1444 	ZEND_PARSE_PARAMETERS_START(5, 6)
1445 		Z_PARAM_OBJECT_OF_CLASS(arg1, socket_ce)
1446 		Z_PARAM_ZVAL(arg2)
1447 		Z_PARAM_LONG(arg3)
1448 		Z_PARAM_LONG(arg4)
1449 		Z_PARAM_ZVAL(arg5)
1450 		Z_PARAM_OPTIONAL
1451 		Z_PARAM_ZVAL(arg6)
1452 	ZEND_PARSE_PARAMETERS_END();
1453 
1454 	php_sock = Z_SOCKET_P(arg1);
1455 	ENSURE_SOCKET_VALID(php_sock);
1456 
1457 	/* overflow check */
1458 	/* Shouldthrow ? */
1459 
1460 	if (arg3 <= 0 || arg3 > ZEND_LONG_MAX - 1) {
1461 		RETURN_FALSE;
1462 	}
1463 
1464 	recv_buf = zend_string_alloc(arg3 + 1, 0);
1465 
1466 	switch (php_sock->type) {
1467 		case AF_UNIX:
1468 			slen = sizeof(s_un);
1469 			memset(&s_un, 0, slen);
1470 			s_un.sun_family = AF_UNIX;
1471 
1472 			retval = recvfrom(php_sock->bsd_socket, ZSTR_VAL(recv_buf), arg3, arg4, (struct sockaddr *)&s_un, (socklen_t *)&slen);
1473 
1474 			if (retval < 0) {
1475 				PHP_SOCKET_ERROR(php_sock, "Unable to recvfrom", errno);
1476 				zend_string_efree(recv_buf);
1477 				RETURN_FALSE;
1478 			}
1479 			ZSTR_LEN(recv_buf) = retval;
1480 			ZSTR_VAL(recv_buf)[ZSTR_LEN(recv_buf)] = '\0';
1481 
1482 			ZEND_TRY_ASSIGN_REF_NEW_STR(arg2, recv_buf);
1483 			ZEND_TRY_ASSIGN_REF_STRING(arg5, s_un.sun_path);
1484 			break;
1485 
1486 		case AF_INET:
1487 			slen = sizeof(sin);
1488 			memset(&sin, 0, slen);
1489 			sin.sin_family = AF_INET;
1490 
1491 			if (arg6 == NULL) {
1492 				zend_string_efree(recv_buf);
1493 				WRONG_PARAM_COUNT;
1494 			}
1495 
1496 			retval = recvfrom(php_sock->bsd_socket, ZSTR_VAL(recv_buf), arg3, arg4, (struct sockaddr *)&sin, (socklen_t *)&slen);
1497 
1498 			if (retval < 0) {
1499 				PHP_SOCKET_ERROR(php_sock, "Unable to recvfrom", errno);
1500 				zend_string_efree(recv_buf);
1501 				RETURN_FALSE;
1502 			}
1503 			ZSTR_LEN(recv_buf) = retval;
1504 			ZSTR_VAL(recv_buf)[ZSTR_LEN(recv_buf)] = '\0';
1505 
1506 			address = inet_ntop(AF_INET, &sin.sin_addr, addrbuf, sizeof(addrbuf));
1507 
1508 			ZEND_TRY_ASSIGN_REF_NEW_STR(arg2, recv_buf);
1509 			ZEND_TRY_ASSIGN_REF_STRING(arg5, address ? address : "0.0.0.0");
1510 			ZEND_TRY_ASSIGN_REF_LONG(arg6, ntohs(sin.sin_port));
1511 			break;
1512 #ifdef HAVE_IPV6
1513 		case AF_INET6:
1514 			slen = sizeof(sin6);
1515 			memset(&sin6, 0, slen);
1516 			sin6.sin6_family = AF_INET6;
1517 
1518 			if (arg6 == NULL) {
1519 				zend_string_efree(recv_buf);
1520 				WRONG_PARAM_COUNT;
1521 			}
1522 
1523 			retval = recvfrom(php_sock->bsd_socket, ZSTR_VAL(recv_buf), arg3, arg4, (struct sockaddr *)&sin6, (socklen_t *)&slen);
1524 
1525 			if (retval < 0) {
1526 				PHP_SOCKET_ERROR(php_sock, "unable to recvfrom", errno);
1527 				zend_string_efree(recv_buf);
1528 				RETURN_FALSE;
1529 			}
1530 			ZSTR_LEN(recv_buf) = retval;
1531 			ZSTR_VAL(recv_buf)[ZSTR_LEN(recv_buf)] = '\0';
1532 
1533 			memset(addrbuf, 0, INET6_ADDRSTRLEN);
1534 			inet_ntop(AF_INET6, &sin6.sin6_addr,  addrbuf, sizeof(addrbuf));
1535 
1536 			ZEND_TRY_ASSIGN_REF_NEW_STR(arg2, recv_buf);
1537 			ZEND_TRY_ASSIGN_REF_STRING(arg5, addrbuf[0] ? addrbuf : "::");
1538 			ZEND_TRY_ASSIGN_REF_LONG(arg6, ntohs(sin6.sin6_port));
1539 			break;
1540 #endif
1541 		default:
1542 			zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET, or AF_INET6");
1543 			RETURN_THROWS();
1544 	}
1545 
1546 	RETURN_LONG(retval);
1547 }
1548 /* }}} */
1549 
1550 /* {{{ Sends a message to a socket, whether it is connected or not */
1551 PHP_FUNCTION(socket_sendto)
1552 {
1553 	zval				*arg1;
1554 	php_socket			*php_sock;
1555 	struct sockaddr_un	s_un;
1556 	struct sockaddr_in	sin;
1557 #ifdef HAVE_IPV6
1558 	struct sockaddr_in6	sin6;
1559 #endif
1560 	int					retval;
1561 	size_t              buf_len, addr_len;
1562 	zend_long			len, flags, port;
1563 	bool           port_is_null = 1;
1564 	char				*buf, *addr;
1565 
1566 	ZEND_PARSE_PARAMETERS_START(5, 6)
1567 		Z_PARAM_OBJECT_OF_CLASS(arg1, socket_ce)
1568 		Z_PARAM_STRING(buf, buf_len)
1569 		Z_PARAM_LONG(len)
1570 		Z_PARAM_LONG(flags)
1571 		Z_PARAM_STRING(addr, addr_len)
1572 		Z_PARAM_OPTIONAL
1573 		Z_PARAM_LONG_OR_NULL(port, port_is_null)
1574 	ZEND_PARSE_PARAMETERS_END();
1575 
1576 	php_sock = Z_SOCKET_P(arg1);
1577 	ENSURE_SOCKET_VALID(php_sock);
1578 
1579 	if (len < 0) {
1580 		zend_argument_value_error(3, "must be greater than or equal to 0");
1581 		RETURN_THROWS();
1582 	}
1583 
1584 	switch (php_sock->type) {
1585 		case AF_UNIX:
1586 			memset(&s_un, 0, sizeof(s_un));
1587 			s_un.sun_family = AF_UNIX;
1588 			snprintf(s_un.sun_path, sizeof(s_un.sun_path), "%s", addr);
1589 
1590 			retval = sendto(php_sock->bsd_socket, buf, ((size_t)len > buf_len) ? buf_len : (size_t)len,	flags, (struct sockaddr *) &s_un, SUN_LEN(&s_un));
1591 			break;
1592 
1593 		case AF_INET:
1594 			if (port_is_null) {
1595 				zend_argument_value_error(6, "cannot be null when the socket type is AF_INET");
1596 				RETURN_THROWS();
1597 			}
1598 
1599 			memset(&sin, 0, sizeof(sin));
1600 			sin.sin_family = AF_INET;
1601 			sin.sin_port = htons((unsigned short) port);
1602 
1603 			if (! php_set_inet_addr(&sin, addr, php_sock)) {
1604 				RETURN_FALSE;
1605 			}
1606 
1607 			retval = sendto(php_sock->bsd_socket, buf, ((size_t)len > buf_len) ? buf_len : (size_t)len, flags, (struct sockaddr *) &sin, sizeof(sin));
1608 			break;
1609 #ifdef HAVE_IPV6
1610 		case AF_INET6:
1611 			if (port_is_null) {
1612 				zend_argument_value_error(6, "cannot be null when the socket type is AF_INET6");
1613 				RETURN_THROWS();
1614 			}
1615 
1616 			memset(&sin6, 0, sizeof(sin6));
1617 			sin6.sin6_family = AF_INET6;
1618 			sin6.sin6_port = htons((unsigned short) port);
1619 
1620 			if (! php_set_inet6_addr(&sin6, addr, php_sock)) {
1621 				RETURN_FALSE;
1622 			}
1623 
1624 			retval = sendto(php_sock->bsd_socket, buf, ((size_t)len > buf_len) ? buf_len : (size_t)len, flags, (struct sockaddr *) &sin6, sizeof(sin6));
1625 			break;
1626 #endif
1627 		default:
1628 			zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET, or AF_INET6");
1629 			RETURN_THROWS();
1630 	}
1631 
1632 	if (retval == -1) {
1633 		PHP_SOCKET_ERROR(php_sock, "Unable to write to socket", errno);
1634 		RETURN_FALSE;
1635 	}
1636 
1637 	RETURN_LONG(retval);
1638 }
1639 /* }}} */
1640 
1641 /* {{{ Gets socket options for the socket */
1642 PHP_FUNCTION(socket_get_option)
1643 {
1644 	zval			*arg1;
1645 	struct linger	linger_val;
1646 	struct timeval	tv;
1647 #ifdef PHP_WIN32
1648 	DWORD				timeout = 0;
1649 #else
1650 	struct timeval timeout;
1651 #endif
1652 	socklen_t		optlen;
1653 	php_socket		*php_sock;
1654 	int				other_val;
1655 	zend_long			level, optname;
1656 
1657 	ZEND_PARSE_PARAMETERS_START(3, 3)
1658 		Z_PARAM_OBJECT_OF_CLASS(arg1, socket_ce)
1659 		Z_PARAM_LONG(level)
1660 		Z_PARAM_LONG(optname)
1661 	ZEND_PARSE_PARAMETERS_END();
1662 
1663 	php_sock = Z_SOCKET_P(arg1);
1664 	ENSURE_SOCKET_VALID(php_sock);
1665 
1666 	if (level == IPPROTO_IP) {
1667 		switch (optname) {
1668 		case IP_MULTICAST_IF: {
1669 			struct in_addr if_addr;
1670 			unsigned int if_index;
1671 			optlen = sizeof(if_addr);
1672 			if (getsockopt(php_sock->bsd_socket, level, optname, (char*)&if_addr, &optlen) != 0) {
1673 				PHP_SOCKET_ERROR(php_sock, "Unable to retrieve socket option", errno);
1674 				RETURN_FALSE;
1675 			}
1676 			if (php_add4_to_if_index(&if_addr, php_sock, &if_index) == SUCCESS) {
1677 				RETURN_LONG((zend_long) if_index);
1678 			} else {
1679 				RETURN_FALSE;
1680 			}
1681 		}
1682 		}
1683 	}
1684 #ifdef HAVE_IPV6
1685 	else if (level == IPPROTO_IPV6) {
1686 		int ret = php_do_getsockopt_ipv6_rfc3542(php_sock, level, optname, return_value);
1687 		if (ret == SUCCESS) {
1688 			return;
1689 		} else if (ret == FAILURE) {
1690 			RETURN_FALSE;
1691 		} /* else continue */
1692 	}
1693 #endif
1694 
1695 	if (level == IPPROTO_TCP) {
1696 		switch (optname) {
1697 #ifdef TCP_CONGESTION
1698 		case TCP_CONGESTION: {
1699 			char name[16];
1700 			optlen = sizeof(name);
1701 			if (getsockopt(php_sock->bsd_socket, level, optname, name, &optlen) != 0) {
1702 				PHP_SOCKET_ERROR(php_sock, "Unable to retrieve socket option", errno);
1703 				RETURN_FALSE;
1704 			} else {
1705 				array_init(return_value);
1706 
1707 				add_assoc_string(return_value, "name", name);
1708 				return;
1709 			}
1710 		}
1711 #endif
1712 
1713 #ifdef TCP_FUNCTION_BLK
1714 		case TCP_FUNCTION_BLK: {
1715 
1716 			struct tcp_function_set tsf = {0};
1717 			optlen = sizeof(tsf);
1718 
1719 			if (getsockopt(php_sock->bsd_socket, level, optname, &tsf, &optlen) != 0) {
1720 				PHP_SOCKET_ERROR(php_sock, "Unable to retrieve socket option", errno);
1721 				RETURN_FALSE;
1722 			}
1723 
1724 			array_init(return_value);
1725 
1726 			add_assoc_string(return_value, "function_set_name", tsf.function_set_name);
1727 			add_assoc_long(return_value, "pcbcnt", tsf.pcbcnt);
1728 			return;
1729 		}
1730 #endif
1731 		}
1732 	}
1733 
1734 	if (level == SOL_SOCKET) {
1735 		switch (optname) {
1736 #ifdef SO_LINGER_SEC
1737 			case SO_LINGER_SEC:
1738 #endif
1739 			case SO_LINGER:
1740 				optlen = sizeof(linger_val);
1741 
1742 				if (getsockopt(php_sock->bsd_socket, level, optname, (char*)&linger_val, &optlen) != 0) {
1743 					PHP_SOCKET_ERROR(php_sock, "Unable to retrieve socket option", errno);
1744 					RETURN_FALSE;
1745 				}
1746 
1747 				array_init(return_value);
1748 				add_assoc_long(return_value, "l_onoff", linger_val.l_onoff);
1749 				add_assoc_long(return_value, "l_linger", linger_val.l_linger);
1750 				return;
1751 
1752 			case SO_RCVTIMEO:
1753 			case SO_SNDTIMEO:
1754 				optlen = sizeof(timeout);
1755 
1756 				if (getsockopt(php_sock->bsd_socket, level, optname, (char*)&timeout, &optlen) != 0) {
1757 					PHP_SOCKET_ERROR(php_sock, "Unable to retrieve socket option", errno);
1758 					RETURN_FALSE;
1759 				}
1760 
1761 #ifndef PHP_WIN32
1762 				tv.tv_sec = timeout.tv_sec;
1763 				tv.tv_usec = timeout.tv_usec;
1764 #else
1765 				tv.tv_sec = timeout ? (long)(timeout / 1000) : 0;
1766 				tv.tv_usec = timeout ? (long)((timeout % 1000) * 1000) : 0;
1767 #endif
1768 
1769 				array_init(return_value);
1770 
1771 				add_assoc_long(return_value, "sec", tv.tv_sec);
1772 				add_assoc_long(return_value, "usec", tv.tv_usec);
1773 				return;
1774 #ifdef SO_MEMINFO
1775 			case SO_MEMINFO: {
1776 				uint32_t minfo[SK_MEMINFO_VARS];
1777 				optlen = sizeof(minfo);
1778 
1779 				if (getsockopt(php_sock->bsd_socket, level, optname, (char*)minfo, &optlen) != 0) {
1780 					PHP_SOCKET_ERROR(php_sock, "Unable to retrieve socket option", errno);
1781 					RETURN_FALSE;
1782 				}
1783 
1784 				if (UNEXPECTED(optlen != sizeof(minfo))) {
1785 					// unlikely since the kernel fills up the whole array if getsockopt succeeded
1786 					// but just an extra precaution in case.
1787 					php_error_docref(NULL, E_WARNING, "Unable to retrieve all socket meminfo data");
1788 					RETURN_FALSE;
1789 				}
1790 
1791 				array_init(return_value);
1792 
1793 				add_assoc_long(return_value, "rmem_alloc", minfo[SK_MEMINFO_RMEM_ALLOC]);
1794 				add_assoc_long(return_value, "rcvbuf", minfo[SK_MEMINFO_RCVBUF]);
1795 				add_assoc_long(return_value, "wmem_alloc", minfo[SK_MEMINFO_WMEM_ALLOC]);
1796 				add_assoc_long(return_value, "sndbuf", minfo[SK_MEMINFO_SNDBUF]);
1797 				add_assoc_long(return_value, "fwd_alloc", minfo[SK_MEMINFO_FWD_ALLOC]);
1798 				add_assoc_long(return_value, "wmem_queued", minfo[SK_MEMINFO_WMEM_QUEUED]);
1799 				add_assoc_long(return_value, "optmem", minfo[SK_MEMINFO_OPTMEM]);
1800 				add_assoc_long(return_value, "backlog", minfo[SK_MEMINFO_BACKLOG]);
1801 				add_assoc_long(return_value, "drops", minfo[SK_MEMINFO_DROPS]);
1802 				return;
1803 			}
1804 #endif
1805 #ifdef SO_ACCEPTFILTER
1806 			case SO_ACCEPTFILTER: {
1807 
1808 				struct accept_filter_arg af = {0};
1809 				optlen = sizeof(af);
1810 
1811 				if (getsockopt(php_sock->bsd_socket, level, optname, (char*)&af, &optlen) != 0) {
1812 					PHP_SOCKET_ERROR(php_sock, "Unable to retrieve socket option", errno);
1813 					RETURN_FALSE;
1814 				}
1815 
1816 				array_init(return_value);
1817 
1818 				add_assoc_string(return_value, "af_name", af.af_name);
1819 				return;
1820 			}
1821 #endif
1822 
1823 		}
1824 	}
1825 
1826 #ifdef SOL_FILTER
1827 	if (level == SOL_FILTER) {
1828 		switch (optname) {
1829 
1830 			case FIL_LIST: {
1831 				size_t i;
1832 				struct fil_info fi[32] = {{0}};
1833 				optlen = sizeof(fi);
1834 
1835 				if (getsockopt(php_sock->bsd_socket, level, optname, (char*)fi, &optlen) != 0) {
1836 					PHP_SOCKET_ERROR(php_sock, "Unable to retrieve socket option", errno);
1837 					RETURN_FALSE;
1838 				}
1839 
1840 				array_init(return_value);
1841 
1842 				for (i = 0; i < optlen / sizeof(struct fil_info); i++) {
1843 					add_index_string(return_value, i, fi[i].fi_name);
1844 				}
1845 
1846 				return;
1847 			}
1848 		}
1849 	}
1850 #endif
1851 
1852 	optlen = sizeof(other_val);
1853 
1854 	if (getsockopt(php_sock->bsd_socket, level, optname, (char*)&other_val, &optlen) != 0) {
1855 		PHP_SOCKET_ERROR(php_sock, "Unable to retrieve socket option", errno);
1856 		RETURN_FALSE;
1857 	}
1858 
1859 	if (optlen == 1) {
1860 		other_val = *((unsigned char *)&other_val);
1861 	}
1862 
1863 	RETURN_LONG(other_val);
1864 }
1865 /* }}} */
1866 
1867 /* {{{ Sets socket options for the socket */
1868 PHP_FUNCTION(socket_set_option)
1869 {
1870 	zval					*arg1, *arg4;
1871 	struct linger			lv;
1872 	php_socket				*php_sock;
1873 	int						ov, optlen, retval;
1874 #ifdef PHP_WIN32
1875 	DWORD						timeout;
1876 #else
1877 	struct					timeval tv;
1878 #endif
1879 	zend_long					level, optname;
1880 	void 					*opt_ptr;
1881 	HashTable		 		*opt_ht;
1882 	zval 					*l_onoff, *l_linger;
1883 	zval		 			*sec, *usec;
1884 
1885 	ZEND_PARSE_PARAMETERS_START(4, 4)
1886 		Z_PARAM_OBJECT_OF_CLASS(arg1, socket_ce)
1887 		Z_PARAM_LONG(level)
1888 		Z_PARAM_LONG(optname)
1889 		Z_PARAM_ZVAL(arg4)
1890 	ZEND_PARSE_PARAMETERS_END();
1891 
1892 	php_sock = Z_SOCKET_P(arg1);
1893 	ENSURE_SOCKET_VALID(php_sock);
1894 
1895 	set_errno(0);
1896 
1897 #define HANDLE_SUBCALL(res) \
1898 	do { \
1899 		if (res == 1) { goto default_case; } \
1900 		else if (res == SUCCESS) { RETURN_TRUE; } \
1901 		else { RETURN_FALSE; } \
1902 	} while (0)
1903 
1904 
1905 	if (level == IPPROTO_IP) {
1906 		int res = php_do_setsockopt_ip_mcast(php_sock, level, optname, arg4);
1907 		HANDLE_SUBCALL(res);
1908 	}
1909 
1910 #ifdef HAVE_IPV6
1911 	else if (level == IPPROTO_IPV6) {
1912 		int res = php_do_setsockopt_ipv6_mcast(php_sock, level, optname, arg4);
1913 		if (res == 1) {
1914 			res = php_do_setsockopt_ipv6_rfc3542(php_sock, level, optname, arg4);
1915 		}
1916 		HANDLE_SUBCALL(res);
1917 	}
1918 #endif
1919 
1920 	if (level == IPPROTO_TCP) {
1921 		switch (optname) {
1922 #ifdef TCP_CONGESTION
1923 		case TCP_CONGESTION: {
1924 			if (Z_TYPE_P(arg4) == IS_STRING) {
1925 				opt_ptr = Z_STRVAL_P(arg4);
1926 				optlen = Z_STRLEN_P(arg4);
1927 			} else {
1928 				opt_ptr = "";
1929 				optlen = 0;
1930 			}
1931 			if (setsockopt(php_sock->bsd_socket, level, optname, opt_ptr, optlen) != 0) {
1932 				PHP_SOCKET_ERROR(php_sock, "Unable to set socket option", errno);
1933 				RETURN_FALSE;
1934 			}
1935 
1936 			RETURN_TRUE;
1937 		}
1938 #endif
1939 
1940 #ifdef TCP_FUNCTION_BLK
1941 		case TCP_FUNCTION_BLK: {
1942 			if (Z_TYPE_P(arg4) != IS_STRING) {
1943 				php_error_docref(NULL, E_WARNING, "Invalid tcp stack name argument type");
1944 				RETURN_FALSE;
1945 			}
1946 			struct tcp_function_set tfs = {0};
1947 			strlcpy(tfs.function_set_name, Z_STRVAL_P(arg4), TCP_FUNCTION_NAME_LEN_MAX);
1948 
1949 			optlen = sizeof(tfs);
1950 			opt_ptr = &tfs;
1951 			if (setsockopt(php_sock->bsd_socket, level, optname, opt_ptr, optlen) != 0) {
1952 				PHP_SOCKET_ERROR(php_sock, "Unable to set socket option", errno);
1953 				RETURN_FALSE;
1954 			}
1955 
1956 			RETURN_TRUE;
1957 		}
1958 #endif
1959 
1960 		}
1961 	}
1962 
1963 	switch (optname) {
1964 #ifdef SO_LINGER_SEC
1965 		case SO_LINGER_SEC:
1966 #endif
1967 		case SO_LINGER: {
1968 			const char l_onoff_key[] = "l_onoff";
1969 			const char l_linger_key[] = "l_linger";
1970 
1971 			if (Z_TYPE_P(arg4) != IS_ARRAY) {
1972 				if (UNEXPECTED(Z_TYPE_P(arg4) != IS_OBJECT)) {
1973 					zend_argument_type_error(4, "must be of type array when argument #3 ($option) is SO_LINGER, %s given", zend_zval_value_name(arg4));
1974 					RETURN_THROWS();
1975 				} else {
1976 					opt_ht = Z_OBJPROP_P(arg4);
1977 				}
1978 			} else {
1979 				opt_ht = Z_ARRVAL_P(arg4);
1980 			}
1981 
1982 			if ((l_onoff = zend_hash_str_find(opt_ht, l_onoff_key, sizeof(l_onoff_key) - 1)) == NULL) {
1983 				zend_argument_value_error(4, "must have key \"%s\"", l_onoff_key);
1984 				RETURN_THROWS();
1985 			}
1986 			if ((l_linger = zend_hash_str_find(opt_ht, l_linger_key, sizeof(l_linger_key) - 1)) == NULL) {
1987 				zend_argument_value_error(4, "must have key \"%s\"", l_linger_key);
1988 				RETURN_THROWS();
1989 			}
1990 
1991 			zend_long val_lonoff = zval_get_long(l_onoff);
1992 			zend_long val_linger = zval_get_long(l_linger);
1993 
1994 			if (val_lonoff < 0 || val_lonoff > USHRT_MAX) {
1995 				zend_argument_value_error(4, "\"%s\" must be between 0 and %u", l_onoff_key, USHRT_MAX);
1996 				RETURN_THROWS();
1997 			}
1998 
1999 			if (val_linger < 0 || val_linger > USHRT_MAX) {
2000 				zend_argument_value_error(4, "\"%s\" must be between 0 and %d", l_linger, USHRT_MAX);
2001 				RETURN_THROWS();
2002 			}
2003 
2004 			lv.l_onoff = (unsigned short)val_lonoff;
2005 			lv.l_linger = (unsigned short)val_linger;
2006 
2007 			optlen = sizeof(lv);
2008 			opt_ptr = &lv;
2009 			break;
2010 		}
2011 
2012 		case SO_RCVTIMEO:
2013 		case SO_SNDTIMEO: {
2014 			const char sec_key[] = "sec";
2015 			const char usec_key[] = "usec";
2016 
2017 			if (Z_TYPE_P(arg4) != IS_ARRAY) {
2018 				if (UNEXPECTED(Z_TYPE_P(arg4) != IS_OBJECT)) {
2019 					zend_argument_type_error(4, "must be of type array when argument #3 ($option) is %s, %s given",
2020 							optname == SO_RCVTIMEO ? "SO_RCVTIMEO" : "SO_SNDTIMEO",
2021 							zend_zval_value_name(arg4));
2022 					RETURN_THROWS();
2023 				} else {
2024 					opt_ht = Z_OBJPROP_P(arg4);
2025 				}
2026 			} else {
2027 				opt_ht = Z_ARRVAL_P(arg4);
2028 			}
2029 
2030 			if ((sec = zend_hash_str_find(opt_ht, sec_key, sizeof(sec_key) - 1)) == NULL) {
2031 				zend_argument_value_error(4, "must have key \"%s\"", sec_key);
2032 				RETURN_THROWS();
2033 			}
2034 			if ((usec = zend_hash_str_find(opt_ht, usec_key, sizeof(usec_key) - 1)) == NULL) {
2035 				zend_argument_value_error(4, "must have key \"%s\"", usec_key);
2036 				RETURN_THROWS();
2037 			}
2038 
2039 			zend_long valsec = zval_get_long(sec);
2040 			zend_long valusec = zval_get_long(usec);
2041 #ifndef PHP_WIN32
2042 			tv.tv_sec = valsec;
2043 			tv.tv_usec = valusec;
2044 			optlen = sizeof(tv);
2045 			opt_ptr = &tv;
2046 #else
2047 			if (valsec < 0 || valsec > ULONG_MAX / 1000) {
2048 				zend_argument_value_error(4, "\"%s\" must be between 0 and %u", sec_key, (ULONG_MAX / 1000));
2049 				RETURN_THROWS();
2050 			}
2051 
2052 			timeout = valsec * 1000;
2053 
2054 
2055 			/*
2056 			 * We deliberately throw if (valusec / 1000) > ULONG_MAX, treating it as a programmer error.
2057 			 * On Windows, ULONG_MAX = 2^32, unlike ZEND_LONG_MAX = 2^63.
2058 			 */
2059 			if (valusec < 0 || timeout > ULONG_MAX - (valusec / 1000)) {
2060 				zend_argument_value_error(4, "\"%s\" must be between 0 and %u", usec_key, (DWORD)(ULONG_MAX - (valusec / 1000)));
2061 				RETURN_THROWS();
2062 			}
2063 
2064 			timeout += valusec / 1000;
2065 			optlen = sizeof(timeout);
2066 			opt_ptr = &timeout;
2067 #endif
2068 			break;
2069 		}
2070 #ifdef SO_BINDTODEVICE
2071 		case SO_BINDTODEVICE: {
2072 			if (Z_TYPE_P(arg4) == IS_STRING) {
2073 				opt_ptr = Z_STRVAL_P(arg4);
2074 				optlen = Z_STRLEN_P(arg4);
2075 			} else {
2076 				opt_ptr = "";
2077 				optlen = 0;
2078 			}
2079 			break;
2080 		}
2081 #endif
2082 
2083 #ifdef SO_ACCEPTFILTER
2084 		case SO_ACCEPTFILTER: {
2085 			if (Z_TYPE_P(arg4) != IS_STRING) {
2086 				php_error_docref(NULL, E_WARNING, "Invalid filter argument type");
2087 				RETURN_FALSE;
2088 			}
2089 			struct accept_filter_arg af = {0};
2090 			strlcpy(af.af_name, Z_STRVAL_P(arg4), sizeof(af.af_name));
2091 			opt_ptr = &af;
2092 			optlen = sizeof(af);
2093 			break;
2094 		}
2095 #endif
2096 
2097 #ifdef FIL_ATTACH
2098 		case FIL_ATTACH:
2099 		case FIL_DETACH: {
2100 			if (level != SOL_FILTER) {
2101 				php_error_docref(NULL, E_WARNING, "Invalid level");
2102 				RETURN_FALSE;
2103 			}
2104 			if (Z_TYPE_P(arg4) != IS_STRING) {
2105 				php_error_docref(NULL, E_WARNING, "Invalid filter argument type");
2106 				RETURN_FALSE;
2107 			}
2108 			opt_ptr = Z_STRVAL_P(arg4);
2109 			optlen = Z_STRLEN_P(arg4);
2110 			break;
2111 		}
2112 #endif
2113 
2114 #ifdef SO_ATTACH_REUSEPORT_CBPF
2115 		case SO_ATTACH_REUSEPORT_CBPF: {
2116 			zend_long cbpf_val = zval_get_long(arg4);
2117 
2118 			if (!cbpf_val) {
2119 				ov = 1;
2120 				optlen = sizeof(ov);
2121 				opt_ptr = &ov;
2122 				optname = SO_DETACH_BPF;
2123 			} else {
2124 				uint32_t k = (uint32_t)cbpf_val;
2125 				static struct sock_filter cbpf[8] = {0};
2126 				static struct sock_fprog bpfprog;
2127 
2128 				switch (k) {
2129 					case SKF_AD_CPU:
2130 					case SKF_AD_QUEUE:
2131 						cbpf[0].code = (BPF_LD|BPF_W|BPF_ABS);
2132 						cbpf[0].k = (uint32_t)(SKF_AD_OFF + k);
2133 						cbpf[1].code = (BPF_RET|BPF_A);
2134 						bpfprog.len = 2;
2135 					break;
2136 					default:
2137 						php_error_docref(NULL, E_WARNING, "Unsupported CBPF filter");
2138 						RETURN_FALSE;
2139 				}
2140 
2141 				bpfprog.filter = cbpf;
2142 				optlen = sizeof(bpfprog);
2143 				opt_ptr = &bpfprog;
2144 			}
2145 			break;
2146 		}
2147 #endif
2148 
2149 		default:
2150 default_case:
2151 			ov = zval_get_long(arg4);
2152 
2153 			optlen = sizeof(ov);
2154 			opt_ptr = &ov;
2155 			break;
2156 	}
2157 
2158 	retval = setsockopt(php_sock->bsd_socket, level, optname, opt_ptr, optlen);
2159 	if (retval != 0) {
2160 		PHP_SOCKET_ERROR(php_sock, "Unable to set socket option", errno);
2161 		RETURN_FALSE;
2162 	}
2163 
2164 	RETURN_TRUE;
2165 }
2166 /* }}} */
2167 
2168 #ifdef HAVE_SOCKETPAIR
2169 /* {{{ Creates a pair of indistinguishable sockets and stores them in fds. */
2170 PHP_FUNCTION(socket_create_pair)
2171 {
2172 	zval		retval[2], *fds_array_zval;
2173 	php_socket	*php_sock[2];
2174 	PHP_SOCKET	fds_array[2];
2175 	zend_long		domain, type, checktype, protocol;
2176 
2177 	ZEND_PARSE_PARAMETERS_START(4, 4)
2178 		Z_PARAM_LONG(domain)
2179 		Z_PARAM_LONG(type)
2180 		Z_PARAM_LONG(protocol)
2181 		Z_PARAM_ZVAL(fds_array_zval)
2182 	ZEND_PARSE_PARAMETERS_END();
2183 
2184 	if (domain != AF_INET
2185 #ifdef HAVE_IPV6
2186 		&& domain != AF_INET6
2187 #endif
2188 		&& domain != AF_UNIX) {
2189 		zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET6, or AF_INET");
2190 		RETURN_THROWS();
2191 	}
2192 
2193 	checktype = type;
2194 #ifdef SOCK_NONBLOCK
2195 	checktype &= ~(SOCK_CLOEXEC | SOCK_NONBLOCK);
2196 #endif
2197 
2198 	if (checktype > 10) {
2199 		zend_argument_value_error(2, "must be one of SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET,"
2200 			" SOCK_RAW, or SOCK_RDM"
2201 #ifdef SOCK_NONBLOCK
2202 			" optionally OR'ed with SOCK_CLOEXEC, SOCK_NONBLOCK"
2203 #endif
2204 		);
2205 		RETURN_THROWS();
2206 	}
2207 
2208 	object_init_ex(&retval[0], socket_ce);
2209 	php_sock[0] = Z_SOCKET_P(&retval[0]);
2210 
2211 	object_init_ex(&retval[1], socket_ce);
2212 	php_sock[1] = Z_SOCKET_P(&retval[1]);
2213 
2214 	if (socketpair(domain, type, protocol, fds_array) != 0) {
2215 		SOCKETS_G(last_error) = errno;
2216 		php_error_docref(NULL, E_WARNING, "Unable to create socket pair [%d]: %s", errno, sockets_strerror(errno));
2217 		zval_ptr_dtor(&retval[0]);
2218 		zval_ptr_dtor(&retval[1]);
2219 		RETURN_FALSE;
2220 	}
2221 
2222 	fds_array_zval = zend_try_array_init(fds_array_zval);
2223 	if (!fds_array_zval) {
2224 		zval_ptr_dtor(&retval[0]);
2225 		zval_ptr_dtor(&retval[1]);
2226 		RETURN_THROWS();
2227 	}
2228 
2229 	php_sock[0]->bsd_socket = fds_array[0];
2230 	php_sock[1]->bsd_socket = fds_array[1];
2231 	php_sock[0]->type		= domain;
2232 	php_sock[1]->type		= domain;
2233 	php_sock[0]->error		= 0;
2234 	php_sock[1]->error		= 0;
2235 	php_sock[0]->blocking	= 1;
2236 	php_sock[1]->blocking	= 1;
2237 
2238 	add_index_zval(fds_array_zval, 0, &retval[0]);
2239 	add_index_zval(fds_array_zval, 1, &retval[1]);
2240 
2241 	RETURN_TRUE;
2242 }
2243 /* }}} */
2244 #endif
2245 
2246 #ifdef HAVE_SHUTDOWN
2247 /* {{{ Shuts down a socket for receiving, sending, or both. */
2248 PHP_FUNCTION(socket_shutdown)
2249 {
2250 	zval		*arg1;
2251 	zend_long		how_shutdown = 2;
2252 	php_socket	*php_sock;
2253 
2254 	ZEND_PARSE_PARAMETERS_START(1, 2)
2255 		Z_PARAM_OBJECT_OF_CLASS(arg1, socket_ce)
2256 		Z_PARAM_OPTIONAL
2257 		Z_PARAM_LONG(how_shutdown)
2258 	ZEND_PARSE_PARAMETERS_END();
2259 
2260 	php_sock = Z_SOCKET_P(arg1);
2261 	ENSURE_SOCKET_VALID(php_sock);
2262 
2263 	if (shutdown(php_sock->bsd_socket, how_shutdown) != 0) {
2264 		PHP_SOCKET_ERROR(php_sock, "Unable to shutdown socket", errno);
2265 		RETURN_FALSE;
2266 	}
2267 
2268 	RETURN_TRUE;
2269 }
2270 /* }}} */
2271 #endif
2272 
2273 #ifdef HAVE_SOCKATMARK
2274 PHP_FUNCTION(socket_atmark)
2275 {
2276 	zval		*arg1;
2277 	php_socket	*php_sock;
2278 	int r;
2279 
2280 	ZEND_PARSE_PARAMETERS_START(1, 1)
2281 		Z_PARAM_OBJECT_OF_CLASS(arg1, socket_ce)
2282 	ZEND_PARSE_PARAMETERS_END();
2283 
2284 	php_sock = Z_SOCKET_P(arg1);
2285 	ENSURE_SOCKET_VALID(php_sock);
2286 
2287 	r = sockatmark(php_sock->bsd_socket);
2288 	if (r < 0) {
2289 		PHP_SOCKET_ERROR(php_sock, "Unable to apply sockmark", errno);
2290 		RETURN_FALSE;
2291 	} else if (r == 0) {
2292 		RETURN_FALSE;
2293 	} else {
2294 		RETURN_TRUE;
2295 	}
2296 }
2297 #endif
2298 
2299 /* {{{ Returns the last socket error (either the last used or the provided socket resource) */
2300 PHP_FUNCTION(socket_last_error)
2301 {
2302 	zval		*arg1 = NULL;
2303 	php_socket	*php_sock;
2304 
2305 	ZEND_PARSE_PARAMETERS_START(0, 1)
2306 		Z_PARAM_OPTIONAL
2307 		Z_PARAM_OBJECT_OF_CLASS_OR_NULL(arg1, socket_ce)
2308 	ZEND_PARSE_PARAMETERS_END();
2309 
2310 	if (arg1) {
2311 		php_sock = Z_SOCKET_P(arg1);
2312 		ENSURE_SOCKET_VALID(php_sock);
2313 
2314 		RETVAL_LONG(php_sock->error);
2315 	} else {
2316 		RETVAL_LONG(SOCKETS_G(last_error));
2317 	}
2318 }
2319 /* }}} */
2320 
2321 /* {{{ Clears the error on the socket or the last error code. */
2322 PHP_FUNCTION(socket_clear_error)
2323 {
2324 	zval		*arg1 = NULL;
2325 	php_socket	*php_sock;
2326 
2327 	ZEND_PARSE_PARAMETERS_START(0, 1)
2328 		Z_PARAM_OPTIONAL
2329 		Z_PARAM_OBJECT_OF_CLASS_OR_NULL(arg1, socket_ce)
2330 	ZEND_PARSE_PARAMETERS_END();
2331 
2332 	if (arg1) {
2333 		php_sock = Z_SOCKET_P(arg1);
2334 		ENSURE_SOCKET_VALID(php_sock);
2335 
2336 		php_sock->error = 0;
2337 	} else {
2338 		SOCKETS_G(last_error) = 0;
2339 	}
2340 
2341 	return;
2342 }
2343 /* }}} */
2344 
2345 bool socket_import_file_descriptor(PHP_SOCKET socket, php_socket *retsock)
2346 {
2347 #ifdef SO_DOMAIN
2348 	int						type;
2349 	socklen_t				type_len = sizeof(type);
2350 #endif
2351 	php_sockaddr_storage	addr;
2352 	socklen_t				addr_len = sizeof(addr);
2353 #ifndef PHP_WIN32
2354 	int					 t;
2355 #endif
2356 
2357 	retsock->bsd_socket = socket;
2358 
2359 	/* determine family */
2360 #ifdef SO_DOMAIN
2361 	if (getsockopt(socket, SOL_SOCKET, SO_DOMAIN, &type, &type_len) == 0) {
2362 		retsock->type = type;
2363 	} else
2364 #endif
2365 	if (getsockname(socket, (struct sockaddr*)&addr, &addr_len) == 0) {
2366 		retsock->type = addr.ss_family;
2367 	} else {
2368 		PHP_SOCKET_ERROR(retsock, "Unable to obtain socket family", errno);
2369 		return 0;
2370 	}
2371 
2372 	/* determine blocking mode */
2373 #ifndef PHP_WIN32
2374 	t = fcntl(socket, F_GETFL);
2375 	if (t == -1) {
2376 		PHP_SOCKET_ERROR(retsock, "Unable to obtain blocking state", errno);
2377 		return 0;
2378 	} else {
2379 		retsock->blocking = !(t & O_NONBLOCK);
2380 	}
2381 #endif
2382 
2383 	return 1;
2384 }
2385 
2386 /* {{{ Imports a stream that encapsulates a socket into a socket extension resource. */
2387 PHP_FUNCTION(socket_import_stream)
2388 {
2389 	zval				 *zstream;
2390 	php_stream			 *stream;
2391 	php_socket			 *retsock = NULL;
2392 	PHP_SOCKET			 socket; /* fd */
2393 
2394 	ZEND_PARSE_PARAMETERS_START(1, 1)
2395 		Z_PARAM_RESOURCE(zstream)
2396 	ZEND_PARSE_PARAMETERS_END();
2397 	php_stream_from_zval(stream, zstream);
2398 
2399 	if (php_stream_cast(stream, PHP_STREAM_AS_SOCKETD, (void**)&socket, 1)) {
2400 		/* error supposedly already shown */
2401 		RETURN_FALSE;
2402 	}
2403 
2404 	object_init_ex(return_value, socket_ce);
2405 	retsock = Z_SOCKET_P(return_value);
2406 
2407 	if (!socket_import_file_descriptor(socket, retsock)) {
2408 		zval_ptr_dtor(return_value);
2409 		RETURN_FALSE;
2410 	}
2411 
2412 #ifdef PHP_WIN32
2413 	/* on windows, check if the stream is a socket stream and read its
2414 	 * private data; otherwise assume it's in non-blocking mode */
2415 	if (php_stream_is(stream, PHP_STREAM_IS_SOCKET)) {
2416 		retsock->blocking =
2417 				((php_netstream_data_t *)stream->abstract)->is_blocked;
2418 	} else {
2419 		retsock->blocking = 1;
2420 	}
2421 #endif
2422 
2423 	/* hold a zval reference to the stream (holding a php_stream* directly could
2424 	 * also be done, but this makes socket_export_stream a bit simpler) */
2425 	ZVAL_COPY(&retsock->zstream, zstream);
2426 
2427 	php_stream_set_option(stream, PHP_STREAM_OPTION_READ_BUFFER, PHP_STREAM_BUFFER_NONE, NULL);
2428 }
2429 /* }}} */
2430 
2431 /* {{{ Exports a socket extension resource into a stream that encapsulates a socket. */
2432 PHP_FUNCTION(socket_export_stream)
2433 {
2434 	zval *zsocket;
2435 	php_socket *socket;
2436 	php_stream *stream = NULL;
2437 	php_netstream_data_t *stream_data;
2438 	const char *protocol = NULL;
2439 	size_t protocollen = 0;
2440 
2441 	ZEND_PARSE_PARAMETERS_START(1, 1)
2442 		Z_PARAM_OBJECT_OF_CLASS(zsocket, socket_ce)
2443 	ZEND_PARSE_PARAMETERS_END();
2444 
2445 	socket = Z_SOCKET_P(zsocket);
2446 	ENSURE_SOCKET_VALID(socket);
2447 
2448 	/* Either we already exported a stream or the socket came from an import,
2449 	 * just return the existing stream */
2450 	if (!Z_ISUNDEF(socket->zstream)) {
2451 		RETURN_COPY(&socket->zstream);
2452 	}
2453 
2454 	/* Determine if socket is using a protocol with one of the default registered
2455 	 * socket stream wrappers */
2456 	if (socket->type == PF_INET
2457 #ifdef HAVE_IPV6
2458 		 || socket->type == PF_INET6
2459 #endif
2460 	) {
2461 		int protoid;
2462 		socklen_t protoidlen = sizeof(protoid);
2463 
2464 		getsockopt(socket->bsd_socket, SOL_SOCKET, SO_TYPE, (char *) &protoid, &protoidlen);
2465 
2466 		if (protoid == SOCK_STREAM) {
2467 			/* SO_PROTOCOL is not (yet?) supported on OS X, so let's assume it's TCP there */
2468 #ifdef SO_PROTOCOL
2469 			protoidlen = sizeof(protoid);
2470 			getsockopt(socket->bsd_socket, SOL_SOCKET, SO_PROTOCOL, (char *) &protoid, &protoidlen);
2471 			if (protoid == IPPROTO_TCP)
2472 #endif
2473 			{
2474 				protocol = "tcp://";
2475 				protocollen = sizeof("tcp://") - 1;
2476 			}
2477 		} else if (protoid == SOCK_DGRAM) {
2478 			protocol = "udp://";
2479 			protocollen = sizeof("udp://") - 1;
2480 		}
2481 #ifdef PF_UNIX
2482 	} else if (socket->type == PF_UNIX) {
2483 		int type;
2484 		socklen_t typelen = sizeof(type);
2485 
2486 		getsockopt(socket->bsd_socket, SOL_SOCKET, SO_TYPE, (char *) &type, &typelen);
2487 
2488 		if (type == SOCK_STREAM) {
2489 			protocol = "unix://";
2490 			protocollen = sizeof("unix://") - 1;
2491 		} else if (type == SOCK_DGRAM) {
2492 			protocol = "udg://";
2493 			protocollen = sizeof("udg://") - 1;
2494 		}
2495 #endif
2496 	}
2497 
2498 	/* Try to get a stream with the registered sockops for the protocol in use
2499 	 * We don't want streams to actually *do* anything though, so don't give it
2500 	 * anything apart from the protocol */
2501 	if (protocol != NULL) {
2502 		stream = php_stream_xport_create(protocol, protocollen, 0, 0, NULL, NULL, NULL, NULL, NULL);
2503 	}
2504 
2505 	/* Fall back to creating a generic socket stream */
2506 	if (stream == NULL) {
2507 		stream = php_stream_sock_open_from_socket(socket->bsd_socket, 0);
2508 
2509 		if (stream == NULL) {
2510 			php_error_docref(NULL, E_WARNING, "Failed to create stream");
2511 			RETURN_FALSE;
2512 		}
2513 	}
2514 
2515 	stream_data = (php_netstream_data_t *) stream->abstract;
2516 	stream_data->socket = socket->bsd_socket;
2517 	stream_data->is_blocked = socket->blocking;
2518 	stream_data->timeout.tv_sec = FG(default_socket_timeout);
2519 	stream_data->timeout.tv_usec = 0;
2520 
2521 	php_stream_to_zval(stream, &socket->zstream);
2522 
2523 	RETURN_COPY(&socket->zstream);
2524 }
2525 /* }}} */
2526 
2527 /* {{{ Gets array with contents of getaddrinfo about the given hostname. */
2528 PHP_FUNCTION(socket_addrinfo_lookup)
2529 {
2530 	char *service = NULL;
2531 	size_t service_len = 0;
2532 	zend_string *hostname, *key;
2533 	zval *hint, *zhints = NULL;
2534 
2535 	struct addrinfo hints, *result, *rp;
2536 	php_addrinfo *res;
2537 
2538 	ZEND_PARSE_PARAMETERS_START(1, 3)
2539 		Z_PARAM_STR(hostname)
2540 		Z_PARAM_OPTIONAL
2541 		Z_PARAM_STRING_OR_NULL(service, service_len)
2542 		Z_PARAM_ARRAY(zhints)
2543 	ZEND_PARSE_PARAMETERS_END();
2544 
2545 	memset(&hints, 0, sizeof(hints));
2546 
2547 	if (zhints && !HT_IS_PACKED(Z_ARRVAL_P(zhints))) {
2548 		ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(zhints), key, hint) {
2549 			if (key) {
2550 				if (zend_string_equals_literal(key, "ai_flags")) {
2551 					hints.ai_flags = zval_get_long(hint);
2552 				} else if (zend_string_equals_literal(key, "ai_socktype")) {
2553 					hints.ai_socktype = zval_get_long(hint);
2554 				} else if (zend_string_equals_literal(key, "ai_protocol")) {
2555 					hints.ai_protocol = zval_get_long(hint);
2556 				} else if (zend_string_equals_literal(key, "ai_family")) {
2557 					hints.ai_family = zval_get_long(hint);
2558 				} else {
2559 					zend_argument_value_error(3, "must only contain array keys \"ai_flags\", \"ai_socktype\", "
2560 						"\"ai_protocol\", or \"ai_family\"");
2561 					RETURN_THROWS();
2562 				}
2563 			}
2564 		} ZEND_HASH_FOREACH_END();
2565 	}
2566 
2567 	if (getaddrinfo(ZSTR_VAL(hostname), service, &hints, &result) != 0) {
2568 		RETURN_FALSE;
2569 	}
2570 
2571 	array_init(return_value);
2572 
2573 	for (rp = result; rp != NULL; rp = rp->ai_next) {
2574 		if (rp->ai_family != AF_UNSPEC) {
2575 			zval zaddr;
2576 
2577 			object_init_ex(&zaddr, address_info_ce);
2578 			res = Z_ADDRESS_INFO_P(&zaddr);
2579 
2580 			memcpy(&res->addrinfo, rp, sizeof(struct addrinfo));
2581 
2582 			res->addrinfo.ai_addr = emalloc(rp->ai_addrlen);
2583 			memcpy(res->addrinfo.ai_addr, rp->ai_addr, rp->ai_addrlen);
2584 
2585 			if (rp->ai_canonname != NULL) {
2586 				res->addrinfo.ai_canonname = estrdup(rp->ai_canonname);
2587 			}
2588 
2589 			add_next_index_zval(return_value, &zaddr);
2590 		}
2591 	}
2592 
2593 	freeaddrinfo(result);
2594 }
2595 /* }}} */
2596 
2597 /* {{{ Creates and binds to a socket from a given addrinfo resource */
2598 PHP_FUNCTION(socket_addrinfo_bind)
2599 {
2600 	zval			*arg1;
2601 	int				retval;
2602 	php_addrinfo	*ai;
2603 	php_socket		*php_sock;
2604 
2605 	ZEND_PARSE_PARAMETERS_START(1, 1)
2606 		Z_PARAM_OBJECT_OF_CLASS(arg1, address_info_ce)
2607 	ZEND_PARSE_PARAMETERS_END();
2608 
2609 	ai = Z_ADDRESS_INFO_P(arg1);
2610 
2611 	object_init_ex(return_value, socket_ce);
2612 	php_sock = Z_SOCKET_P(return_value);
2613 
2614 	php_sock->bsd_socket = socket(ai->addrinfo.ai_family, ai->addrinfo.ai_socktype, ai->addrinfo.ai_protocol);
2615 	php_sock->type = ai->addrinfo.ai_family;
2616 
2617 	if (IS_INVALID_SOCKET(php_sock)) {
2618 		SOCKETS_G(last_error) = errno;
2619 		php_error_docref(NULL, E_WARNING, "Unable to create socket [%d]: %s", errno, sockets_strerror(errno));
2620 		zval_ptr_dtor(return_value);
2621 		RETURN_FALSE;
2622 	}
2623 
2624 	php_sock->error = 0;
2625 	php_sock->blocking = 1;
2626 
2627 	switch(php_sock->type) {
2628 		case AF_UNIX:
2629 			{
2630 				// AF_UNIX sockets via getaddrino are not implemented due to security problems
2631 				close(php_sock->bsd_socket);
2632 				zval_ptr_dtor(return_value);
2633 				RETURN_FALSE;
2634 			}
2635 
2636 		case AF_INET:
2637 #ifdef HAVE_IPV6
2638 		case AF_INET6:
2639 #endif
2640 			{
2641 				retval = bind(php_sock->bsd_socket, ai->addrinfo.ai_addr, ai->addrinfo.ai_addrlen);
2642 				break;
2643 			}
2644 		default:
2645 			close(php_sock->bsd_socket);
2646 			zval_ptr_dtor(return_value);
2647 			zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET, or AF_INET6");
2648 			RETURN_THROWS();
2649 	}
2650 
2651 	if (retval != 0) {
2652 		PHP_SOCKET_ERROR(php_sock, "Unable to bind address", errno);
2653 		close(php_sock->bsd_socket);
2654 		zval_ptr_dtor(return_value);
2655 		RETURN_FALSE;
2656 	}
2657 }
2658 /* }}} */
2659 
2660 /* {{{ Creates and connects to a socket from a given addrinfo resource */
2661 PHP_FUNCTION(socket_addrinfo_connect)
2662 {
2663 	zval			*arg1;
2664 	int				retval;
2665 	php_addrinfo	*ai;
2666 	php_socket		*php_sock;
2667 
2668 	ZEND_PARSE_PARAMETERS_START(1, 1)
2669 		Z_PARAM_OBJECT_OF_CLASS(arg1, address_info_ce)
2670 	ZEND_PARSE_PARAMETERS_END();
2671 
2672 	ai = Z_ADDRESS_INFO_P(arg1);
2673 
2674 	object_init_ex(return_value, socket_ce);
2675 	php_sock = Z_SOCKET_P(return_value);
2676 
2677 	php_sock->bsd_socket = socket(ai->addrinfo.ai_family, ai->addrinfo.ai_socktype, ai->addrinfo.ai_protocol);
2678 	php_sock->type = ai->addrinfo.ai_family;
2679 
2680 	if (IS_INVALID_SOCKET(php_sock)) {
2681 		SOCKETS_G(last_error) = errno;
2682 		php_error_docref(NULL, E_WARNING, "Unable to create socket [%d]: %s", errno, sockets_strerror(errno));
2683 		zval_ptr_dtor(return_value);
2684 		RETURN_FALSE;
2685 	}
2686 
2687 	php_sock->error = 0;
2688 	php_sock->blocking = 1;
2689 
2690 	switch(php_sock->type) {
2691 		case AF_UNIX:
2692 			{
2693 				// AF_UNIX sockets via getaddrino are not implemented due to security problems
2694 				close(php_sock->bsd_socket);
2695 				zval_ptr_dtor(return_value);
2696 				RETURN_FALSE;
2697 			}
2698 
2699 		case AF_INET:
2700 #ifdef HAVE_IPV6
2701 		case AF_INET6:
2702 #endif
2703 			{
2704 				retval = connect(php_sock->bsd_socket, ai->addrinfo.ai_addr, ai->addrinfo.ai_addrlen);
2705 				break;
2706 			}
2707 		default:
2708 			zend_argument_value_error(1, "socket type must be one of AF_UNIX, AF_INET, or AF_INET6");
2709 			close(php_sock->bsd_socket);
2710 			zval_ptr_dtor(return_value);
2711 			RETURN_THROWS();
2712 	}
2713 
2714 	if (retval != 0) {
2715 		PHP_SOCKET_ERROR(php_sock, "Unable to connect address", errno);
2716 		close(php_sock->bsd_socket);
2717 		zval_ptr_dtor(return_value);
2718 		RETURN_FALSE;
2719 	}
2720 }
2721 /* }}} */
2722 
2723 /* {{{ Creates and connects to a socket from a given addrinfo resource */
2724 PHP_FUNCTION(socket_addrinfo_explain)
2725 {
2726 	zval			*arg1, sockaddr;
2727 	php_addrinfo	*ai;
2728 
2729 	ZEND_PARSE_PARAMETERS_START(1, 1)
2730 		Z_PARAM_OBJECT_OF_CLASS(arg1, address_info_ce)
2731 	ZEND_PARSE_PARAMETERS_END();
2732 
2733 	ai = Z_ADDRESS_INFO_P(arg1);
2734 
2735 	array_init(return_value);
2736 
2737 	add_assoc_long(return_value, "ai_flags", ai->addrinfo.ai_flags);
2738 	add_assoc_long(return_value, "ai_family", ai->addrinfo.ai_family);
2739 	add_assoc_long(return_value, "ai_socktype", ai->addrinfo.ai_socktype);
2740 	add_assoc_long(return_value, "ai_protocol", ai->addrinfo.ai_protocol);
2741 	if (ai->addrinfo.ai_canonname != NULL) {
2742 		add_assoc_string(return_value, "ai_canonname", ai->addrinfo.ai_canonname);
2743 	}
2744 
2745 	array_init(&sockaddr);
2746 	switch (ai->addrinfo.ai_family) {
2747 		case AF_INET:
2748 			{
2749 				struct sockaddr_in *sa = (struct sockaddr_in *) ai->addrinfo.ai_addr;
2750 				char addr[INET_ADDRSTRLEN];
2751 
2752 				add_assoc_long(&sockaddr, "sin_port", ntohs((unsigned short) sa->sin_port));
2753 				inet_ntop(ai->addrinfo.ai_family, &sa->sin_addr, addr, sizeof(addr));
2754 				add_assoc_string(&sockaddr, "sin_addr", addr);
2755 				break;
2756 			}
2757 #ifdef HAVE_IPV6
2758 		case AF_INET6:
2759 			{
2760 				struct sockaddr_in6 *sa = (struct sockaddr_in6 *) ai->addrinfo.ai_addr;
2761 				char addr[INET6_ADDRSTRLEN];
2762 
2763 				add_assoc_long(&sockaddr, "sin6_port", ntohs((unsigned short) sa->sin6_port));
2764 				inet_ntop(ai->addrinfo.ai_family, &sa->sin6_addr, addr, sizeof(addr));
2765 				add_assoc_string(&sockaddr, "sin6_addr", addr);
2766 				break;
2767 			}
2768 #endif
2769 	}
2770 
2771 	add_assoc_zval(return_value, "ai_addr", &sockaddr);
2772 }
2773 /* }}} */
2774 
2775 #ifdef PHP_WIN32
2776 
2777 /* {{{ Exports the network socket information suitable to be used in another process and returns the info id. */
2778 PHP_FUNCTION(socket_wsaprotocol_info_export)
2779 {
2780 	WSAPROTOCOL_INFO wi;
2781 	zval *zsocket;
2782 	php_socket *socket;
2783 	zend_long target_pid;
2784 	zend_string *seg_name;
2785 	HANDLE map;
2786 
2787 	ZEND_PARSE_PARAMETERS_START(2, 2)
2788 		Z_PARAM_OBJECT_OF_CLASS(zsocket, socket_ce)
2789 		Z_PARAM_LONG(target_pid)
2790 	ZEND_PARSE_PARAMETERS_END();
2791 
2792 	socket = Z_SOCKET_P(zsocket);
2793 	ENSURE_SOCKET_VALID(socket);
2794 
2795 	if (SOCKET_ERROR == WSADuplicateSocket(socket->bsd_socket, (DWORD)target_pid, &wi)) {
2796 		DWORD err = WSAGetLastError();
2797 		char *buf = php_win32_error_to_msg(err);
2798 
2799 		if (!buf[0]) {
2800 			php_error_docref(NULL, E_WARNING, "Unable to export WSA protocol info [0x%08lx]", err);
2801 		} else {
2802 			php_error_docref(NULL, E_WARNING, "Unable to export WSA protocol info [0x%08lx]: %s", err, buf);
2803 		}
2804 
2805 		php_win32_error_msg_free(buf);
2806 
2807 		RETURN_FALSE;
2808 	}
2809 
2810 	seg_name = zend_strpprintf(0, "php_wsa_for_%u", SOCKETS_G(wsa_child_count)++);
2811 	map = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(WSAPROTOCOL_INFO), ZSTR_VAL(seg_name));
2812 	if (NULL != map) {
2813 		LPVOID view = MapViewOfFile(map, FILE_MAP_WRITE, 0, 0, 0);
2814 		if (view) {
2815 			memcpy(view, &wi, sizeof(wi));
2816 			UnmapViewOfFile(view);
2817 			zend_hash_add_ptr(&(SOCKETS_G(wsa_info)), seg_name, map);
2818 			RETURN_STR(seg_name);
2819 		} else {
2820 			DWORD err = GetLastError();
2821 			php_error_docref(NULL, E_WARNING, "Unable to map file view [0x%08lx]", err);
2822 		}
2823 	} else {
2824 		DWORD err = GetLastError();
2825 		php_error_docref(NULL, E_WARNING, "Unable to create file mapping [0x%08lx]", err);
2826 	}
2827 	zend_string_release_ex(seg_name, 0);
2828 
2829 	RETURN_FALSE;
2830 }
2831 /* }}} */
2832 
2833 /* {{{ Imports the network socket information using the supplied id and creates a new socket on its base. */
2834 PHP_FUNCTION(socket_wsaprotocol_info_import)
2835 {
2836 	char *id;
2837 	size_t id_len;
2838 	WSAPROTOCOL_INFO wi;
2839 	PHP_SOCKET sock;
2840 	php_socket	*php_sock;
2841 	HANDLE map;
2842 
2843 	ZEND_PARSE_PARAMETERS_START(1, 1)
2844 		Z_PARAM_STRING(id, id_len)
2845 	ZEND_PARSE_PARAMETERS_END();
2846 
2847 	map = OpenFileMapping(FILE_MAP_READ, FALSE, id);
2848 	if (map) {
2849 		LPVOID view = MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0);
2850 		if (view) {
2851 			memcpy(&wi, view, sizeof(WSAPROTOCOL_INFO));
2852 			UnmapViewOfFile(view);
2853 		} else {
2854 			DWORD err = GetLastError();
2855 			php_error_docref(NULL, E_WARNING, "Unable to map file view [0x%08lx]", err);
2856 			RETURN_FALSE;
2857 		}
2858 		CloseHandle(map);
2859 	} else {
2860 		DWORD err = GetLastError();
2861 		php_error_docref(NULL, E_WARNING, "Unable to open file mapping [0x%08lx]", err);
2862 		RETURN_FALSE;
2863 	}
2864 
2865 	sock = WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, &wi, 0, 0);
2866 	if (INVALID_SOCKET == sock) {
2867 		DWORD err = WSAGetLastError();
2868 		char *buf = php_win32_error_to_msg(err);
2869 
2870 		if (!buf[0]) {
2871 			php_error_docref(NULL, E_WARNING, "Unable to import WSA protocol info [0x%08lx]", err);
2872 		} else {
2873 			php_error_docref(NULL, E_WARNING, "Unable to import WSA protocol info [0x%08lx]: %s", err, buf);
2874 		}
2875 
2876 		php_win32_error_msg_free(buf);
2877 
2878 		RETURN_FALSE;
2879 	}
2880 
2881 	object_init_ex(return_value, socket_ce);
2882 	php_sock = Z_SOCKET_P(return_value);
2883 
2884 	php_sock->bsd_socket = sock;
2885 	php_sock->type = wi.iAddressFamily;
2886 	php_sock->error = 0;
2887 	php_sock->blocking = 1;
2888 }
2889 /* }}} */
2890 
2891 /* {{{ Frees the exported info and corresponding resources using the supplied id. */
2892 PHP_FUNCTION(socket_wsaprotocol_info_release)
2893 {
2894 	char *id;
2895 	size_t id_len;
2896 
2897 	ZEND_PARSE_PARAMETERS_START(1, 1)
2898 		Z_PARAM_STRING(id, id_len)
2899 	ZEND_PARSE_PARAMETERS_END();
2900 
2901 	RETURN_BOOL(SUCCESS == zend_hash_str_del(&(SOCKETS_G(wsa_info)), id, id_len));
2902 }
2903 /* }}} */
2904 #endif
2905