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