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