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