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