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