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