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