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