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