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