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 = ⁡
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