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