xref: /PHP-5.5/main/streams/xp_socket.c (revision 73c1be26)
1 /*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2015 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,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, 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 	php_sockaddr_storage sa;
251 	socklen_t sl = sizeof(sa);
252 	int ret;
253 	int want_addr = textaddr || addr;
254 
255 	if (want_addr) {
256 		ret = recvfrom(sock->socket, buf, buflen, flags, (struct sockaddr*)&sa, &sl);
257 		ret = (ret == SOCK_CONN_ERR) ? -1 : ret;
258 		php_network_populate_name_from_sockaddr((struct sockaddr*)&sa, sl,
259 			textaddr, textaddrlen, addr, addrlen TSRMLS_CC);
260 	} else {
261 		ret = recv(sock->socket, buf, buflen, flags);
262 		ret = (ret == SOCK_CONN_ERR) ? -1 : ret;
263 	}
264 
265 	return ret;
266 }
267 
php_sockop_set_option(php_stream * stream,int option,int value,void * ptrparam TSRMLS_DC)268 static int php_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC)
269 {
270 	int oldmode, flags;
271 	php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
272 	php_stream_xport_param *xparam;
273 
274 	switch(option) {
275 		case PHP_STREAM_OPTION_CHECK_LIVENESS:
276 			{
277 				struct timeval tv;
278 				char buf;
279 				int alive = 1;
280 
281 				if (value == -1) {
282 					if (sock->timeout.tv_sec == -1) {
283 						tv.tv_sec = FG(default_socket_timeout);
284 						tv.tv_usec = 0;
285 					} else {
286 						tv = sock->timeout;
287 					}
288 				} else {
289 					tv.tv_sec = value;
290 					tv.tv_usec = 0;
291 				}
292 
293 				if (sock->socket == -1) {
294 					alive = 0;
295 				} else if (php_pollfd_for(sock->socket, PHP_POLLREADABLE|POLLPRI, &tv) > 0) {
296 					if (0 >= recv(sock->socket, &buf, sizeof(buf), MSG_PEEK) && php_socket_errno() != EWOULDBLOCK) {
297 						alive = 0;
298 					}
299 				}
300 				return alive ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR;
301 			}
302 
303 		case PHP_STREAM_OPTION_BLOCKING:
304 			oldmode = sock->is_blocked;
305 			if (SUCCESS == php_set_sock_blocking(sock->socket, value TSRMLS_CC)) {
306 				sock->is_blocked = value;
307 				return oldmode;
308 			}
309 			return PHP_STREAM_OPTION_RETURN_ERR;
310 
311 		case PHP_STREAM_OPTION_READ_TIMEOUT:
312 			sock->timeout = *(struct timeval*)ptrparam;
313 			sock->timeout_event = 0;
314 			return PHP_STREAM_OPTION_RETURN_OK;
315 
316 		case PHP_STREAM_OPTION_META_DATA_API:
317 			add_assoc_bool((zval *)ptrparam, "timed_out", sock->timeout_event);
318 			add_assoc_bool((zval *)ptrparam, "blocked", sock->is_blocked);
319 			add_assoc_bool((zval *)ptrparam, "eof", stream->eof);
320 			return PHP_STREAM_OPTION_RETURN_OK;
321 
322 		case PHP_STREAM_OPTION_XPORT_API:
323 			xparam = (php_stream_xport_param *)ptrparam;
324 
325 			switch (xparam->op) {
326 				case STREAM_XPORT_OP_LISTEN:
327 					xparam->outputs.returncode = (listen(sock->socket, xparam->inputs.backlog) == 0) ?  0: -1;
328 					return PHP_STREAM_OPTION_RETURN_OK;
329 
330 				case STREAM_XPORT_OP_GET_NAME:
331 					xparam->outputs.returncode = php_network_get_sock_name(sock->socket,
332 							xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
333 							xparam->want_textaddr ? &xparam->outputs.textaddrlen : NULL,
334 							xparam->want_addr ? &xparam->outputs.addr : NULL,
335 							xparam->want_addr ? &xparam->outputs.addrlen : NULL
336 							TSRMLS_CC);
337 					return PHP_STREAM_OPTION_RETURN_OK;
338 
339 				case STREAM_XPORT_OP_GET_PEER_NAME:
340 					xparam->outputs.returncode = php_network_get_peer_name(sock->socket,
341 							xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
342 							xparam->want_textaddr ? &xparam->outputs.textaddrlen : NULL,
343 							xparam->want_addr ? &xparam->outputs.addr : NULL,
344 							xparam->want_addr ? &xparam->outputs.addrlen : NULL
345 							TSRMLS_CC);
346 					return PHP_STREAM_OPTION_RETURN_OK;
347 
348 				case STREAM_XPORT_OP_SEND:
349 					flags = 0;
350 					if ((xparam->inputs.flags & STREAM_OOB) == STREAM_OOB) {
351 						flags |= MSG_OOB;
352 					}
353 					xparam->outputs.returncode = sock_sendto(sock,
354 							xparam->inputs.buf, xparam->inputs.buflen,
355 							flags,
356 							xparam->inputs.addr,
357 							xparam->inputs.addrlen TSRMLS_CC);
358 					if (xparam->outputs.returncode == -1) {
359 						char *err = php_socket_strerror(php_socket_errno(), NULL, 0);
360 						php_error_docref(NULL TSRMLS_CC, E_WARNING,
361 						   	"%s\n", err);
362 						efree(err);
363 					}
364 					return PHP_STREAM_OPTION_RETURN_OK;
365 
366 				case STREAM_XPORT_OP_RECV:
367 					flags = 0;
368 					if ((xparam->inputs.flags & STREAM_OOB) == STREAM_OOB) {
369 						flags |= MSG_OOB;
370 					}
371 					if ((xparam->inputs.flags & STREAM_PEEK) == STREAM_PEEK) {
372 						flags |= MSG_PEEK;
373 					}
374 					xparam->outputs.returncode = sock_recvfrom(sock,
375 							xparam->inputs.buf, xparam->inputs.buflen,
376 							flags,
377 							xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
378 							xparam->want_textaddr ? &xparam->outputs.textaddrlen : NULL,
379 							xparam->want_addr ? &xparam->outputs.addr : NULL,
380 							xparam->want_addr ? &xparam->outputs.addrlen : NULL
381 							TSRMLS_CC);
382 					return PHP_STREAM_OPTION_RETURN_OK;
383 
384 
385 #ifdef HAVE_SHUTDOWN
386 # ifndef SHUT_RD
387 #  define SHUT_RD 0
388 # endif
389 # ifndef SHUT_WR
390 #  define SHUT_WR 1
391 # endif
392 # ifndef SHUT_RDWR
393 #  define SHUT_RDWR 2
394 # endif
395 				case STREAM_XPORT_OP_SHUTDOWN: {
396 					static const int shutdown_how[] = {SHUT_RD, SHUT_WR, SHUT_RDWR};
397 
398 					xparam->outputs.returncode = shutdown(sock->socket, shutdown_how[xparam->how]);
399 					return PHP_STREAM_OPTION_RETURN_OK;
400 				}
401 #endif
402 
403 				default:
404 					return PHP_STREAM_OPTION_RETURN_NOTIMPL;
405 			}
406 
407 		default:
408 			return PHP_STREAM_OPTION_RETURN_NOTIMPL;
409 	}
410 }
411 
php_sockop_cast(php_stream * stream,int castas,void ** ret TSRMLS_DC)412 static int php_sockop_cast(php_stream *stream, int castas, void **ret TSRMLS_DC)
413 {
414 	php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
415 
416 	switch(castas)	{
417 		case PHP_STREAM_AS_STDIO:
418 			if (ret)	{
419 				*(FILE**)ret = fdopen(sock->socket, stream->mode);
420 				if (*ret)
421 					return SUCCESS;
422 				return FAILURE;
423 			}
424 			return SUCCESS;
425 		case PHP_STREAM_AS_FD_FOR_SELECT:
426 		case PHP_STREAM_AS_FD:
427 		case PHP_STREAM_AS_SOCKETD:
428 			if (ret)
429 				*(php_socket_t *)ret = sock->socket;
430 			return SUCCESS;
431 		default:
432 			return FAILURE;
433 	}
434 }
435 /* }}} */
436 
437 /* These may look identical, but we need them this way so that
438  * we can determine which type of socket we are dealing with
439  * by inspecting stream->ops.
440  * A "useful" side-effect is that the user's scripts can then
441  * make similar decisions using stream_get_meta_data.
442  * */
443 php_stream_ops php_stream_generic_socket_ops = {
444 	php_sockop_write, php_sockop_read,
445 	php_sockop_close, php_sockop_flush,
446 	"generic_socket",
447 	NULL, /* seek */
448 	php_sockop_cast,
449 	php_sockop_stat,
450 	php_sockop_set_option,
451 };
452 
453 
454 php_stream_ops php_stream_socket_ops = {
455 	php_sockop_write, php_sockop_read,
456 	php_sockop_close, php_sockop_flush,
457 	"tcp_socket",
458 	NULL, /* seek */
459 	php_sockop_cast,
460 	php_sockop_stat,
461 	php_tcp_sockop_set_option,
462 };
463 
464 php_stream_ops php_stream_udp_socket_ops = {
465 	php_sockop_write, php_sockop_read,
466 	php_sockop_close, php_sockop_flush,
467 	"udp_socket",
468 	NULL, /* seek */
469 	php_sockop_cast,
470 	php_sockop_stat,
471 	php_tcp_sockop_set_option,
472 };
473 
474 #ifdef AF_UNIX
475 php_stream_ops php_stream_unix_socket_ops = {
476 	php_sockop_write, php_sockop_read,
477 	php_sockop_close, php_sockop_flush,
478 	"unix_socket",
479 	NULL, /* seek */
480 	php_sockop_cast,
481 	php_sockop_stat,
482 	php_tcp_sockop_set_option,
483 };
484 php_stream_ops php_stream_unixdg_socket_ops = {
485 	php_sockop_write, php_sockop_read,
486 	php_sockop_close, php_sockop_flush,
487 	"udg_socket",
488 	NULL, /* seek */
489 	php_sockop_cast,
490 	php_sockop_stat,
491 	php_tcp_sockop_set_option,
492 };
493 #endif
494 
495 
496 /* network socket operations */
497 
498 #ifdef AF_UNIX
parse_unix_address(php_stream_xport_param * xparam,struct sockaddr_un * unix_addr TSRMLS_DC)499 static inline int parse_unix_address(php_stream_xport_param *xparam, struct sockaddr_un *unix_addr TSRMLS_DC)
500 {
501 	memset(unix_addr, 0, sizeof(*unix_addr));
502 	unix_addr->sun_family = AF_UNIX;
503 
504 	/* we need to be binary safe on systems that support an abstract
505 	 * namespace */
506 	if (xparam->inputs.namelen >= sizeof(unix_addr->sun_path)) {
507 		/* On linux, when the path begins with a NUL byte we are
508 		 * referring to an abstract namespace.  In theory we should
509 		 * allow an extra byte below, since we don't need the NULL.
510 		 * BUT, to get into this branch of code, the name is too long,
511 		 * so we don't care. */
512 		xparam->inputs.namelen = sizeof(unix_addr->sun_path) - 1;
513 		php_error_docref(NULL TSRMLS_CC, E_NOTICE,
514 			"socket path exceeded the maximum allowed length of %lu bytes "
515 			"and was truncated", (unsigned long)sizeof(unix_addr->sun_path));
516 	}
517 
518 	memcpy(unix_addr->sun_path, xparam->inputs.name, xparam->inputs.namelen);
519 
520 	return 1;
521 }
522 #endif
523 
parse_ip_address_ex(const char * str,int str_len,int * portno,int get_err,char ** err TSRMLS_DC)524 static inline char *parse_ip_address_ex(const char *str, int str_len, int *portno, int get_err, char **err TSRMLS_DC)
525 {
526 	char *colon;
527 	char *host = NULL;
528 
529 #ifdef HAVE_IPV6
530 	char *p;
531 
532 	if (*(str) == '[' && str_len > 1) {
533 		/* IPV6 notation to specify raw address with port (i.e. [fe80::1]:80) */
534 		p = memchr(str + 1, ']', str_len - 2);
535 		if (!p || *(p + 1) != ':') {
536 			if (get_err) {
537 				spprintf(err, 0, "Failed to parse IPv6 address \"%s\"", str);
538 			}
539 			return NULL;
540 		}
541 		*portno = atoi(p + 2);
542 		return estrndup(str + 1, p - str - 1);
543 	}
544 #endif
545 	if (str_len) {
546 		colon = memchr(str, ':', str_len - 1);
547 	} else {
548 		colon = NULL;
549 	}
550 	if (colon) {
551 		*portno = atoi(colon + 1);
552 		host = estrndup(str, colon - str);
553 	} else {
554 		if (get_err) {
555 			spprintf(err, 0, "Failed to parse address \"%s\"", str);
556 		}
557 		return NULL;
558 	}
559 
560 	return host;
561 }
562 
parse_ip_address(php_stream_xport_param * xparam,int * portno TSRMLS_DC)563 static inline char *parse_ip_address(php_stream_xport_param *xparam, int *portno TSRMLS_DC)
564 {
565 	return parse_ip_address_ex(xparam->inputs.name, xparam->inputs.namelen, portno, xparam->want_errortext, &xparam->outputs.error_text TSRMLS_CC);
566 }
567 
php_tcp_sockop_bind(php_stream * stream,php_netstream_data_t * sock,php_stream_xport_param * xparam TSRMLS_DC)568 static inline int php_tcp_sockop_bind(php_stream *stream, php_netstream_data_t *sock,
569 		php_stream_xport_param *xparam TSRMLS_DC)
570 {
571 	char *host = NULL;
572 	int portno, err;
573 
574 #ifdef AF_UNIX
575 	if (stream->ops == &php_stream_unix_socket_ops || stream->ops == &php_stream_unixdg_socket_ops) {
576 		struct sockaddr_un unix_addr;
577 
578 		sock->socket = socket(PF_UNIX, stream->ops == &php_stream_unix_socket_ops ? SOCK_STREAM : SOCK_DGRAM, 0);
579 
580 		if (sock->socket == SOCK_ERR) {
581 			if (xparam->want_errortext) {
582 				spprintf(&xparam->outputs.error_text, 0, "Failed to create unix%s socket %s",
583 						stream->ops == &php_stream_unix_socket_ops ? "" : "datagram",
584 						strerror(errno));
585 			}
586 			return -1;
587 		}
588 
589 		parse_unix_address(xparam, &unix_addr TSRMLS_CC);
590 
591 		return bind(sock->socket, (const struct sockaddr *)&unix_addr,
592 			(socklen_t) XtOffsetOf(struct sockaddr_un, sun_path) + xparam->inputs.namelen);
593 	}
594 #endif
595 
596 	host = parse_ip_address(xparam, &portno TSRMLS_CC);
597 
598 	if (host == NULL) {
599 		return -1;
600 	}
601 
602 	sock->socket = php_network_bind_socket_to_local_addr(host, portno,
603 			stream->ops == &php_stream_udp_socket_ops ? SOCK_DGRAM : SOCK_STREAM,
604 			xparam->want_errortext ? &xparam->outputs.error_text : NULL,
605 			&err
606 			TSRMLS_CC);
607 
608 	if (host) {
609 		efree(host);
610 	}
611 
612 	return sock->socket == -1 ? -1 : 0;
613 }
614 
php_tcp_sockop_connect(php_stream * stream,php_netstream_data_t * sock,php_stream_xport_param * xparam TSRMLS_DC)615 static inline int php_tcp_sockop_connect(php_stream *stream, php_netstream_data_t *sock,
616 		php_stream_xport_param *xparam TSRMLS_DC)
617 {
618 	char *host = NULL, *bindto = NULL;
619 	int portno, bindport = 0;
620 	int err = 0;
621 	int ret;
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 				spprintf(&xparam->outputs.error_text, 0, "Failed to create unix socket");
633 			}
634 			return -1;
635 		}
636 
637 		parse_unix_address(xparam, &unix_addr TSRMLS_CC);
638 
639 		ret = php_network_connect_socket(sock->socket,
640 				(const struct sockaddr *)&unix_addr, (socklen_t) XtOffsetOf(struct sockaddr_un, sun_path) + xparam->inputs.namelen,
641 				xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC, xparam->inputs.timeout,
642 				xparam->want_errortext ? &xparam->outputs.error_text : NULL,
643 				&err);
644 
645 		xparam->outputs.error_code = err;
646 
647 		goto out;
648 	}
649 #endif
650 
651 	host = parse_ip_address(xparam, &portno TSRMLS_CC);
652 
653 	if (host == NULL) {
654 		return -1;
655 	}
656 
657 	if (stream->context && php_stream_context_get_option(stream->context, "socket", "bindto", &tmpzval) == SUCCESS) {
658 		if (Z_TYPE_PP(tmpzval) != IS_STRING) {
659 			if (xparam->want_errortext) {
660 				spprintf(&xparam->outputs.error_text, 0, "local_addr context option is not a string.");
661 			}
662 			efree(host);
663 			return -1;
664 		}
665 		bindto = parse_ip_address_ex(Z_STRVAL_PP(tmpzval), Z_STRLEN_PP(tmpzval), &bindport, xparam->want_errortext, &xparam->outputs.error_text TSRMLS_CC);
666 	}
667 
668 	/* Note: the test here for php_stream_udp_socket_ops is important, because we
669 	 * want the default to be TCP sockets so that the openssl extension can
670 	 * re-use this code. */
671 
672 	sock->socket = php_network_connect_socket_to_host(host, portno,
673 			stream->ops == &php_stream_udp_socket_ops ? SOCK_DGRAM : SOCK_STREAM,
674 			xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC,
675 			xparam->inputs.timeout,
676 			xparam->want_errortext ? &xparam->outputs.error_text : NULL,
677 			&err,
678 			bindto,
679 			bindport
680 			TSRMLS_CC);
681 
682 	ret = sock->socket == -1 ? -1 : 0;
683 	xparam->outputs.error_code = err;
684 
685 	if (host) {
686 		efree(host);
687 	}
688 	if (bindto) {
689 		efree(bindto);
690 	}
691 
692 #ifdef AF_UNIX
693 out:
694 #endif
695 
696 	if (ret >= 0 && xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC && err == EINPROGRESS) {
697 		/* indicates pending connection */
698 		return 1;
699 	}
700 
701 	return ret;
702 }
703 
php_tcp_sockop_accept(php_stream * stream,php_netstream_data_t * sock,php_stream_xport_param * xparam STREAMS_DC TSRMLS_DC)704 static inline int php_tcp_sockop_accept(php_stream *stream, php_netstream_data_t *sock,
705 		php_stream_xport_param *xparam STREAMS_DC TSRMLS_DC)
706 {
707 	int clisock;
708 
709 	xparam->outputs.client = NULL;
710 
711 	clisock = php_network_accept_incoming(sock->socket,
712 			xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
713 			xparam->want_textaddr ? &xparam->outputs.textaddrlen : NULL,
714 			xparam->want_addr ? &xparam->outputs.addr : NULL,
715 			xparam->want_addr ? &xparam->outputs.addrlen : NULL,
716 			xparam->inputs.timeout,
717 			xparam->want_errortext ? &xparam->outputs.error_text : NULL,
718 			&xparam->outputs.error_code
719 			TSRMLS_CC);
720 
721 	if (clisock >= 0) {
722 		php_netstream_data_t *clisockdata;
723 
724 		clisockdata = emalloc(sizeof(*clisockdata));
725 
726 		if (clisockdata == NULL) {
727 			close(clisock);
728 			/* technically a fatal error */
729 		} else {
730 			memcpy(clisockdata, sock, sizeof(*clisockdata));
731 			clisockdata->socket = clisock;
732 
733 			xparam->outputs.client = php_stream_alloc_rel(stream->ops, clisockdata, NULL, "r+");
734 			if (xparam->outputs.client) {
735 				xparam->outputs.client->context = stream->context;
736 				if (stream->context) {
737 					zend_list_addref(stream->context->rsrc_id);
738 				}
739 			}
740 		}
741 	}
742 
743 	return xparam->outputs.client == NULL ? -1 : 0;
744 }
745 
php_tcp_sockop_set_option(php_stream * stream,int option,int value,void * ptrparam TSRMLS_DC)746 static int php_tcp_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC)
747 {
748 	php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
749 	php_stream_xport_param *xparam;
750 
751 	switch(option) {
752 		case PHP_STREAM_OPTION_XPORT_API:
753 			xparam = (php_stream_xport_param *)ptrparam;
754 
755 			switch(xparam->op) {
756 				case STREAM_XPORT_OP_CONNECT:
757 				case STREAM_XPORT_OP_CONNECT_ASYNC:
758 					xparam->outputs.returncode = php_tcp_sockop_connect(stream, sock, xparam TSRMLS_CC);
759 					return PHP_STREAM_OPTION_RETURN_OK;
760 
761 				case STREAM_XPORT_OP_BIND:
762 					xparam->outputs.returncode = php_tcp_sockop_bind(stream, sock, xparam TSRMLS_CC);
763 					return PHP_STREAM_OPTION_RETURN_OK;
764 
765 
766 				case STREAM_XPORT_OP_ACCEPT:
767 					xparam->outputs.returncode = php_tcp_sockop_accept(stream, sock, xparam STREAMS_CC TSRMLS_CC);
768 					return PHP_STREAM_OPTION_RETURN_OK;
769 				default:
770 					/* fall through */
771 					;
772 			}
773 	}
774 	return php_sockop_set_option(stream, option, value, ptrparam TSRMLS_CC);
775 }
776 
777 
php_stream_generic_socket_factory(const char * proto,long protolen,char * resourcename,long resourcenamelen,const char * persistent_id,int options,int flags,struct timeval * timeout,php_stream_context * context STREAMS_DC TSRMLS_DC)778 PHPAPI php_stream *php_stream_generic_socket_factory(const char *proto, long protolen,
779 		char *resourcename, long resourcenamelen,
780 		const char *persistent_id, int options, int flags,
781 		struct timeval *timeout,
782 		php_stream_context *context STREAMS_DC TSRMLS_DC)
783 {
784 	php_stream *stream = NULL;
785 	php_netstream_data_t *sock;
786 	php_stream_ops *ops;
787 
788 	/* which type of socket ? */
789 	if (strncmp(proto, "tcp", protolen) == 0) {
790 		ops = &php_stream_socket_ops;
791 	} else if (strncmp(proto, "udp", protolen) == 0) {
792 		ops = &php_stream_udp_socket_ops;
793 	}
794 #ifdef AF_UNIX
795 	else if (strncmp(proto, "unix", protolen) == 0) {
796 		ops = &php_stream_unix_socket_ops;
797 	} else if (strncmp(proto, "udg", protolen) == 0) {
798 		ops = &php_stream_unixdg_socket_ops;
799 	}
800 #endif
801 	else {
802 		/* should never happen */
803 		return NULL;
804 	}
805 
806 	sock = pemalloc(sizeof(php_netstream_data_t), persistent_id ? 1 : 0);
807 	memset(sock, 0, sizeof(php_netstream_data_t));
808 
809 	sock->is_blocked = 1;
810 	sock->timeout.tv_sec = FG(default_socket_timeout);
811 	sock->timeout.tv_usec = 0;
812 
813 	/* we don't know the socket until we have determined if we are binding or
814 	 * connecting */
815 	sock->socket = -1;
816 
817 	stream = php_stream_alloc_rel(ops, sock, persistent_id, "r+");
818 
819 	if (stream == NULL)	{
820 		pefree(sock, persistent_id ? 1 : 0);
821 		return NULL;
822 	}
823 
824 	if (flags == 0) {
825 		return stream;
826 	}
827 
828 	return stream;
829 }
830 
831 
832 /*
833  * Local variables:
834  * tab-width: 4
835  * c-basic-offset: 4
836  * End:
837  * vim600: noet sw=4 ts=4 fdm=marker
838  * vim<600: noet sw=4 ts=4
839  */
840