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 | Author: Wez Furlong <wez@thebrainroom.com> |
14 +----------------------------------------------------------------------+
15 */
16
17 #include "php.h"
18 #include "ext/standard/file.h"
19 #include "streams/php_streams_int.h"
20 #include "php_network.h"
21
22 #if defined(PHP_WIN32) || defined(__riscos__)
23 # undef AF_UNIX
24 #endif
25
26 #ifdef AF_UNIX
27 #include <sys/un.h>
28 #endif
29
30 #ifndef MSG_DONTWAIT
31 # define MSG_DONTWAIT 0
32 #endif
33
34 #ifndef MSG_PEEK
35 # define MSG_PEEK 0
36 #endif
37
38 #ifdef PHP_WIN32
39 /* send/recv family on windows expects int */
40 # define XP_SOCK_BUF_SIZE(sz) (((sz) > INT_MAX) ? INT_MAX : (int)(sz))
41 #else
42 # define XP_SOCK_BUF_SIZE(sz) (sz)
43 #endif
44
45 const php_stream_ops php_stream_generic_socket_ops;
46 PHPAPI const php_stream_ops php_stream_socket_ops;
47 const php_stream_ops php_stream_udp_socket_ops;
48 #ifdef AF_UNIX
49 const php_stream_ops php_stream_unix_socket_ops;
50 const php_stream_ops php_stream_unixdg_socket_ops;
51 #endif
52
53
54 static int php_tcp_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam);
55
56 /* {{{ Generic socket stream operations */
php_sockop_write(php_stream * stream,const char * buf,size_t count)57 static ssize_t php_sockop_write(php_stream *stream, const char *buf, size_t count)
58 {
59 php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
60 ssize_t didwrite;
61 struct timeval *ptimeout;
62
63 if (!sock || sock->socket == -1) {
64 return 0;
65 }
66
67 if (sock->timeout.tv_sec == -1)
68 ptimeout = NULL;
69 else
70 ptimeout = &sock->timeout;
71
72 retry:
73 didwrite = send(sock->socket, buf, XP_SOCK_BUF_SIZE(count), (sock->is_blocked && ptimeout) ? MSG_DONTWAIT : 0);
74
75 if (didwrite <= 0) {
76 char *estr;
77 int err = php_socket_errno();
78
79 if (PHP_IS_TRANSIENT_ERROR(err)) {
80 if (sock->is_blocked) {
81 int retval;
82
83 sock->timeout_event = 0;
84
85 do {
86 retval = php_pollfd_for(sock->socket, POLLOUT, ptimeout);
87
88 if (retval == 0) {
89 sock->timeout_event = 1;
90 break;
91 }
92
93 if (retval > 0) {
94 /* writable now; retry */
95 goto retry;
96 }
97
98 err = php_socket_errno();
99 } while (err == EINTR);
100 } else {
101 /* EWOULDBLOCK/EAGAIN is not an error for a non-blocking stream.
102 * Report zero byte write instead. */
103 return 0;
104 }
105 }
106
107 if (!(stream->flags & PHP_STREAM_FLAG_SUPPRESS_ERRORS)) {
108 estr = php_socket_strerror(err, NULL, 0);
109 php_error_docref(NULL, E_NOTICE,
110 "Send of " ZEND_LONG_FMT " bytes failed with errno=%d %s",
111 (zend_long)count, err, estr);
112 efree(estr);
113 }
114 }
115
116 if (didwrite > 0) {
117 php_stream_notify_progress_increment(PHP_STREAM_CONTEXT(stream), didwrite, 0);
118 }
119
120 return didwrite;
121 }
122
php_sock_stream_wait_for_data(php_stream * stream,php_netstream_data_t * sock)123 static void php_sock_stream_wait_for_data(php_stream *stream, php_netstream_data_t *sock)
124 {
125 int retval;
126 struct timeval *ptimeout;
127
128 if (!sock || sock->socket == -1) {
129 return;
130 }
131
132 sock->timeout_event = 0;
133
134 if (sock->timeout.tv_sec == -1)
135 ptimeout = NULL;
136 else
137 ptimeout = &sock->timeout;
138
139 while(1) {
140 retval = php_pollfd_for(sock->socket, PHP_POLLREADABLE, ptimeout);
141
142 if (retval == 0)
143 sock->timeout_event = 1;
144
145 if (retval >= 0)
146 break;
147
148 if (php_socket_errno() != EINTR)
149 break;
150 }
151 }
152
php_sockop_read(php_stream * stream,char * buf,size_t count)153 static ssize_t php_sockop_read(php_stream *stream, char *buf, size_t count)
154 {
155 php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
156 ssize_t nr_bytes = 0;
157 int err;
158
159 if (!sock || sock->socket == -1) {
160 return -1;
161 }
162
163 if (sock->is_blocked) {
164 php_sock_stream_wait_for_data(stream, sock);
165 if (sock->timeout_event)
166 return -1;
167 }
168
169 nr_bytes = recv(sock->socket, buf, XP_SOCK_BUF_SIZE(count), (sock->is_blocked && sock->timeout.tv_sec != -1) ? MSG_DONTWAIT : 0);
170 err = php_socket_errno();
171
172 if (nr_bytes < 0) {
173 if (PHP_IS_TRANSIENT_ERROR(err)) {
174 nr_bytes = 0;
175 } else {
176 stream->eof = 1;
177 }
178 } else if (nr_bytes == 0) {
179 stream->eof = 1;
180 }
181
182 if (nr_bytes > 0) {
183 php_stream_notify_progress_increment(PHP_STREAM_CONTEXT(stream), nr_bytes, 0);
184 }
185
186 return nr_bytes;
187 }
188
189
php_sockop_close(php_stream * stream,int close_handle)190 static int php_sockop_close(php_stream *stream, int close_handle)
191 {
192 php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
193 #ifdef PHP_WIN32
194 int n;
195 #endif
196
197 if (!sock) {
198 return 0;
199 }
200
201 if (close_handle) {
202
203 #ifdef PHP_WIN32
204 if (sock->socket == -1)
205 sock->socket = SOCK_ERR;
206 #endif
207 if (sock->socket != SOCK_ERR) {
208 #ifdef PHP_WIN32
209 /* prevent more data from coming in */
210 shutdown(sock->socket, SHUT_RD);
211
212 /* try to make sure that the OS sends all data before we close the connection.
213 * Essentially, we are waiting for the socket to become writeable, which means
214 * that all pending data has been sent.
215 * We use a small timeout which should encourage the OS to send the data,
216 * but at the same time avoid hanging indefinitely.
217 * */
218 do {
219 n = php_pollfd_for_ms(sock->socket, POLLOUT, 500);
220 } while (n == -1 && php_socket_errno() == EINTR);
221 #endif
222 closesocket(sock->socket);
223 sock->socket = SOCK_ERR;
224 }
225
226 }
227
228 pefree(sock, php_stream_is_persistent(stream));
229
230 return 0;
231 }
232
php_sockop_flush(php_stream * stream)233 static int php_sockop_flush(php_stream *stream)
234 {
235 #if 0
236 php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
237 return fsync(sock->socket);
238 #endif
239 return 0;
240 }
241
php_sockop_stat(php_stream * stream,php_stream_statbuf * ssb)242 static int php_sockop_stat(php_stream *stream, php_stream_statbuf *ssb)
243 {
244 #ifdef ZEND_WIN32
245 return 0;
246 #else
247 php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
248
249 return zend_fstat(sock->socket, &ssb->sb);
250 #endif
251 }
252
sock_sendto(php_netstream_data_t * sock,const char * buf,size_t buflen,int flags,struct sockaddr * addr,socklen_t addrlen)253 static inline int sock_sendto(php_netstream_data_t *sock, const char *buf, size_t buflen, int flags,
254 struct sockaddr *addr, socklen_t addrlen
255 )
256 {
257 int ret;
258 if (addr) {
259 ret = sendto(sock->socket, buf, XP_SOCK_BUF_SIZE(buflen), flags, addr, XP_SOCK_BUF_SIZE(addrlen));
260
261 return (ret == SOCK_CONN_ERR) ? -1 : ret;
262 }
263 #ifdef PHP_WIN32
264 return ((ret = send(sock->socket, buf, buflen > INT_MAX ? INT_MAX : (int)buflen, flags)) == SOCK_CONN_ERR) ? -1 : ret;
265 #else
266 return ((ret = send(sock->socket, buf, buflen, flags)) == SOCK_CONN_ERR) ? -1 : ret;
267 #endif
268 }
269
sock_recvfrom(php_netstream_data_t * sock,char * buf,size_t buflen,int flags,zend_string ** textaddr,struct sockaddr ** addr,socklen_t * addrlen)270 static inline int sock_recvfrom(php_netstream_data_t *sock, char *buf, size_t buflen, int flags,
271 zend_string **textaddr,
272 struct sockaddr **addr, socklen_t *addrlen
273 )
274 {
275 int ret;
276 int want_addr = textaddr || addr;
277
278 if (want_addr) {
279 php_sockaddr_storage sa;
280 socklen_t sl = sizeof(sa);
281 ret = recvfrom(sock->socket, buf, XP_SOCK_BUF_SIZE(buflen), flags, (struct sockaddr*)&sa, &sl);
282 ret = (ret == SOCK_CONN_ERR) ? -1 : ret;
283 #ifdef PHP_WIN32
284 /* POSIX discards excess bytes without signalling failure; emulate this on Windows */
285 if (ret == -1 && WSAGetLastError() == WSAEMSGSIZE) {
286 ret = buflen;
287 }
288 #endif
289 if (sl) {
290 php_network_populate_name_from_sockaddr((struct sockaddr*)&sa, sl,
291 textaddr, addr, addrlen);
292 } else {
293 if (textaddr) {
294 *textaddr = ZSTR_EMPTY_ALLOC();
295 }
296 if (addr) {
297 *addr = NULL;
298 *addrlen = 0;
299 }
300 }
301 } else {
302 ret = recv(sock->socket, buf, XP_SOCK_BUF_SIZE(buflen), flags);
303 ret = (ret == SOCK_CONN_ERR) ? -1 : ret;
304 }
305
306 return ret;
307 }
308
php_sockop_set_option(php_stream * stream,int option,int value,void * ptrparam)309 static int php_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam)
310 {
311 int oldmode, flags;
312 php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
313 php_stream_xport_param *xparam;
314
315 if (!sock) {
316 return PHP_STREAM_OPTION_RETURN_NOTIMPL;
317 }
318
319 switch(option) {
320 case PHP_STREAM_OPTION_CHECK_LIVENESS:
321 {
322 struct timeval tv;
323 char buf;
324 int alive = 1;
325
326 if (value == -1) {
327 if (sock->timeout.tv_sec == -1) {
328 tv.tv_sec = FG(default_socket_timeout);
329 tv.tv_usec = 0;
330 } else {
331 tv = sock->timeout;
332 }
333 } else {
334 tv.tv_sec = value;
335 tv.tv_usec = 0;
336 }
337
338 if (sock->socket == -1) {
339 alive = 0;
340 } else if (php_pollfd_for(sock->socket, PHP_POLLREADABLE|POLLPRI, &tv) > 0) {
341 #ifdef PHP_WIN32
342 int ret;
343 #else
344 ssize_t ret;
345 #endif
346 int err;
347
348 ret = recv(sock->socket, &buf, sizeof(buf), MSG_PEEK);
349 err = php_socket_errno();
350 if (0 == ret || /* the counterpart did properly shutdown*/
351 (0 > ret && err != EWOULDBLOCK && err != EAGAIN && err != EMSGSIZE)) { /* there was an unrecoverable error */
352 alive = 0;
353 }
354 }
355 return alive ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR;
356 }
357
358 case PHP_STREAM_OPTION_BLOCKING:
359 oldmode = sock->is_blocked;
360 if (SUCCESS == php_set_sock_blocking(sock->socket, value)) {
361 sock->is_blocked = value;
362 return oldmode;
363 }
364 return PHP_STREAM_OPTION_RETURN_ERR;
365
366 case PHP_STREAM_OPTION_READ_TIMEOUT:
367 sock->timeout = *(struct timeval*)ptrparam;
368 sock->timeout_event = 0;
369 return PHP_STREAM_OPTION_RETURN_OK;
370
371 case PHP_STREAM_OPTION_META_DATA_API:
372 add_assoc_bool((zval *)ptrparam, "timed_out", sock->timeout_event);
373 add_assoc_bool((zval *)ptrparam, "blocked", sock->is_blocked);
374 add_assoc_bool((zval *)ptrparam, "eof", stream->eof);
375 return PHP_STREAM_OPTION_RETURN_OK;
376
377 case PHP_STREAM_OPTION_XPORT_API:
378 xparam = (php_stream_xport_param *)ptrparam;
379
380 switch (xparam->op) {
381 case STREAM_XPORT_OP_LISTEN:
382 xparam->outputs.returncode = (listen(sock->socket, xparam->inputs.backlog) == 0) ? 0: -1;
383 return PHP_STREAM_OPTION_RETURN_OK;
384
385 case STREAM_XPORT_OP_GET_NAME:
386 xparam->outputs.returncode = php_network_get_sock_name(sock->socket,
387 xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
388 xparam->want_addr ? &xparam->outputs.addr : NULL,
389 xparam->want_addr ? &xparam->outputs.addrlen : NULL
390 );
391 return PHP_STREAM_OPTION_RETURN_OK;
392
393 case STREAM_XPORT_OP_GET_PEER_NAME:
394 xparam->outputs.returncode = php_network_get_peer_name(sock->socket,
395 xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
396 xparam->want_addr ? &xparam->outputs.addr : NULL,
397 xparam->want_addr ? &xparam->outputs.addrlen : NULL
398 );
399 return PHP_STREAM_OPTION_RETURN_OK;
400
401 case STREAM_XPORT_OP_SEND:
402 flags = 0;
403 if ((xparam->inputs.flags & STREAM_OOB) == STREAM_OOB) {
404 flags |= MSG_OOB;
405 }
406 xparam->outputs.returncode = sock_sendto(sock,
407 xparam->inputs.buf, xparam->inputs.buflen,
408 flags,
409 xparam->inputs.addr,
410 xparam->inputs.addrlen);
411 if (xparam->outputs.returncode == -1) {
412 char *err = php_socket_strerror(php_socket_errno(), NULL, 0);
413 php_error_docref(NULL, E_WARNING,
414 "%s\n", err);
415 efree(err);
416 }
417 return PHP_STREAM_OPTION_RETURN_OK;
418
419 case STREAM_XPORT_OP_RECV:
420 flags = 0;
421 if ((xparam->inputs.flags & STREAM_OOB) == STREAM_OOB) {
422 flags |= MSG_OOB;
423 }
424 if ((xparam->inputs.flags & STREAM_PEEK) == STREAM_PEEK) {
425 flags |= MSG_PEEK;
426 }
427 xparam->outputs.returncode = sock_recvfrom(sock,
428 xparam->inputs.buf, xparam->inputs.buflen,
429 flags,
430 xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
431 xparam->want_addr ? &xparam->outputs.addr : NULL,
432 xparam->want_addr ? &xparam->outputs.addrlen : NULL
433 );
434 return PHP_STREAM_OPTION_RETURN_OK;
435
436
437 #ifdef HAVE_SHUTDOWN
438 # ifndef SHUT_RD
439 # define SHUT_RD 0
440 # endif
441 # ifndef SHUT_WR
442 # define SHUT_WR 1
443 # endif
444 # ifndef SHUT_RDWR
445 # define SHUT_RDWR 2
446 # endif
447 case STREAM_XPORT_OP_SHUTDOWN: {
448 static const int shutdown_how[] = {SHUT_RD, SHUT_WR, SHUT_RDWR};
449
450 xparam->outputs.returncode = shutdown(sock->socket, shutdown_how[xparam->how]);
451 return PHP_STREAM_OPTION_RETURN_OK;
452 }
453 #endif
454
455 default:
456 break;
457 }
458 }
459
460 return PHP_STREAM_OPTION_RETURN_NOTIMPL;
461 }
462
php_sockop_cast(php_stream * stream,int castas,void ** ret)463 static int php_sockop_cast(php_stream *stream, int castas, void **ret)
464 {
465 php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
466
467 if (!sock) {
468 return FAILURE;
469 }
470
471 switch(castas) {
472 case PHP_STREAM_AS_STDIO:
473 if (ret) {
474 *(FILE**)ret = fdopen(sock->socket, stream->mode);
475 if (*ret)
476 return SUCCESS;
477 return FAILURE;
478 }
479 return SUCCESS;
480 case PHP_STREAM_AS_FD_FOR_SELECT:
481 case PHP_STREAM_AS_FD:
482 case PHP_STREAM_AS_SOCKETD:
483 if (ret)
484 *(php_socket_t *)ret = sock->socket;
485 return SUCCESS;
486 default:
487 return FAILURE;
488 }
489 }
490 /* }}} */
491
492 /* These may look identical, but we need them this way so that
493 * we can determine which type of socket we are dealing with
494 * by inspecting stream->ops.
495 * A "useful" side-effect is that the user's scripts can then
496 * make similar decisions using stream_get_meta_data.
497 * */
498 const php_stream_ops php_stream_generic_socket_ops = {
499 php_sockop_write, php_sockop_read,
500 php_sockop_close, php_sockop_flush,
501 "generic_socket",
502 NULL, /* seek */
503 php_sockop_cast,
504 php_sockop_stat,
505 php_sockop_set_option,
506 };
507
508
509 const php_stream_ops php_stream_socket_ops = {
510 php_sockop_write, php_sockop_read,
511 php_sockop_close, php_sockop_flush,
512 "tcp_socket",
513 NULL, /* seek */
514 php_sockop_cast,
515 php_sockop_stat,
516 php_tcp_sockop_set_option,
517 };
518
519 const php_stream_ops php_stream_udp_socket_ops = {
520 php_sockop_write, php_sockop_read,
521 php_sockop_close, php_sockop_flush,
522 "udp_socket",
523 NULL, /* seek */
524 php_sockop_cast,
525 php_sockop_stat,
526 php_tcp_sockop_set_option,
527 };
528
529 #ifdef AF_UNIX
530 const php_stream_ops php_stream_unix_socket_ops = {
531 php_sockop_write, php_sockop_read,
532 php_sockop_close, php_sockop_flush,
533 "unix_socket",
534 NULL, /* seek */
535 php_sockop_cast,
536 php_sockop_stat,
537 php_tcp_sockop_set_option,
538 };
539 const php_stream_ops php_stream_unixdg_socket_ops = {
540 php_sockop_write, php_sockop_read,
541 php_sockop_close, php_sockop_flush,
542 "udg_socket",
543 NULL, /* seek */
544 php_sockop_cast,
545 php_sockop_stat,
546 php_tcp_sockop_set_option,
547 };
548 #endif
549
550
551 /* network socket operations */
552
553 #ifdef AF_UNIX
parse_unix_address(php_stream_xport_param * xparam,struct sockaddr_un * unix_addr)554 static inline int parse_unix_address(php_stream_xport_param *xparam, struct sockaddr_un *unix_addr)
555 {
556 memset(unix_addr, 0, sizeof(*unix_addr));
557 unix_addr->sun_family = AF_UNIX;
558
559 /* we need to be binary safe on systems that support an abstract
560 * namespace */
561 if (xparam->inputs.namelen >= sizeof(unix_addr->sun_path)) {
562 /* On linux, when the path begins with a NUL byte we are
563 * referring to an abstract namespace. In theory we should
564 * allow an extra byte below, since we don't need the NULL.
565 * BUT, to get into this branch of code, the name is too long,
566 * so we don't care. */
567 xparam->inputs.namelen = sizeof(unix_addr->sun_path) - 1;
568 php_error_docref(NULL, E_NOTICE,
569 "socket path exceeded the maximum allowed length of %lu bytes "
570 "and was truncated", (unsigned long)sizeof(unix_addr->sun_path));
571 }
572
573 memcpy(unix_addr->sun_path, xparam->inputs.name, xparam->inputs.namelen);
574
575 return 1;
576 }
577 #endif
578
parse_ip_address_ex(const char * str,size_t str_len,int * portno,int get_err,zend_string ** err)579 static inline char *parse_ip_address_ex(const char *str, size_t str_len, int *portno, int get_err, zend_string **err)
580 {
581 char *colon;
582 char *host = NULL;
583
584 #ifdef HAVE_IPV6
585 char *p;
586
587 if (*(str) == '[' && str_len > 1) {
588 /* IPV6 notation to specify raw address with port (i.e. [fe80::1]:80) */
589 p = memchr(str + 1, ']', str_len - 2);
590 if (!p || *(p + 1) != ':') {
591 if (get_err) {
592 *err = strpprintf(0, "Failed to parse IPv6 address \"%s\"", str);
593 }
594 return NULL;
595 }
596 *portno = atoi(p + 2);
597 return estrndup(str + 1, p - str - 1);
598 }
599 #endif
600 if (str_len) {
601 colon = memchr(str, ':', str_len - 1);
602 } else {
603 colon = NULL;
604 }
605 if (colon) {
606 *portno = atoi(colon + 1);
607 host = estrndup(str, colon - str);
608 } else {
609 if (get_err) {
610 *err = strpprintf(0, "Failed to parse address \"%s\"", str);
611 }
612 return NULL;
613 }
614
615 return host;
616 }
617
parse_ip_address(php_stream_xport_param * xparam,int * portno)618 static inline char *parse_ip_address(php_stream_xport_param *xparam, int *portno)
619 {
620 return parse_ip_address_ex(xparam->inputs.name, xparam->inputs.namelen, portno, xparam->want_errortext, &xparam->outputs.error_text);
621 }
622
php_tcp_sockop_bind(php_stream * stream,php_netstream_data_t * sock,php_stream_xport_param * xparam)623 static inline int php_tcp_sockop_bind(php_stream *stream, php_netstream_data_t *sock,
624 php_stream_xport_param *xparam)
625 {
626 char *host = NULL;
627 int portno, err;
628 long sockopts = STREAM_SOCKOP_NONE;
629 zval *tmpzval = NULL;
630
631 #ifdef AF_UNIX
632 if (stream->ops == &php_stream_unix_socket_ops || stream->ops == &php_stream_unixdg_socket_ops) {
633 struct sockaddr_un unix_addr;
634
635 sock->socket = socket(PF_UNIX, stream->ops == &php_stream_unix_socket_ops ? SOCK_STREAM : SOCK_DGRAM, 0);
636
637 if (sock->socket == SOCK_ERR) {
638 if (xparam->want_errortext) {
639 xparam->outputs.error_text = strpprintf(0, "Failed to create unix%s socket %s",
640 stream->ops == &php_stream_unix_socket_ops ? "" : "datagram",
641 strerror(errno));
642 }
643 return -1;
644 }
645
646 parse_unix_address(xparam, &unix_addr);
647
648 return bind(sock->socket, (const struct sockaddr *)&unix_addr,
649 (socklen_t) XtOffsetOf(struct sockaddr_un, sun_path) + xparam->inputs.namelen);
650 }
651 #endif
652
653 host = parse_ip_address(xparam, &portno);
654
655 if (host == NULL) {
656 return -1;
657 }
658
659 #ifdef IPV6_V6ONLY
660 if (PHP_STREAM_CONTEXT(stream)
661 && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "ipv6_v6only")) != NULL
662 && Z_TYPE_P(tmpzval) != IS_NULL
663 ) {
664 sockopts |= STREAM_SOCKOP_IPV6_V6ONLY;
665 sockopts |= STREAM_SOCKOP_IPV6_V6ONLY_ENABLED * zend_is_true(tmpzval);
666 }
667 #endif
668
669 #ifdef SO_REUSEPORT
670 if (PHP_STREAM_CONTEXT(stream)
671 && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "so_reuseport")) != NULL
672 && zend_is_true(tmpzval)
673 ) {
674 sockopts |= STREAM_SOCKOP_SO_REUSEPORT;
675 }
676 #endif
677
678 #ifdef SO_BROADCAST
679 if (stream->ops == &php_stream_udp_socket_ops /* SO_BROADCAST is only applicable for UDP */
680 && PHP_STREAM_CONTEXT(stream)
681 && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "so_broadcast")) != NULL
682 && zend_is_true(tmpzval)
683 ) {
684 sockopts |= STREAM_SOCKOP_SO_BROADCAST;
685 }
686 #endif
687
688 sock->socket = php_network_bind_socket_to_local_addr(host, portno,
689 stream->ops == &php_stream_udp_socket_ops ? SOCK_DGRAM : SOCK_STREAM,
690 sockopts,
691 xparam->want_errortext ? &xparam->outputs.error_text : NULL,
692 &err
693 );
694
695 if (host) {
696 efree(host);
697 }
698
699 return sock->socket == -1 ? -1 : 0;
700 }
701
php_tcp_sockop_connect(php_stream * stream,php_netstream_data_t * sock,php_stream_xport_param * xparam)702 static inline int php_tcp_sockop_connect(php_stream *stream, php_netstream_data_t *sock,
703 php_stream_xport_param *xparam)
704 {
705 char *host = NULL, *bindto = NULL;
706 int portno, bindport = 0;
707 int err = 0;
708 int ret;
709 zval *tmpzval = NULL;
710 long sockopts = STREAM_SOCKOP_NONE;
711
712 #ifdef AF_UNIX
713 if (stream->ops == &php_stream_unix_socket_ops || stream->ops == &php_stream_unixdg_socket_ops) {
714 struct sockaddr_un unix_addr;
715
716 sock->socket = socket(PF_UNIX, stream->ops == &php_stream_unix_socket_ops ? SOCK_STREAM : SOCK_DGRAM, 0);
717
718 if (sock->socket == SOCK_ERR) {
719 if (xparam->want_errortext) {
720 xparam->outputs.error_text = strpprintf(0, "Failed to create unix socket");
721 }
722 return -1;
723 }
724
725 parse_unix_address(xparam, &unix_addr);
726
727 ret = php_network_connect_socket(sock->socket,
728 (const struct sockaddr *)&unix_addr, (socklen_t) XtOffsetOf(struct sockaddr_un, sun_path) + xparam->inputs.namelen,
729 xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC, xparam->inputs.timeout,
730 xparam->want_errortext ? &xparam->outputs.error_text : NULL,
731 &err);
732
733 xparam->outputs.error_code = err;
734
735 goto out;
736 }
737 #endif
738
739 host = parse_ip_address(xparam, &portno);
740
741 if (host == NULL) {
742 return -1;
743 }
744
745 if (PHP_STREAM_CONTEXT(stream) && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "bindto")) != NULL) {
746 if (Z_TYPE_P(tmpzval) != IS_STRING) {
747 if (xparam->want_errortext) {
748 xparam->outputs.error_text = strpprintf(0, "local_addr context option is not a string.");
749 }
750 efree(host);
751 return -1;
752 }
753 bindto = parse_ip_address_ex(Z_STRVAL_P(tmpzval), Z_STRLEN_P(tmpzval), &bindport, xparam->want_errortext, &xparam->outputs.error_text);
754 }
755
756 #ifdef SO_BROADCAST
757 if (stream->ops == &php_stream_udp_socket_ops /* SO_BROADCAST is only applicable for UDP */
758 && PHP_STREAM_CONTEXT(stream)
759 && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "so_broadcast")) != NULL
760 && zend_is_true(tmpzval)
761 ) {
762 sockopts |= STREAM_SOCKOP_SO_BROADCAST;
763 }
764 #endif
765
766 if (stream->ops != &php_stream_udp_socket_ops /* TCP_NODELAY is only applicable for TCP */
767 #ifdef AF_UNIX
768 && stream->ops != &php_stream_unix_socket_ops
769 && stream->ops != &php_stream_unixdg_socket_ops
770 #endif
771 && PHP_STREAM_CONTEXT(stream)
772 && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "tcp_nodelay")) != NULL
773 && zend_is_true(tmpzval)
774 ) {
775 sockopts |= STREAM_SOCKOP_TCP_NODELAY;
776 }
777
778 /* Note: the test here for php_stream_udp_socket_ops is important, because we
779 * want the default to be TCP sockets so that the openssl extension can
780 * re-use this code. */
781
782 sock->socket = php_network_connect_socket_to_host(host, portno,
783 stream->ops == &php_stream_udp_socket_ops ? SOCK_DGRAM : SOCK_STREAM,
784 xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC,
785 xparam->inputs.timeout,
786 xparam->want_errortext ? &xparam->outputs.error_text : NULL,
787 &err,
788 bindto,
789 bindport,
790 sockopts
791 );
792
793 ret = sock->socket == -1 ? -1 : 0;
794 xparam->outputs.error_code = err;
795
796 if (host) {
797 efree(host);
798 }
799 if (bindto) {
800 efree(bindto);
801 }
802
803 #ifdef AF_UNIX
804 out:
805 #endif
806
807 if (ret >= 0 && xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC && err == EINPROGRESS) {
808 /* indicates pending connection */
809 return 1;
810 }
811
812 return ret;
813 }
814
php_tcp_sockop_accept(php_stream * stream,php_netstream_data_t * sock,php_stream_xport_param * xparam STREAMS_DC)815 static inline int php_tcp_sockop_accept(php_stream *stream, php_netstream_data_t *sock,
816 php_stream_xport_param *xparam STREAMS_DC)
817 {
818 int clisock;
819 bool nodelay = 0;
820 zval *tmpzval = NULL;
821
822 xparam->outputs.client = NULL;
823
824 if ((NULL != PHP_STREAM_CONTEXT(stream)) &&
825 (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "tcp_nodelay")) != NULL &&
826 zend_is_true(tmpzval)) {
827 nodelay = 1;
828 }
829
830 clisock = php_network_accept_incoming(sock->socket,
831 xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
832 xparam->want_addr ? &xparam->outputs.addr : NULL,
833 xparam->want_addr ? &xparam->outputs.addrlen : NULL,
834 xparam->inputs.timeout,
835 xparam->want_errortext ? &xparam->outputs.error_text : NULL,
836 &xparam->outputs.error_code,
837 nodelay);
838
839 if (clisock >= 0) {
840 php_netstream_data_t *clisockdata = (php_netstream_data_t*) emalloc(sizeof(*clisockdata));
841
842 memcpy(clisockdata, sock, sizeof(*clisockdata));
843 clisockdata->socket = clisock;
844 #ifdef __linux__
845 /* O_NONBLOCK is not inherited on Linux */
846 clisockdata->is_blocked = 1;
847 #endif
848
849 xparam->outputs.client = php_stream_alloc_rel(stream->ops, clisockdata, NULL, "r+");
850 if (xparam->outputs.client) {
851 xparam->outputs.client->ctx = stream->ctx;
852 if (stream->ctx) {
853 GC_ADDREF(stream->ctx);
854 }
855 }
856 }
857
858 return xparam->outputs.client == NULL ? -1 : 0;
859 }
860
php_tcp_sockop_set_option(php_stream * stream,int option,int value,void * ptrparam)861 static int php_tcp_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam)
862 {
863 php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
864 php_stream_xport_param *xparam;
865
866 switch(option) {
867 case PHP_STREAM_OPTION_XPORT_API:
868 xparam = (php_stream_xport_param *)ptrparam;
869
870 switch(xparam->op) {
871 case STREAM_XPORT_OP_CONNECT:
872 case STREAM_XPORT_OP_CONNECT_ASYNC:
873 xparam->outputs.returncode = php_tcp_sockop_connect(stream, sock, xparam);
874 return PHP_STREAM_OPTION_RETURN_OK;
875
876 case STREAM_XPORT_OP_BIND:
877 xparam->outputs.returncode = php_tcp_sockop_bind(stream, sock, xparam);
878 return PHP_STREAM_OPTION_RETURN_OK;
879
880
881 case STREAM_XPORT_OP_ACCEPT:
882 xparam->outputs.returncode = php_tcp_sockop_accept(stream, sock, xparam STREAMS_CC);
883 return PHP_STREAM_OPTION_RETURN_OK;
884 default:
885 /* fall through */
886 ;
887 }
888 }
889 return php_sockop_set_option(stream, option, value, ptrparam);
890 }
891
892
php_stream_generic_socket_factory(const char * proto,size_t protolen,const char * resourcename,size_t resourcenamelen,const char * persistent_id,int options,int flags,struct timeval * timeout,php_stream_context * context STREAMS_DC)893 PHPAPI php_stream *php_stream_generic_socket_factory(const char *proto, size_t protolen,
894 const char *resourcename, size_t resourcenamelen,
895 const char *persistent_id, int options, int flags,
896 struct timeval *timeout,
897 php_stream_context *context STREAMS_DC)
898 {
899 php_stream *stream = NULL;
900 php_netstream_data_t *sock;
901 const php_stream_ops *ops;
902
903 /* which type of socket ? */
904 if (strncmp(proto, "tcp", protolen) == 0) {
905 ops = &php_stream_socket_ops;
906 } else if (strncmp(proto, "udp", protolen) == 0) {
907 ops = &php_stream_udp_socket_ops;
908 }
909 #ifdef AF_UNIX
910 else if (strncmp(proto, "unix", protolen) == 0) {
911 ops = &php_stream_unix_socket_ops;
912 } else if (strncmp(proto, "udg", protolen) == 0) {
913 ops = &php_stream_unixdg_socket_ops;
914 }
915 #endif
916 else {
917 /* should never happen */
918 return NULL;
919 }
920
921 sock = pemalloc(sizeof(php_netstream_data_t), persistent_id ? 1 : 0);
922 memset(sock, 0, sizeof(php_netstream_data_t));
923
924 sock->is_blocked = 1;
925 sock->timeout.tv_sec = FG(default_socket_timeout);
926 sock->timeout.tv_usec = 0;
927
928 /* we don't know the socket until we have determined if we are binding or
929 * connecting */
930 sock->socket = -1;
931
932 stream = php_stream_alloc_rel(ops, sock, persistent_id, "r+");
933
934 if (stream == NULL) {
935 pefree(sock, persistent_id ? 1 : 0);
936 return NULL;
937 }
938
939 if (flags == 0) {
940 return stream;
941 }
942
943 return stream;
944 }
945