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