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