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