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