xref: /PHP-8.2/ext/sockets/sockets.c (revision ba4c82fd)
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 	RETURN_STRING(sockets_strerror(arg1));
1215 }
1216 /* }}} */
1217 
1218 /* {{{ Binds an open socket to a listening port, port is only specified in AF_INET family. */
1219 PHP_FUNCTION(socket_bind)
1220 {
1221 	zval					*arg1;
1222 	php_sockaddr_storage	sa_storage = {0};
1223 	struct sockaddr			*sock_type = (struct sockaddr*) &sa_storage;
1224 	php_socket				*php_sock;
1225 	char					*addr;
1226 	size_t						addr_len;
1227 	zend_long					port = 0;
1228 	zend_long					retval = 0;
1229 
1230 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os|l", &arg1, socket_ce, &addr, &addr_len, &port) == FAILURE) {
1231 		RETURN_THROWS();
1232 	}
1233 
1234 	php_sock = Z_SOCKET_P(arg1);
1235 	ENSURE_SOCKET_VALID(php_sock);
1236 
1237 	switch(php_sock->type) {
1238 		case AF_UNIX:
1239 			{
1240 				struct sockaddr_un *sa = (struct sockaddr_un *) sock_type;
1241 
1242 				sa->sun_family = AF_UNIX;
1243 
1244 				if (addr_len >= sizeof(sa->sun_path)) {
1245 					zend_argument_value_error(2, "must be less than %d", sizeof(sa->sun_path));
1246 					RETURN_THROWS();
1247 				}
1248 				memcpy(&sa->sun_path, addr, addr_len);
1249 
1250 				retval = bind(php_sock->bsd_socket, (struct sockaddr *) sa,
1251 						offsetof(struct sockaddr_un, sun_path) + addr_len);
1252 				break;
1253 			}
1254 
1255 		case AF_INET:
1256 			{
1257 				struct sockaddr_in *sa = (struct sockaddr_in *) sock_type;
1258 
1259 				sa->sin_family = AF_INET;
1260 				sa->sin_port = htons((unsigned short) port);
1261 
1262 				if (! php_set_inet_addr(sa, addr, php_sock)) {
1263 					RETURN_FALSE;
1264 				}
1265 
1266 				retval = bind(php_sock->bsd_socket, (struct sockaddr *)sa, sizeof(struct sockaddr_in));
1267 				break;
1268 			}
1269 #if HAVE_IPV6
1270 		case AF_INET6:
1271 			{
1272 				struct sockaddr_in6 *sa = (struct sockaddr_in6 *) sock_type;
1273 
1274 				sa->sin6_family = AF_INET6;
1275 				sa->sin6_port = htons((unsigned short) port);
1276 
1277 				if (! php_set_inet6_addr(sa, addr, php_sock)) {
1278 					RETURN_FALSE;
1279 				}
1280 
1281 				retval = bind(php_sock->bsd_socket, (struct sockaddr *)sa, sizeof(struct sockaddr_in6));
1282 				break;
1283 			}
1284 #endif
1285 		default:
1286 			zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET, or AF_INET6");
1287 			RETURN_THROWS();
1288 	}
1289 
1290 	if (retval != 0) {
1291 		PHP_SOCKET_ERROR(php_sock, "Unable to bind address", errno);
1292 		RETURN_FALSE;
1293 	}
1294 
1295 	RETURN_TRUE;
1296 }
1297 /* }}} */
1298 
1299 /* {{{ Receives data from a connected socket */
1300 PHP_FUNCTION(socket_recv)
1301 {
1302 	zval		*php_sock_res, *buf;
1303 	zend_string	*recv_buf;
1304 	php_socket	*php_sock;
1305 	int			retval;
1306 	zend_long		len, flags;
1307 
1308 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ozll", &php_sock_res, socket_ce, &buf, &len, &flags) == FAILURE) {
1309 		RETURN_THROWS();
1310 	}
1311 
1312 	php_sock = Z_SOCKET_P(php_sock_res);
1313 	ENSURE_SOCKET_VALID(php_sock);
1314 
1315 	/* overflow check */
1316 	if ((len + 1) < 2) {
1317 		RETURN_FALSE;
1318 	}
1319 
1320 	recv_buf = zend_string_alloc(len, 0);
1321 
1322 	if ((retval = recv(php_sock->bsd_socket, ZSTR_VAL(recv_buf), len, flags)) < 1) {
1323 		zend_string_efree(recv_buf);
1324 		ZEND_TRY_ASSIGN_REF_NULL(buf);
1325 	} else {
1326 		ZSTR_LEN(recv_buf) = retval;
1327 		ZSTR_VAL(recv_buf)[ZSTR_LEN(recv_buf)] = '\0';
1328 		ZEND_TRY_ASSIGN_REF_NEW_STR(buf, recv_buf);
1329 	}
1330 
1331 	if (retval == -1) {
1332 		PHP_SOCKET_ERROR(php_sock, "Unable to read from socket", errno);
1333 		RETURN_FALSE;
1334 	}
1335 
1336 	RETURN_LONG(retval);
1337 }
1338 /* }}} */
1339 
1340 /* {{{ Sends data to a connected socket */
1341 PHP_FUNCTION(socket_send)
1342 {
1343 	zval		*arg1;
1344 	php_socket	*php_sock;
1345 	size_t			buf_len, retval;
1346 	zend_long		len, flags;
1347 	char		*buf;
1348 
1349 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Osll", &arg1, socket_ce, &buf, &buf_len, &len, &flags) == FAILURE) {
1350 		RETURN_THROWS();
1351 	}
1352 
1353 	php_sock = Z_SOCKET_P(arg1);
1354 	ENSURE_SOCKET_VALID(php_sock);
1355 
1356 	if (len < 0) {
1357 		zend_argument_value_error(3, "must be greater than or equal to 0");
1358 		RETURN_THROWS();
1359 	}
1360 
1361 	retval = send(php_sock->bsd_socket, buf, (buf_len < (size_t)len ? buf_len : (size_t)len), flags);
1362 
1363 	if (retval == (size_t)-1) {
1364 		PHP_SOCKET_ERROR(php_sock, "Unable to write to socket", errno);
1365 		RETURN_FALSE;
1366 	}
1367 
1368 	RETURN_LONG(retval);
1369 }
1370 /* }}} */
1371 
1372 /* {{{ Receives data from a socket, connected or not */
1373 PHP_FUNCTION(socket_recvfrom)
1374 {
1375 	zval				*arg1, *arg2, *arg5, *arg6 = NULL;
1376 	php_socket			*php_sock;
1377 	struct sockaddr_un	s_un;
1378 	struct sockaddr_in	sin;
1379 #if HAVE_IPV6
1380 	struct sockaddr_in6	sin6;
1381 #endif
1382 #ifdef HAVE_INET_NTOP
1383 	char				addrbuf[INET6_ADDRSTRLEN];
1384 #endif
1385 	socklen_t			slen;
1386 	int					retval;
1387 	zend_long				arg3, arg4;
1388 	const char			*address;
1389 	zend_string			*recv_buf;
1390 
1391 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ozllz|z", &arg1, socket_ce, &arg2, &arg3, &arg4, &arg5, &arg6) == FAILURE) {
1392 		RETURN_THROWS();
1393 	}
1394 
1395 	php_sock = Z_SOCKET_P(arg1);
1396 	ENSURE_SOCKET_VALID(php_sock);
1397 
1398 	/* overflow check */
1399 	/* Shouldthrow ? */
1400 	if ((arg3 + 2) < 3) {
1401 		RETURN_FALSE;
1402 	}
1403 
1404 	recv_buf = zend_string_alloc(arg3 + 1, 0);
1405 
1406 	switch (php_sock->type) {
1407 		case AF_UNIX:
1408 			slen = sizeof(s_un);
1409 			memset(&s_un, 0, slen);
1410 			s_un.sun_family = AF_UNIX;
1411 
1412 			retval = recvfrom(php_sock->bsd_socket, ZSTR_VAL(recv_buf), arg3, arg4, (struct sockaddr *)&s_un, (socklen_t *)&slen);
1413 
1414 			if (retval < 0) {
1415 				PHP_SOCKET_ERROR(php_sock, "Unable to recvfrom", errno);
1416 				zend_string_efree(recv_buf);
1417 				RETURN_FALSE;
1418 			}
1419 			ZSTR_LEN(recv_buf) = retval;
1420 			ZSTR_VAL(recv_buf)[ZSTR_LEN(recv_buf)] = '\0';
1421 
1422 			ZEND_TRY_ASSIGN_REF_NEW_STR(arg2, recv_buf);
1423 			ZEND_TRY_ASSIGN_REF_STRING(arg5, s_un.sun_path);
1424 			break;
1425 
1426 		case AF_INET:
1427 			slen = sizeof(sin);
1428 			memset(&sin, 0, slen);
1429 			sin.sin_family = AF_INET;
1430 
1431 			if (arg6 == NULL) {
1432 				zend_string_efree(recv_buf);
1433 				WRONG_PARAM_COUNT;
1434 			}
1435 
1436 			retval = recvfrom(php_sock->bsd_socket, ZSTR_VAL(recv_buf), arg3, arg4, (struct sockaddr *)&sin, (socklen_t *)&slen);
1437 
1438 			if (retval < 0) {
1439 				PHP_SOCKET_ERROR(php_sock, "Unable to recvfrom", errno);
1440 				zend_string_efree(recv_buf);
1441 				RETURN_FALSE;
1442 			}
1443 			ZSTR_LEN(recv_buf) = retval;
1444 			ZSTR_VAL(recv_buf)[ZSTR_LEN(recv_buf)] = '\0';
1445 
1446 #ifdef HAVE_INET_NTOP
1447 			address = inet_ntop(AF_INET, &sin.sin_addr, addrbuf, sizeof(addrbuf));
1448 #else
1449 			address = inet_ntoa(sin.sin_addr);
1450 #endif
1451 
1452 			ZEND_TRY_ASSIGN_REF_NEW_STR(arg2, recv_buf);
1453 			ZEND_TRY_ASSIGN_REF_STRING(arg5, address ? address : "0.0.0.0");
1454 			ZEND_TRY_ASSIGN_REF_LONG(arg6, ntohs(sin.sin_port));
1455 			break;
1456 #if HAVE_IPV6
1457 		case AF_INET6:
1458 			slen = sizeof(sin6);
1459 			memset(&sin6, 0, slen);
1460 			sin6.sin6_family = AF_INET6;
1461 
1462 			if (arg6 == NULL) {
1463 				zend_string_efree(recv_buf);
1464 				WRONG_PARAM_COUNT;
1465 			}
1466 
1467 			retval = recvfrom(php_sock->bsd_socket, ZSTR_VAL(recv_buf), arg3, arg4, (struct sockaddr *)&sin6, (socklen_t *)&slen);
1468 
1469 			if (retval < 0) {
1470 				PHP_SOCKET_ERROR(php_sock, "unable to recvfrom", errno);
1471 				zend_string_efree(recv_buf);
1472 				RETURN_FALSE;
1473 			}
1474 			ZSTR_LEN(recv_buf) = retval;
1475 			ZSTR_VAL(recv_buf)[ZSTR_LEN(recv_buf)] = '\0';
1476 
1477 			memset(addrbuf, 0, INET6_ADDRSTRLEN);
1478 			inet_ntop(AF_INET6, &sin6.sin6_addr,  addrbuf, sizeof(addrbuf));
1479 
1480 			ZEND_TRY_ASSIGN_REF_NEW_STR(arg2, recv_buf);
1481 			ZEND_TRY_ASSIGN_REF_STRING(arg5, addrbuf[0] ? addrbuf : "::");
1482 			ZEND_TRY_ASSIGN_REF_LONG(arg6, ntohs(sin6.sin6_port));
1483 			break;
1484 #endif
1485 		default:
1486 			zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET, or AF_INET6");
1487 			RETURN_THROWS();
1488 	}
1489 
1490 	RETURN_LONG(retval);
1491 }
1492 /* }}} */
1493 
1494 /* {{{ Sends a message to a socket, whether it is connected or not */
1495 PHP_FUNCTION(socket_sendto)
1496 {
1497 	zval				*arg1;
1498 	php_socket			*php_sock;
1499 	struct sockaddr_un	s_un;
1500 	struct sockaddr_in	sin;
1501 #if HAVE_IPV6
1502 	struct sockaddr_in6	sin6;
1503 #endif
1504 	int					retval;
1505 	size_t              buf_len, addr_len;
1506 	zend_long			len, flags, port;
1507 	bool           port_is_null = 1;
1508 	char				*buf, *addr;
1509 
1510 	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) {
1511 		RETURN_THROWS();
1512 	}
1513 
1514 	php_sock = Z_SOCKET_P(arg1);
1515 	ENSURE_SOCKET_VALID(php_sock);
1516 
1517 	if (len < 0) {
1518 		zend_argument_value_error(3, "must be greater than or equal to 0");
1519 		RETURN_THROWS();
1520 	}
1521 
1522 	switch (php_sock->type) {
1523 		case AF_UNIX:
1524 			memset(&s_un, 0, sizeof(s_un));
1525 			s_un.sun_family = AF_UNIX;
1526 			snprintf(s_un.sun_path, sizeof(s_un.sun_path), "%s", addr);
1527 
1528 			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));
1529 			break;
1530 
1531 		case AF_INET:
1532 			if (port_is_null) {
1533 				zend_argument_value_error(6, "cannot be null when the socket type is AF_INET");
1534 				RETURN_THROWS();
1535 			}
1536 
1537 			memset(&sin, 0, sizeof(sin));
1538 			sin.sin_family = AF_INET;
1539 			sin.sin_port = htons((unsigned short) port);
1540 
1541 			if (! php_set_inet_addr(&sin, addr, php_sock)) {
1542 				RETURN_FALSE;
1543 			}
1544 
1545 			retval = sendto(php_sock->bsd_socket, buf, ((size_t)len > buf_len) ? buf_len : (size_t)len, flags, (struct sockaddr *) &sin, sizeof(sin));
1546 			break;
1547 #if HAVE_IPV6
1548 		case AF_INET6:
1549 			if (port_is_null) {
1550 				zend_argument_value_error(6, "cannot be null when the socket type is AF_INET6");
1551 				RETURN_THROWS();
1552 			}
1553 
1554 			memset(&sin6, 0, sizeof(sin6));
1555 			sin6.sin6_family = AF_INET6;
1556 			sin6.sin6_port = htons((unsigned short) port);
1557 
1558 			if (! php_set_inet6_addr(&sin6, addr, php_sock)) {
1559 				RETURN_FALSE;
1560 			}
1561 
1562 			retval = sendto(php_sock->bsd_socket, buf, ((size_t)len > buf_len) ? buf_len : (size_t)len, flags, (struct sockaddr *) &sin6, sizeof(sin6));
1563 			break;
1564 #endif
1565 		default:
1566 			zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET, or AF_INET6");
1567 			RETURN_THROWS();
1568 	}
1569 
1570 	if (retval == -1) {
1571 		PHP_SOCKET_ERROR(php_sock, "Unable to write to socket", errno);
1572 		RETURN_FALSE;
1573 	}
1574 
1575 	RETURN_LONG(retval);
1576 }
1577 /* }}} */
1578 
1579 /* {{{ Gets socket options for the socket */
1580 PHP_FUNCTION(socket_get_option)
1581 {
1582 	zval			*arg1;
1583 	struct linger	linger_val;
1584 	struct timeval	tv;
1585 #ifdef PHP_WIN32
1586 	int				timeout = 0;
1587 #endif
1588 	socklen_t		optlen;
1589 	php_socket		*php_sock;
1590 	int				other_val;
1591 	zend_long			level, optname;
1592 
1593 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oll", &arg1, socket_ce, &level, &optname) == FAILURE) {
1594 		RETURN_THROWS();
1595 	}
1596 
1597 	php_sock = Z_SOCKET_P(arg1);
1598 	ENSURE_SOCKET_VALID(php_sock);
1599 
1600 	if (level == IPPROTO_IP) {
1601 		switch (optname) {
1602 		case IP_MULTICAST_IF: {
1603 			struct in_addr if_addr;
1604 			unsigned int if_index;
1605 			optlen = sizeof(if_addr);
1606 			if (getsockopt(php_sock->bsd_socket, level, optname, (char*)&if_addr, &optlen) != 0) {
1607 				PHP_SOCKET_ERROR(php_sock, "Unable to retrieve socket option", errno);
1608 				RETURN_FALSE;
1609 			}
1610 			if (php_add4_to_if_index(&if_addr, php_sock, &if_index) == SUCCESS) {
1611 				RETURN_LONG((zend_long) if_index);
1612 			} else {
1613 				RETURN_FALSE;
1614 			}
1615 		}
1616 		}
1617 	}
1618 #if HAVE_IPV6
1619 	else if (level == IPPROTO_IPV6) {
1620 		int ret = php_do_getsockopt_ipv6_rfc3542(php_sock, level, optname, return_value);
1621 		if (ret == SUCCESS) {
1622 			return;
1623 		} else if (ret == FAILURE) {
1624 			RETURN_FALSE;
1625 		} /* else continue */
1626 	}
1627 #endif
1628 
1629 	if (level == IPPROTO_TCP) {
1630 		switch (optname) {
1631 #ifdef TCP_CONGESTION
1632 		case TCP_CONGESTION: {
1633 			char name[16];
1634 			optlen = sizeof(name);
1635 			if (getsockopt(php_sock->bsd_socket, level, optname, name, &optlen) != 0) {
1636 				PHP_SOCKET_ERROR(php_sock, "Unable to retrieve socket option", errno);
1637 				RETURN_FALSE;
1638 			} else {
1639 				array_init(return_value);
1640 
1641 				add_assoc_string(return_value, "name", name);
1642 				return;
1643 			}
1644 		}
1645 #endif
1646 		}
1647 	}
1648 
1649 	if (level == SOL_SOCKET) {
1650 		switch (optname) {
1651 			case SO_LINGER:
1652 				optlen = sizeof(linger_val);
1653 
1654 				if (getsockopt(php_sock->bsd_socket, level, optname, (char*)&linger_val, &optlen) != 0) {
1655 					PHP_SOCKET_ERROR(php_sock, "Unable to retrieve socket option", errno);
1656 					RETURN_FALSE;
1657 				}
1658 
1659 				array_init(return_value);
1660 				add_assoc_long(return_value, "l_onoff", linger_val.l_onoff);
1661 				add_assoc_long(return_value, "l_linger", linger_val.l_linger);
1662 				return;
1663 
1664 			case SO_RCVTIMEO:
1665 			case SO_SNDTIMEO:
1666 #ifndef PHP_WIN32
1667 				optlen = sizeof(tv);
1668 
1669 				if (getsockopt(php_sock->bsd_socket, level, optname, (char*)&tv, &optlen) != 0) {
1670 					PHP_SOCKET_ERROR(php_sock, "Unable to retrieve socket option", errno);
1671 					RETURN_FALSE;
1672 				}
1673 #else
1674 				optlen = sizeof(int);
1675 
1676 				if (getsockopt(php_sock->bsd_socket, level, optname, (char*)&timeout, &optlen) != 0) {
1677 					PHP_SOCKET_ERROR(php_sock, "Unable to retrieve socket option", errno);
1678 					RETURN_FALSE;
1679 				}
1680 
1681 				tv.tv_sec = timeout ? timeout / 1000 : 0;
1682 				tv.tv_usec = timeout ? (timeout * 1000) % 1000000 : 0;
1683 #endif
1684 
1685 				array_init(return_value);
1686 
1687 				add_assoc_long(return_value, "sec", tv.tv_sec);
1688 				add_assoc_long(return_value, "usec", tv.tv_usec);
1689 				return;
1690 #ifdef SO_MEMINFO
1691 			case SO_MEMINFO: {
1692 				uint32_t minfo[SK_MEMINFO_VARS];
1693 				optlen = sizeof(minfo);
1694 
1695 				if (getsockopt(php_sock->bsd_socket, level, optname, (char*)minfo, &optlen) != 0) {
1696 					PHP_SOCKET_ERROR(php_sock, "Unable to retrieve socket option", errno);
1697 					RETURN_FALSE;
1698 				}
1699 
1700 				if (UNEXPECTED(optlen != sizeof(minfo))) {
1701 					// unlikely since the kernel fills up the whole array if getsockopt succeeded
1702 					// but just an extra precaution in case.
1703 					php_error_docref(NULL, E_WARNING, "Unable to retrieve all socket meminfo data");
1704 					RETURN_FALSE;
1705 				}
1706 
1707 				array_init(return_value);
1708 
1709 				add_assoc_long(return_value, "rmem_alloc", minfo[SK_MEMINFO_RMEM_ALLOC]);
1710 				add_assoc_long(return_value, "rcvbuf", minfo[SK_MEMINFO_RCVBUF]);
1711 				add_assoc_long(return_value, "wmem_alloc", minfo[SK_MEMINFO_WMEM_ALLOC]);
1712 				add_assoc_long(return_value, "sndbuf", minfo[SK_MEMINFO_SNDBUF]);
1713 				add_assoc_long(return_value, "fwd_alloc", minfo[SK_MEMINFO_FWD_ALLOC]);
1714 				add_assoc_long(return_value, "wmem_queued", minfo[SK_MEMINFO_WMEM_QUEUED]);
1715 				add_assoc_long(return_value, "optmem", minfo[SK_MEMINFO_OPTMEM]);
1716 				add_assoc_long(return_value, "backlog", minfo[SK_MEMINFO_BACKLOG]);
1717 				add_assoc_long(return_value, "drops", minfo[SK_MEMINFO_DROPS]);
1718 				return;
1719 			}
1720 #endif
1721 #ifdef SO_ACCEPTFILTER
1722 			case SO_ACCEPTFILTER: {
1723 
1724 				struct accept_filter_arg af = {0};
1725 				optlen = sizeof(af);
1726 
1727 				if (getsockopt(php_sock->bsd_socket, level, optname, (char*)&af, &optlen) != 0) {
1728 					PHP_SOCKET_ERROR(php_sock, "Unable to retrieve socket option", errno);
1729 					RETURN_FALSE;
1730 				}
1731 
1732 				array_init(return_value);
1733 
1734 				add_assoc_string(return_value, "af_name", af.af_name);
1735 				return;
1736 			}
1737 #endif
1738 		}
1739 	}
1740 
1741 #ifdef SOL_FILTER
1742 	if (level == SOL_FILTER) {
1743 		switch (optname) {
1744 
1745 			case FIL_LIST: {
1746 				size_t i;
1747 				struct fil_info fi[32] = {{0}};
1748 				optlen = sizeof(fi);
1749 
1750 				if (getsockopt(php_sock->bsd_socket, level, optname, (char*)fi, &optlen) != 0) {
1751 					PHP_SOCKET_ERROR(php_sock, "Unable to retrieve socket option", errno);
1752 					RETURN_FALSE;
1753 				}
1754 
1755 				array_init(return_value);
1756 
1757 				for (i = 0; i < optlen / sizeof(struct fil_info); i++) {
1758 					add_index_string(return_value, i, fi[i].fi_name);
1759 				}
1760 
1761 				return;
1762 			}
1763 		}
1764 	}
1765 #endif
1766 
1767 	optlen = sizeof(other_val);
1768 
1769 	if (getsockopt(php_sock->bsd_socket, level, optname, (char*)&other_val, &optlen) != 0) {
1770 		PHP_SOCKET_ERROR(php_sock, "Unable to retrieve socket option", errno);
1771 		RETURN_FALSE;
1772 	}
1773 
1774 	if (optlen == 1) {
1775 		other_val = *((unsigned char *)&other_val);
1776 	}
1777 
1778 	RETURN_LONG(other_val);
1779 }
1780 /* }}} */
1781 
1782 /* {{{ Sets socket options for the socket */
1783 PHP_FUNCTION(socket_set_option)
1784 {
1785 	zval					*arg1, *arg4;
1786 	struct linger			lv;
1787 	php_socket				*php_sock;
1788 	int						ov, optlen, retval;
1789 #ifdef PHP_WIN32
1790 	int						timeout;
1791 #else
1792 	struct					timeval tv;
1793 #endif
1794 	zend_long					level, optname;
1795 	void 					*opt_ptr;
1796 	HashTable		 		*opt_ht;
1797 	zval 					*l_onoff, *l_linger;
1798 	zval		 			*sec, *usec;
1799 
1800 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ollz", &arg1, socket_ce, &level, &optname, &arg4) == FAILURE) {
1801 		RETURN_THROWS();
1802 	}
1803 
1804 	php_sock = Z_SOCKET_P(arg1);
1805 	ENSURE_SOCKET_VALID(php_sock);
1806 
1807 	set_errno(0);
1808 
1809 #define HANDLE_SUBCALL(res) \
1810 	do { \
1811 		if (res == 1) { goto default_case; } \
1812 		else if (res == SUCCESS) { RETURN_TRUE; } \
1813 		else { RETURN_FALSE; } \
1814 	} while (0)
1815 
1816 
1817 	if (level == IPPROTO_IP) {
1818 		int res = php_do_setsockopt_ip_mcast(php_sock, level, optname, arg4);
1819 		HANDLE_SUBCALL(res);
1820 	}
1821 
1822 #if HAVE_IPV6
1823 	else if (level == IPPROTO_IPV6) {
1824 		int res = php_do_setsockopt_ipv6_mcast(php_sock, level, optname, arg4);
1825 		if (res == 1) {
1826 			res = php_do_setsockopt_ipv6_rfc3542(php_sock, level, optname, arg4);
1827 		}
1828 		HANDLE_SUBCALL(res);
1829 	}
1830 #endif
1831 
1832 	if (level == IPPROTO_TCP) {
1833 		switch (optname) {
1834 #ifdef TCP_CONGESTION
1835 		case TCP_CONGESTION: {
1836 			if (Z_TYPE_P(arg4) == IS_STRING) {
1837 				opt_ptr = Z_STRVAL_P(arg4);
1838 				optlen = Z_STRLEN_P(arg4);
1839 			} else {
1840 				opt_ptr = "";
1841 				optlen = 0;
1842 			}
1843 			if (setsockopt(php_sock->bsd_socket, level, optname, opt_ptr, optlen) != 0) {
1844 				PHP_SOCKET_ERROR(php_sock, "Unable to set socket option", errno);
1845 				RETURN_FALSE;
1846 			}
1847 
1848 			RETURN_TRUE;
1849 		}
1850 #endif
1851 		}
1852 	}
1853 
1854 	switch (optname) {
1855 		case SO_LINGER: {
1856 			const char l_onoff_key[] = "l_onoff";
1857 			const char l_linger_key[] = "l_linger";
1858 
1859 			convert_to_array(arg4);
1860 			opt_ht = Z_ARRVAL_P(arg4);
1861 
1862 			if ((l_onoff = zend_hash_str_find(opt_ht, l_onoff_key, sizeof(l_onoff_key) - 1)) == NULL) {
1863 				zend_argument_value_error(4, "must have key \"%s\"", l_onoff_key);
1864 				RETURN_THROWS();
1865 			}
1866 			if ((l_linger = zend_hash_str_find(opt_ht, l_linger_key, sizeof(l_linger_key) - 1)) == NULL) {
1867 				zend_argument_value_error(4, "must have key \"%s\"", l_linger_key);
1868 				RETURN_THROWS();
1869 			}
1870 
1871 			convert_to_long(l_onoff);
1872 			convert_to_long(l_linger);
1873 
1874 			lv.l_onoff = (unsigned short)Z_LVAL_P(l_onoff);
1875 			lv.l_linger = (unsigned short)Z_LVAL_P(l_linger);
1876 
1877 			optlen = sizeof(lv);
1878 			opt_ptr = &lv;
1879 			break;
1880 		}
1881 
1882 		case SO_RCVTIMEO:
1883 		case SO_SNDTIMEO: {
1884 			const char sec_key[] = "sec";
1885 			const char usec_key[] = "usec";
1886 
1887 			convert_to_array(arg4);
1888 			opt_ht = Z_ARRVAL_P(arg4);
1889 
1890 			if ((sec = zend_hash_str_find(opt_ht, sec_key, sizeof(sec_key) - 1)) == NULL) {
1891 				zend_argument_value_error(4, "must have key \"%s\"", sec_key);
1892 				RETURN_THROWS();
1893 			}
1894 			if ((usec = zend_hash_str_find(opt_ht, usec_key, sizeof(usec_key) - 1)) == NULL) {
1895 				zend_argument_value_error(4, "must have key \"%s\"", usec_key);
1896 				RETURN_THROWS();
1897 			}
1898 
1899 			convert_to_long(sec);
1900 			convert_to_long(usec);
1901 #ifndef PHP_WIN32
1902 			tv.tv_sec = Z_LVAL_P(sec);
1903 			tv.tv_usec = Z_LVAL_P(usec);
1904 			optlen = sizeof(tv);
1905 			opt_ptr = &tv;
1906 #else
1907 			timeout = Z_LVAL_P(sec) * 1000 + Z_LVAL_P(usec) / 1000;
1908 			optlen = sizeof(int);
1909 			opt_ptr = &timeout;
1910 #endif
1911 			break;
1912 		}
1913 #ifdef SO_BINDTODEVICE
1914 		case SO_BINDTODEVICE: {
1915 			if (Z_TYPE_P(arg4) == IS_STRING) {
1916 				opt_ptr = Z_STRVAL_P(arg4);
1917 				optlen = Z_STRLEN_P(arg4);
1918 			} else {
1919 				opt_ptr = "";
1920 				optlen = 0;
1921 			}
1922 			break;
1923 		}
1924 #endif
1925 
1926 #ifdef SO_ACCEPTFILTER
1927 		case SO_ACCEPTFILTER: {
1928 			if (Z_TYPE_P(arg4) != IS_STRING) {
1929 				php_error_docref(NULL, E_WARNING, "Invalid filter argument type");
1930 				RETURN_FALSE;
1931 			}
1932 			struct accept_filter_arg af = {0};
1933 			strlcpy(af.af_name, Z_STRVAL_P(arg4), sizeof(af.af_name));
1934 			opt_ptr = &af;
1935 			optlen = sizeof(af);
1936 			break;
1937 		}
1938 #endif
1939 
1940 #ifdef FIL_ATTACH
1941 		case FIL_ATTACH:
1942 		case FIL_DETACH: {
1943 			if (level != SOL_FILTER) {
1944 				php_error_docref(NULL, E_WARNING, "Invalid level");
1945 				RETURN_FALSE;
1946 			}
1947 			if (Z_TYPE_P(arg4) != IS_STRING) {
1948 				php_error_docref(NULL, E_WARNING, "Invalid filter argument type");
1949 				RETURN_FALSE;
1950 			}
1951 			opt_ptr = Z_STRVAL_P(arg4);
1952 			optlen = Z_STRLEN_P(arg4);
1953 			break;
1954 		}
1955 #endif
1956 
1957 		default:
1958 default_case:
1959 			convert_to_long(arg4);
1960 			ov = Z_LVAL_P(arg4);
1961 
1962 			optlen = sizeof(ov);
1963 			opt_ptr = &ov;
1964 			break;
1965 	}
1966 
1967 	retval = setsockopt(php_sock->bsd_socket, level, optname, opt_ptr, optlen);
1968 	if (retval != 0) {
1969 		PHP_SOCKET_ERROR(php_sock, "Unable to set socket option", errno);
1970 		RETURN_FALSE;
1971 	}
1972 
1973 	RETURN_TRUE;
1974 }
1975 /* }}} */
1976 
1977 #ifdef HAVE_SOCKETPAIR
1978 /* {{{ Creates a pair of indistinguishable sockets and stores them in fds. */
1979 PHP_FUNCTION(socket_create_pair)
1980 {
1981 	zval		retval[2], *fds_array_zval;
1982 	php_socket	*php_sock[2];
1983 	PHP_SOCKET	fds_array[2];
1984 	zend_long		domain, type, protocol;
1985 
1986 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "lllz", &domain, &type, &protocol, &fds_array_zval) == FAILURE) {
1987 		RETURN_THROWS();
1988 	}
1989 
1990 	if (domain != AF_INET
1991 #if HAVE_IPV6
1992 		&& domain != AF_INET6
1993 #endif
1994 		&& domain != AF_UNIX) {
1995 		zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET6, or AF_INET");
1996 		RETURN_THROWS();
1997 	}
1998 
1999 	if (type > 10) {
2000 		zend_argument_value_error(2, "must be one of SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET,"
2001 			" SOCK_RAW, or SOCK_RDM");
2002 		RETURN_THROWS();
2003 	}
2004 
2005 	object_init_ex(&retval[0], socket_ce);
2006 	php_sock[0] = Z_SOCKET_P(&retval[0]);
2007 
2008 	object_init_ex(&retval[1], socket_ce);
2009 	php_sock[1] = Z_SOCKET_P(&retval[1]);
2010 
2011 	if (socketpair(domain, type, protocol, fds_array) != 0) {
2012 		SOCKETS_G(last_error) = errno;
2013 		php_error_docref(NULL, E_WARNING, "Unable to create socket pair [%d]: %s", errno, sockets_strerror(errno));
2014 		zval_ptr_dtor(&retval[0]);
2015 		zval_ptr_dtor(&retval[1]);
2016 		RETURN_FALSE;
2017 	}
2018 
2019 	fds_array_zval = zend_try_array_init(fds_array_zval);
2020 	if (!fds_array_zval) {
2021 		zval_ptr_dtor(&retval[0]);
2022 		zval_ptr_dtor(&retval[1]);
2023 		RETURN_THROWS();
2024 	}
2025 
2026 	php_sock[0]->bsd_socket = fds_array[0];
2027 	php_sock[1]->bsd_socket = fds_array[1];
2028 	php_sock[0]->type		= domain;
2029 	php_sock[1]->type		= domain;
2030 	php_sock[0]->error		= 0;
2031 	php_sock[1]->error		= 0;
2032 	php_sock[0]->blocking	= 1;
2033 	php_sock[1]->blocking	= 1;
2034 
2035 	add_index_zval(fds_array_zval, 0, &retval[0]);
2036 	add_index_zval(fds_array_zval, 1, &retval[1]);
2037 
2038 	RETURN_TRUE;
2039 }
2040 /* }}} */
2041 #endif
2042 
2043 #ifdef HAVE_SHUTDOWN
2044 /* {{{ Shuts down a socket for receiving, sending, or both. */
2045 PHP_FUNCTION(socket_shutdown)
2046 {
2047 	zval		*arg1;
2048 	zend_long		how_shutdown = 2;
2049 	php_socket	*php_sock;
2050 
2051 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|l", &arg1, socket_ce, &how_shutdown) == FAILURE) {
2052 		RETURN_THROWS();
2053 	}
2054 
2055 	php_sock = Z_SOCKET_P(arg1);
2056 	ENSURE_SOCKET_VALID(php_sock);
2057 
2058 	if (shutdown(php_sock->bsd_socket, how_shutdown) != 0) {
2059 		PHP_SOCKET_ERROR(php_sock, "Unable to shutdown socket", errno);
2060 		RETURN_FALSE;
2061 	}
2062 
2063 	RETURN_TRUE;
2064 }
2065 /* }}} */
2066 #endif
2067 
2068 /* {{{ Returns the last socket error (either the last used or the provided socket resource) */
2069 PHP_FUNCTION(socket_last_error)
2070 {
2071 	zval		*arg1 = NULL;
2072 	php_socket	*php_sock;
2073 
2074 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|O!", &arg1, socket_ce) == FAILURE) {
2075 		RETURN_THROWS();
2076 	}
2077 
2078 	if (arg1) {
2079 		php_sock = Z_SOCKET_P(arg1);
2080 		ENSURE_SOCKET_VALID(php_sock);
2081 
2082 		RETVAL_LONG(php_sock->error);
2083 	} else {
2084 		RETVAL_LONG(SOCKETS_G(last_error));
2085 	}
2086 }
2087 /* }}} */
2088 
2089 /* {{{ Clears the error on the socket or the last error code. */
2090 PHP_FUNCTION(socket_clear_error)
2091 {
2092 	zval		*arg1 = NULL;
2093 	php_socket	*php_sock;
2094 
2095 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|O!", &arg1, socket_ce) == FAILURE) {
2096 		RETURN_THROWS();
2097 	}
2098 
2099 	if (arg1) {
2100 		php_sock = Z_SOCKET_P(arg1);
2101 		ENSURE_SOCKET_VALID(php_sock);
2102 
2103 		php_sock->error = 0;
2104 	} else {
2105 		SOCKETS_G(last_error) = 0;
2106 	}
2107 
2108 	return;
2109 }
2110 /* }}} */
2111 
2112 int socket_import_file_descriptor(PHP_SOCKET socket, php_socket *retsock)
2113 {
2114 #ifdef SO_DOMAIN
2115 	int						type;
2116 	socklen_t				type_len = sizeof(type);
2117 #endif
2118 	php_sockaddr_storage	addr;
2119 	socklen_t				addr_len = sizeof(addr);
2120 #ifndef PHP_WIN32
2121 	int					 t;
2122 #endif
2123 
2124 	retsock->bsd_socket = socket;
2125 
2126 	/* determine family */
2127 #ifdef SO_DOMAIN
2128 	if (getsockopt(socket, SOL_SOCKET, SO_DOMAIN, &type, &type_len) == 0) {
2129 		retsock->type = type;
2130 	} else
2131 #endif
2132 	if (getsockname(socket, (struct sockaddr*)&addr, &addr_len) == 0) {
2133 		retsock->type = addr.ss_family;
2134 	} else {
2135 		PHP_SOCKET_ERROR(retsock, "Unable to obtain socket family", errno);
2136 		return 0;
2137 	}
2138 
2139 	/* determine blocking mode */
2140 #ifndef PHP_WIN32
2141 	t = fcntl(socket, F_GETFL);
2142 	if (t == -1) {
2143 		PHP_SOCKET_ERROR(retsock, "Unable to obtain blocking state", errno);
2144 		return 0;
2145 	} else {
2146 		retsock->blocking = !(t & O_NONBLOCK);
2147 	}
2148 #endif
2149 
2150 	return 1;
2151 }
2152 
2153 /* {{{ Imports a stream that encapsulates a socket into a socket extension resource. */
2154 PHP_FUNCTION(socket_import_stream)
2155 {
2156 	zval				 *zstream;
2157 	php_stream			 *stream;
2158 	php_socket			 *retsock = NULL;
2159 	PHP_SOCKET			 socket; /* fd */
2160 
2161 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zstream) == FAILURE) {
2162 		RETURN_THROWS();
2163 	}
2164 	php_stream_from_zval(stream, zstream);
2165 
2166 	if (php_stream_cast(stream, PHP_STREAM_AS_SOCKETD, (void**)&socket, 1)) {
2167 		/* error supposedly already shown */
2168 		RETURN_FALSE;
2169 	}
2170 
2171 	object_init_ex(return_value, socket_ce);
2172 	retsock = Z_SOCKET_P(return_value);
2173 
2174 	if (!socket_import_file_descriptor(socket, retsock)) {
2175 		zval_ptr_dtor(return_value);
2176 		RETURN_FALSE;
2177 	}
2178 
2179 #ifdef PHP_WIN32
2180 	/* on windows, check if the stream is a socket stream and read its
2181 	 * private data; otherwise assume it's in non-blocking mode */
2182 	if (php_stream_is(stream, PHP_STREAM_IS_SOCKET)) {
2183 		retsock->blocking =
2184 				((php_netstream_data_t *)stream->abstract)->is_blocked;
2185 	} else {
2186 		retsock->blocking = 1;
2187 	}
2188 #endif
2189 
2190 	/* hold a zval reference to the stream (holding a php_stream* directly could
2191 	 * also be done, but this makes socket_export_stream a bit simpler) */
2192 	ZVAL_COPY(&retsock->zstream, zstream);
2193 
2194 	php_stream_set_option(stream, PHP_STREAM_OPTION_READ_BUFFER, PHP_STREAM_BUFFER_NONE, NULL);
2195 }
2196 /* }}} */
2197 
2198 /* {{{ Exports a socket extension resource into a stream that encapsulates a socket. */
2199 PHP_FUNCTION(socket_export_stream)
2200 {
2201 	zval *zsocket;
2202 	php_socket *socket;
2203 	php_stream *stream = NULL;
2204 	php_netstream_data_t *stream_data;
2205 	const char *protocol = NULL;
2206 	size_t protocollen = 0;
2207 
2208 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &zsocket, socket_ce) == FAILURE) {
2209 		RETURN_THROWS();
2210 	}
2211 
2212 	socket = Z_SOCKET_P(zsocket);
2213 	ENSURE_SOCKET_VALID(socket);
2214 
2215 	/* Either we already exported a stream or the socket came from an import,
2216 	 * just return the existing stream */
2217 	if (!Z_ISUNDEF(socket->zstream)) {
2218 		RETURN_COPY(&socket->zstream);
2219 	}
2220 
2221 	/* Determine if socket is using a protocol with one of the default registered
2222 	 * socket stream wrappers */
2223 	if (socket->type == PF_INET
2224 #if HAVE_IPV6
2225 		 || socket->type == PF_INET6
2226 #endif
2227 	) {
2228 		int protoid;
2229 		socklen_t protoidlen = sizeof(protoid);
2230 
2231 		getsockopt(socket->bsd_socket, SOL_SOCKET, SO_TYPE, (char *) &protoid, &protoidlen);
2232 
2233 		if (protoid == SOCK_STREAM) {
2234 			/* SO_PROTOCOL is not (yet?) supported on OS X, so lets assume it's TCP there */
2235 #ifdef SO_PROTOCOL
2236 			protoidlen = sizeof(protoid);
2237 			getsockopt(socket->bsd_socket, SOL_SOCKET, SO_PROTOCOL, (char *) &protoid, &protoidlen);
2238 			if (protoid == IPPROTO_TCP)
2239 #endif
2240 			{
2241 				protocol = "tcp://";
2242 				protocollen = sizeof("tcp://") - 1;
2243 			}
2244 		} else if (protoid == SOCK_DGRAM) {
2245 			protocol = "udp://";
2246 			protocollen = sizeof("udp://") - 1;
2247 		}
2248 #ifdef PF_UNIX
2249 	} else if (socket->type == PF_UNIX) {
2250 		int type;
2251 		socklen_t typelen = sizeof(type);
2252 
2253 		getsockopt(socket->bsd_socket, SOL_SOCKET, SO_TYPE, (char *) &type, &typelen);
2254 
2255 		if (type == SOCK_STREAM) {
2256 			protocol = "unix://";
2257 			protocollen = sizeof("unix://") - 1;
2258 		} else if (type == SOCK_DGRAM) {
2259 			protocol = "udg://";
2260 			protocollen = sizeof("udg://") - 1;
2261 		}
2262 #endif
2263 	}
2264 
2265 	/* Try to get a stream with the registered sockops for the protocol in use
2266 	 * We don't want streams to actually *do* anything though, so don't give it
2267 	 * anything apart from the protocol */
2268 	if (protocol != NULL) {
2269 		stream = php_stream_xport_create(protocol, protocollen, 0, 0, NULL, NULL, NULL, NULL, NULL);
2270 	}
2271 
2272 	/* Fall back to creating a generic socket stream */
2273 	if (stream == NULL) {
2274 		stream = php_stream_sock_open_from_socket(socket->bsd_socket, 0);
2275 
2276 		if (stream == NULL) {
2277 			php_error_docref(NULL, E_WARNING, "Failed to create stream");
2278 			RETURN_FALSE;
2279 		}
2280 	}
2281 
2282 	stream_data = (php_netstream_data_t *) stream->abstract;
2283 	stream_data->socket = socket->bsd_socket;
2284 	stream_data->is_blocked = socket->blocking;
2285 	stream_data->timeout.tv_sec = FG(default_socket_timeout);
2286 	stream_data->timeout.tv_usec = 0;
2287 
2288 	php_stream_to_zval(stream, &socket->zstream);
2289 
2290 	RETURN_COPY(&socket->zstream);
2291 }
2292 /* }}} */
2293 
2294 /* {{{ Gets array with contents of getaddrinfo about the given hostname. */
2295 PHP_FUNCTION(socket_addrinfo_lookup)
2296 {
2297 	char *service = NULL;
2298 	size_t service_len = 0;
2299 	zend_string *hostname, *key;
2300 	zval *hint, *zhints = NULL;
2301 
2302 	struct addrinfo hints, *result, *rp;
2303 	php_addrinfo *res;
2304 
2305 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|s!a", &hostname, &service, &service_len, &zhints) == FAILURE) {
2306 		RETURN_THROWS();
2307 	}
2308 
2309 	memset(&hints, 0, sizeof(hints));
2310 
2311 	if (zhints && !HT_IS_PACKED(Z_ARRVAL_P(zhints))) {
2312 		ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(zhints), key, hint) {
2313 			if (key) {
2314 				if (zend_string_equals_literal(key, "ai_flags")) {
2315 					hints.ai_flags = zval_get_long(hint);
2316 				} else if (zend_string_equals_literal(key, "ai_socktype")) {
2317 					hints.ai_socktype = zval_get_long(hint);
2318 				} else if (zend_string_equals_literal(key, "ai_protocol")) {
2319 					hints.ai_protocol = zval_get_long(hint);
2320 				} else if (zend_string_equals_literal(key, "ai_family")) {
2321 					hints.ai_family = zval_get_long(hint);
2322 				} else {
2323 					zend_argument_value_error(3, "must only contain array keys \"ai_flags\", \"ai_socktype\", "
2324 						"\"ai_protocol\", or \"ai_family\"");
2325 					RETURN_THROWS();
2326 				}
2327 			}
2328 		} ZEND_HASH_FOREACH_END();
2329 	}
2330 
2331 	if (getaddrinfo(ZSTR_VAL(hostname), service, &hints, &result) != 0) {
2332 		RETURN_FALSE;
2333 	}
2334 
2335 	array_init(return_value);
2336 
2337 	for (rp = result; rp != NULL; rp = rp->ai_next) {
2338 		if (rp->ai_family != AF_UNSPEC) {
2339 			zval zaddr;
2340 
2341 			object_init_ex(&zaddr, address_info_ce);
2342 			res = Z_ADDRESS_INFO_P(&zaddr);
2343 
2344 			memcpy(&res->addrinfo, rp, sizeof(struct addrinfo));
2345 
2346 			res->addrinfo.ai_addr = emalloc(rp->ai_addrlen);
2347 			memcpy(res->addrinfo.ai_addr, rp->ai_addr, rp->ai_addrlen);
2348 
2349 			if (rp->ai_canonname != NULL) {
2350 				res->addrinfo.ai_canonname = estrdup(rp->ai_canonname);
2351 			}
2352 
2353 			add_next_index_zval(return_value, &zaddr);
2354 		}
2355 	}
2356 
2357 	freeaddrinfo(result);
2358 }
2359 /* }}} */
2360 
2361 /* {{{ Creates and binds to a socket from a given addrinfo resource */
2362 PHP_FUNCTION(socket_addrinfo_bind)
2363 {
2364 	zval			*arg1;
2365 	int				retval;
2366 	php_addrinfo	*ai;
2367 	php_socket		*php_sock;
2368 
2369 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &arg1, address_info_ce) == FAILURE) {
2370 		RETURN_THROWS();
2371 	}
2372 
2373 	ai = Z_ADDRESS_INFO_P(arg1);
2374 
2375 	object_init_ex(return_value, socket_ce);
2376 	php_sock = Z_SOCKET_P(return_value);
2377 
2378 	php_sock->bsd_socket = socket(ai->addrinfo.ai_family, ai->addrinfo.ai_socktype, ai->addrinfo.ai_protocol);
2379 	php_sock->type = ai->addrinfo.ai_family;
2380 
2381 	if (IS_INVALID_SOCKET(php_sock)) {
2382 		SOCKETS_G(last_error) = errno;
2383 		php_error_docref(NULL, E_WARNING, "Unable to create socket [%d]: %s", errno, sockets_strerror(errno));
2384 		zval_ptr_dtor(return_value);
2385 		RETURN_FALSE;
2386 	}
2387 
2388 	php_sock->error = 0;
2389 	php_sock->blocking = 1;
2390 
2391 	switch(php_sock->type) {
2392 		case AF_UNIX:
2393 			{
2394 				// AF_UNIX sockets via getaddrino are not implemented due to security problems
2395 				close(php_sock->bsd_socket);
2396 				zval_ptr_dtor(return_value);
2397 				RETURN_FALSE;
2398 			}
2399 
2400 		case AF_INET:
2401 #if HAVE_IPV6
2402 		case AF_INET6:
2403 #endif
2404 			{
2405 				retval = bind(php_sock->bsd_socket, ai->addrinfo.ai_addr, ai->addrinfo.ai_addrlen);
2406 				break;
2407 			}
2408 		default:
2409 			close(php_sock->bsd_socket);
2410 			zval_ptr_dtor(return_value);
2411 			zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET, or AF_INET6");
2412 			RETURN_THROWS();
2413 	}
2414 
2415 	if (retval != 0) {
2416 		PHP_SOCKET_ERROR(php_sock, "Unable to bind address", errno);
2417 		close(php_sock->bsd_socket);
2418 		zval_ptr_dtor(return_value);
2419 		RETURN_FALSE;
2420 	}
2421 }
2422 /* }}} */
2423 
2424 /* {{{ Creates and connects to a socket from a given addrinfo resource */
2425 PHP_FUNCTION(socket_addrinfo_connect)
2426 {
2427 	zval			*arg1;
2428 	int				retval;
2429 	php_addrinfo	*ai;
2430 	php_socket		*php_sock;
2431 
2432 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &arg1, address_info_ce) == FAILURE) {
2433 		RETURN_THROWS();
2434 	}
2435 
2436 	ai = Z_ADDRESS_INFO_P(arg1);
2437 
2438 	object_init_ex(return_value, socket_ce);
2439 	php_sock = Z_SOCKET_P(return_value);
2440 
2441 	php_sock->bsd_socket = socket(ai->addrinfo.ai_family, ai->addrinfo.ai_socktype, ai->addrinfo.ai_protocol);
2442 	php_sock->type = ai->addrinfo.ai_family;
2443 
2444 	if (IS_INVALID_SOCKET(php_sock)) {
2445 		SOCKETS_G(last_error) = errno;
2446 		php_error_docref(NULL, E_WARNING, "Unable to create socket [%d]: %s", errno, sockets_strerror(errno));
2447 		zval_ptr_dtor(return_value);
2448 		RETURN_FALSE;
2449 	}
2450 
2451 	php_sock->error = 0;
2452 	php_sock->blocking = 1;
2453 
2454 	switch(php_sock->type) {
2455 		case AF_UNIX:
2456 			{
2457 				// AF_UNIX sockets via getaddrino are not implemented due to security problems
2458 				close(php_sock->bsd_socket);
2459 				zval_ptr_dtor(return_value);
2460 				RETURN_FALSE;
2461 			}
2462 
2463 		case AF_INET:
2464 #if HAVE_IPV6
2465 		case AF_INET6:
2466 #endif
2467 			{
2468 				retval = connect(php_sock->bsd_socket, ai->addrinfo.ai_addr, ai->addrinfo.ai_addrlen);
2469 				break;
2470 			}
2471 		default:
2472 			zend_argument_value_error(1, "socket type must be one of AF_UNIX, AF_INET, or AF_INET6");
2473 			close(php_sock->bsd_socket);
2474 			zval_ptr_dtor(return_value);
2475 			RETURN_THROWS();
2476 	}
2477 
2478 	if (retval != 0) {
2479 		PHP_SOCKET_ERROR(php_sock, "Unable to connect address", errno);
2480 		close(php_sock->bsd_socket);
2481 		zval_ptr_dtor(return_value);
2482 		RETURN_FALSE;
2483 	}
2484 }
2485 /* }}} */
2486 
2487 /* {{{ Creates and connects to a socket from a given addrinfo resource */
2488 PHP_FUNCTION(socket_addrinfo_explain)
2489 {
2490 	zval			*arg1, sockaddr;
2491 	php_addrinfo	*ai;
2492 
2493 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &arg1, address_info_ce) == FAILURE) {
2494 		RETURN_THROWS();
2495 	}
2496 
2497 	ai = Z_ADDRESS_INFO_P(arg1);
2498 
2499 	array_init(return_value);
2500 
2501 	add_assoc_long(return_value, "ai_flags", ai->addrinfo.ai_flags);
2502 	add_assoc_long(return_value, "ai_family", ai->addrinfo.ai_family);
2503 	add_assoc_long(return_value, "ai_socktype", ai->addrinfo.ai_socktype);
2504 	add_assoc_long(return_value, "ai_protocol", ai->addrinfo.ai_protocol);
2505 	if (ai->addrinfo.ai_canonname != NULL) {
2506 		add_assoc_string(return_value, "ai_canonname", ai->addrinfo.ai_canonname);
2507 	}
2508 
2509 	array_init(&sockaddr);
2510 	switch (ai->addrinfo.ai_family) {
2511 		case AF_INET:
2512 			{
2513 				struct sockaddr_in *sa = (struct sockaddr_in *) ai->addrinfo.ai_addr;
2514 				char addr[INET_ADDRSTRLEN];
2515 
2516 				add_assoc_long(&sockaddr, "sin_port", ntohs((unsigned short) sa->sin_port));
2517 				inet_ntop(ai->addrinfo.ai_family, &sa->sin_addr, addr, sizeof(addr));
2518 				add_assoc_string(&sockaddr, "sin_addr", addr);
2519 				break;
2520 			}
2521 #if HAVE_IPV6
2522 		case AF_INET6:
2523 			{
2524 				struct sockaddr_in6 *sa = (struct sockaddr_in6 *) ai->addrinfo.ai_addr;
2525 				char addr[INET6_ADDRSTRLEN];
2526 
2527 				add_assoc_long(&sockaddr, "sin6_port", ntohs((unsigned short) sa->sin6_port));
2528 				inet_ntop(ai->addrinfo.ai_family, &sa->sin6_addr, addr, sizeof(addr));
2529 				add_assoc_string(&sockaddr, "sin6_addr", addr);
2530 				break;
2531 			}
2532 #endif
2533 	}
2534 
2535 	add_assoc_zval(return_value, "ai_addr", &sockaddr);
2536 }
2537 /* }}} */
2538 
2539 #ifdef PHP_WIN32
2540 
2541 /* {{{ Exports the network socket information suitable to be used in another process and returns the info id. */
2542 PHP_FUNCTION(socket_wsaprotocol_info_export)
2543 {
2544 	WSAPROTOCOL_INFO wi;
2545 	zval *zsocket;
2546 	php_socket *socket;
2547 	zend_long target_pid;
2548 	zend_string *seg_name;
2549 	HANDLE map;
2550 
2551 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &zsocket, socket_ce, &target_pid) == FAILURE) {
2552 		RETURN_THROWS();
2553 	}
2554 
2555 	socket = Z_SOCKET_P(zsocket);
2556 	ENSURE_SOCKET_VALID(socket);
2557 
2558 	if (SOCKET_ERROR == WSADuplicateSocket(socket->bsd_socket, (DWORD)target_pid, &wi)) {
2559 		DWORD err = WSAGetLastError();
2560 		char *buf = php_win32_error_to_msg(err);
2561 
2562 		if (!buf[0]) {
2563 			php_error_docref(NULL, E_WARNING, "Unable to export WSA protocol info [0x%08lx]", err);
2564 		} else {
2565 			php_error_docref(NULL, E_WARNING, "Unable to export WSA protocol info [0x%08lx]: %s", err, buf);
2566 		}
2567 
2568 		php_win32_error_msg_free(buf);
2569 
2570 		RETURN_FALSE;
2571 	}
2572 
2573 	seg_name = zend_strpprintf(0, "php_wsa_for_%u", SOCKETS_G(wsa_child_count)++);
2574 	map = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(WSAPROTOCOL_INFO), ZSTR_VAL(seg_name));
2575 	if (NULL != map) {
2576 		LPVOID view = MapViewOfFile(map, FILE_MAP_WRITE, 0, 0, 0);
2577 		if (view) {
2578 			memcpy(view, &wi, sizeof(wi));
2579 			UnmapViewOfFile(view);
2580 			zend_hash_add_ptr(&(SOCKETS_G(wsa_info)), seg_name, map);
2581 			RETURN_STR(seg_name);
2582 		} else {
2583 			DWORD err = GetLastError();
2584 			php_error_docref(NULL, E_WARNING, "Unable to map file view [0x%08lx]", err);
2585 		}
2586 	} else {
2587 		DWORD err = GetLastError();
2588 		php_error_docref(NULL, E_WARNING, "Unable to create file mapping [0x%08lx]", err);
2589 	}
2590 	zend_string_release_ex(seg_name, 0);
2591 
2592 	RETURN_FALSE;
2593 }
2594 /* }}} */
2595 
2596 /* {{{ Imports the network socket information using the supplied id and creates a new socket on its base. */
2597 PHP_FUNCTION(socket_wsaprotocol_info_import)
2598 {
2599 	char *id;
2600 	size_t id_len;
2601 	WSAPROTOCOL_INFO wi;
2602 	PHP_SOCKET sock;
2603 	php_socket	*php_sock;
2604 	HANDLE map;
2605 
2606 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &id, &id_len) == FAILURE) {
2607 		RETURN_THROWS();
2608 	}
2609 
2610 	map = OpenFileMapping(FILE_MAP_READ, FALSE, id);
2611 	if (map) {
2612 		LPVOID view = MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0);
2613 		if (view) {
2614 			memcpy(&wi, view, sizeof(WSAPROTOCOL_INFO));
2615 			UnmapViewOfFile(view);
2616 		} else {
2617 			DWORD err = GetLastError();
2618 			php_error_docref(NULL, E_WARNING, "Unable to map file view [0x%08lx]", err);
2619 			RETURN_FALSE;
2620 		}
2621 		CloseHandle(map);
2622 	} else {
2623 		DWORD err = GetLastError();
2624 		php_error_docref(NULL, E_WARNING, "Unable to open file mapping [0x%08lx]", err);
2625 		RETURN_FALSE;
2626 	}
2627 
2628 	sock = WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, &wi, 0, 0);
2629 	if (INVALID_SOCKET == sock) {
2630 		DWORD err = WSAGetLastError();
2631 		char *buf = php_win32_error_to_msg(err);
2632 
2633 		if (!buf[0]) {
2634 			php_error_docref(NULL, E_WARNING, "Unable to import WSA protocol info [0x%08lx]", err);
2635 		} else {
2636 			php_error_docref(NULL, E_WARNING, "Unable to import WSA protocol info [0x%08lx]: %s", err, buf);
2637 		}
2638 
2639 		php_win32_error_msg_free(buf);
2640 
2641 		RETURN_FALSE;
2642 	}
2643 
2644 	object_init_ex(return_value, socket_ce);
2645 	php_sock = Z_SOCKET_P(return_value);
2646 
2647 	php_sock->bsd_socket = sock;
2648 	php_sock->type = wi.iAddressFamily;
2649 	php_sock->error = 0;
2650 	php_sock->blocking = 1;
2651 }
2652 /* }}} */
2653 
2654 /* {{{ Frees the exported info and corresponding resources using the supplied id. */
2655 PHP_FUNCTION(socket_wsaprotocol_info_release)
2656 {
2657 	char *id;
2658 	size_t id_len;
2659 
2660 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &id, &id_len) == FAILURE) {
2661 		RETURN_THROWS();
2662 	}
2663 
2664 	RETURN_BOOL(SUCCESS == zend_hash_str_del(&(SOCKETS_G(wsa_info)), id, id_len));
2665 }
2666 /* }}} */
2667 #endif
2668