xref: /PHP-8.3/main/streams/xp_socket.c (revision 31798e4b)
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