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