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