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