xref: /PHP-5.3/main/streams/xp_socket.c (revision a2045ff3)
1 /*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2013 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 indefintely.
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 				case PHP_STREAM_OPTION_WRITE_BUFFER:
404 					php_stream_set_chunk_size(stream, (ptrparam ? *(size_t *)ptrparam : PHP_SOCK_CHUNK_SIZE));
405 					return PHP_STREAM_OPTION_RETURN_OK;
406 
407 				default:
408 					return PHP_STREAM_OPTION_RETURN_NOTIMPL;
409 			}
410 
411 		default:
412 			return PHP_STREAM_OPTION_RETURN_NOTIMPL;
413 	}
414 }
415 
php_sockop_cast(php_stream * stream,int castas,void ** ret TSRMLS_DC)416 static int php_sockop_cast(php_stream *stream, int castas, void **ret TSRMLS_DC)
417 {
418 	php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
419 
420 	switch(castas)	{
421 		case PHP_STREAM_AS_STDIO:
422 			if (ret)	{
423 				*(FILE**)ret = fdopen(sock->socket, stream->mode);
424 				if (*ret)
425 					return SUCCESS;
426 				return FAILURE;
427 			}
428 			return SUCCESS;
429 		case PHP_STREAM_AS_FD_FOR_SELECT:
430 		case PHP_STREAM_AS_FD:
431 		case PHP_STREAM_AS_SOCKETD:
432 			if (ret)
433 				*(int*)ret = sock->socket;
434 			return SUCCESS;
435 		default:
436 			return FAILURE;
437 	}
438 }
439 /* }}} */
440 
441 /* These may look identical, but we need them this way so that
442  * we can determine which type of socket we are dealing with
443  * by inspecting stream->ops.
444  * A "useful" side-effect is that the user's scripts can then
445  * make similar decisions using stream_get_meta_data.
446  * */
447 php_stream_ops php_stream_generic_socket_ops = {
448 	php_sockop_write, php_sockop_read,
449 	php_sockop_close, php_sockop_flush,
450 	"generic_socket",
451 	NULL, /* seek */
452 	php_sockop_cast,
453 	php_sockop_stat,
454 	php_sockop_set_option,
455 };
456 
457 
458 php_stream_ops php_stream_socket_ops = {
459 	php_sockop_write, php_sockop_read,
460 	php_sockop_close, php_sockop_flush,
461 	"tcp_socket",
462 	NULL, /* seek */
463 	php_sockop_cast,
464 	php_sockop_stat,
465 	php_tcp_sockop_set_option,
466 };
467 
468 php_stream_ops php_stream_udp_socket_ops = {
469 	php_sockop_write, php_sockop_read,
470 	php_sockop_close, php_sockop_flush,
471 	"udp_socket",
472 	NULL, /* seek */
473 	php_sockop_cast,
474 	php_sockop_stat,
475 	php_tcp_sockop_set_option,
476 };
477 
478 #ifdef AF_UNIX
479 php_stream_ops php_stream_unix_socket_ops = {
480 	php_sockop_write, php_sockop_read,
481 	php_sockop_close, php_sockop_flush,
482 	"unix_socket",
483 	NULL, /* seek */
484 	php_sockop_cast,
485 	php_sockop_stat,
486 	php_tcp_sockop_set_option,
487 };
488 php_stream_ops php_stream_unixdg_socket_ops = {
489 	php_sockop_write, php_sockop_read,
490 	php_sockop_close, php_sockop_flush,
491 	"udg_socket",
492 	NULL, /* seek */
493 	php_sockop_cast,
494 	php_sockop_stat,
495 	php_tcp_sockop_set_option,
496 };
497 #endif
498 
499 
500 /* network socket operations */
501 
502 #ifdef AF_UNIX
parse_unix_address(php_stream_xport_param * xparam,struct sockaddr_un * unix_addr TSRMLS_DC)503 static inline int parse_unix_address(php_stream_xport_param *xparam, struct sockaddr_un *unix_addr TSRMLS_DC)
504 {
505 	memset(unix_addr, 0, sizeof(*unix_addr));
506 	unix_addr->sun_family = AF_UNIX;
507 
508 	/* we need to be binary safe on systems that support an abstract
509 	 * namespace */
510 	if (xparam->inputs.namelen >= sizeof(unix_addr->sun_path)) {
511 		/* On linux, when the path begins with a NUL byte we are
512 		 * referring to an abstract namespace.  In theory we should
513 		 * allow an extra byte below, since we don't need the NULL.
514 		 * BUT, to get into this branch of code, the name is too long,
515 		 * so we don't care. */
516 		xparam->inputs.namelen = sizeof(unix_addr->sun_path) - 1;
517 		php_error_docref(NULL TSRMLS_CC, E_NOTICE,
518 			"socket path exceeded the maximum allowed length of %lu bytes "
519 			"and was truncated", (unsigned long)sizeof(unix_addr->sun_path));
520 	}
521 
522 	memcpy(unix_addr->sun_path, xparam->inputs.name, xparam->inputs.namelen);
523 
524 	return 1;
525 }
526 #endif
527 
parse_ip_address_ex(const char * str,int str_len,int * portno,int get_err,char ** err TSRMLS_DC)528 static inline char *parse_ip_address_ex(const char *str, int str_len, int *portno, int get_err, char **err TSRMLS_DC)
529 {
530 	char *colon;
531 	char *host = NULL;
532 
533 #ifdef HAVE_IPV6
534 	char *p;
535 
536 	if (*(str) == '[' && str_len > 1) {
537 		/* IPV6 notation to specify raw address with port (i.e. [fe80::1]:80) */
538 		p = memchr(str + 1, ']', str_len - 2);
539 		if (!p || *(p + 1) != ':') {
540 			if (get_err) {
541 				spprintf(err, 0, "Failed to parse IPv6 address \"%s\"", str);
542 			}
543 			return NULL;
544 		}
545 		*portno = atoi(p + 2);
546 		return estrndup(str + 1, p - str - 1);
547 	}
548 #endif
549 	if (str_len) {
550 		colon = memchr(str, ':', str_len - 1);
551 	} else {
552 		colon = NULL;
553 	}
554 	if (colon) {
555 		*portno = atoi(colon + 1);
556 		host = estrndup(str, colon - str);
557 	} else {
558 		if (get_err) {
559 			spprintf(err, 0, "Failed to parse address \"%s\"", str);
560 		}
561 		return NULL;
562 	}
563 
564 	return host;
565 }
566 
parse_ip_address(php_stream_xport_param * xparam,int * portno TSRMLS_DC)567 static inline char *parse_ip_address(php_stream_xport_param *xparam, int *portno TSRMLS_DC)
568 {
569 	return parse_ip_address_ex(xparam->inputs.name, xparam->inputs.namelen, portno, xparam->want_errortext, &xparam->outputs.error_text TSRMLS_CC);
570 }
571 
php_tcp_sockop_bind(php_stream * stream,php_netstream_data_t * sock,php_stream_xport_param * xparam TSRMLS_DC)572 static inline int php_tcp_sockop_bind(php_stream *stream, php_netstream_data_t *sock,
573 		php_stream_xport_param *xparam TSRMLS_DC)
574 {
575 	char *host = NULL;
576 	int portno, err;
577 
578 #ifdef AF_UNIX
579 	if (stream->ops == &php_stream_unix_socket_ops || stream->ops == &php_stream_unixdg_socket_ops) {
580 		struct sockaddr_un unix_addr;
581 
582 		sock->socket = socket(PF_UNIX, stream->ops == &php_stream_unix_socket_ops ? SOCK_STREAM : SOCK_DGRAM, 0);
583 
584 		if (sock->socket == SOCK_ERR) {
585 			if (xparam->want_errortext) {
586 				spprintf(&xparam->outputs.error_text, 0, "Failed to create unix%s socket %s",
587 						stream->ops == &php_stream_unix_socket_ops ? "" : "datagram",
588 						strerror(errno));
589 			}
590 			return -1;
591 		}
592 
593 		parse_unix_address(xparam, &unix_addr TSRMLS_CC);
594 
595 		return bind(sock->socket, (struct sockaddr *)&unix_addr, sizeof(unix_addr));
596 	}
597 #endif
598 
599 	host = parse_ip_address(xparam, &portno TSRMLS_CC);
600 
601 	if (host == NULL) {
602 		return -1;
603 	}
604 
605 	sock->socket = php_network_bind_socket_to_local_addr(host, portno,
606 			stream->ops == &php_stream_udp_socket_ops ? SOCK_DGRAM : SOCK_STREAM,
607 			xparam->want_errortext ? &xparam->outputs.error_text : NULL,
608 			&err
609 			TSRMLS_CC);
610 
611 	if (host) {
612 		efree(host);
613 	}
614 
615 	return sock->socket == -1 ? -1 : 0;
616 }
617 
php_tcp_sockop_connect(php_stream * stream,php_netstream_data_t * sock,php_stream_xport_param * xparam TSRMLS_DC)618 static inline int php_tcp_sockop_connect(php_stream *stream, php_netstream_data_t *sock,
619 		php_stream_xport_param *xparam TSRMLS_DC)
620 {
621 	char *host = NULL, *bindto = NULL;
622 	int portno, bindport = 0;
623 	int err = 0;
624 	int ret;
625 	zval **tmpzval = NULL;
626 
627 #ifdef AF_UNIX
628 	if (stream->ops == &php_stream_unix_socket_ops || stream->ops == &php_stream_unixdg_socket_ops) {
629 		struct sockaddr_un unix_addr;
630 
631 		sock->socket = socket(PF_UNIX, stream->ops == &php_stream_unix_socket_ops ? SOCK_STREAM : SOCK_DGRAM, 0);
632 
633 		if (sock->socket == SOCK_ERR) {
634 			if (xparam->want_errortext) {
635 				spprintf(&xparam->outputs.error_text, 0, "Failed to create unix socket");
636 			}
637 			return -1;
638 		}
639 
640 		parse_unix_address(xparam, &unix_addr TSRMLS_CC);
641 
642 		ret = php_network_connect_socket(sock->socket,
643 				(const struct sockaddr *)&unix_addr, (socklen_t) XtOffsetOf(struct sockaddr_un, sun_path) + xparam->inputs.namelen,
644 				xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC, xparam->inputs.timeout,
645 				xparam->want_errortext ? &xparam->outputs.error_text : NULL,
646 				&err);
647 
648 		xparam->outputs.error_code = err;
649 
650 		goto out;
651 	}
652 #endif
653 
654 	host = parse_ip_address(xparam, &portno TSRMLS_CC);
655 
656 	if (host == NULL) {
657 		return -1;
658 	}
659 
660 	if (stream->context && php_stream_context_get_option(stream->context, "socket", "bindto", &tmpzval) == SUCCESS) {
661 		if (Z_TYPE_PP(tmpzval) != IS_STRING) {
662 			if (xparam->want_errortext) {
663 				spprintf(&xparam->outputs.error_text, 0, "local_addr context option is not a string.");
664 			}
665 			efree(host);
666 			return -1;
667 		}
668 		bindto = parse_ip_address_ex(Z_STRVAL_PP(tmpzval), Z_STRLEN_PP(tmpzval), &bindport, xparam->want_errortext, &xparam->outputs.error_text TSRMLS_CC);
669 	}
670 
671 	/* Note: the test here for php_stream_udp_socket_ops is important, because we
672 	 * want the default to be TCP sockets so that the openssl extension can
673 	 * re-use this code. */
674 
675 	sock->socket = php_network_connect_socket_to_host(host, portno,
676 			stream->ops == &php_stream_udp_socket_ops ? SOCK_DGRAM : SOCK_STREAM,
677 			xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC,
678 			xparam->inputs.timeout,
679 			xparam->want_errortext ? &xparam->outputs.error_text : NULL,
680 			&err,
681 			bindto,
682 			bindport
683 			TSRMLS_CC);
684 
685 	ret = sock->socket == -1 ? -1 : 0;
686 	xparam->outputs.error_code = err;
687 
688 	if (host) {
689 		efree(host);
690 	}
691 	if (bindto) {
692 		efree(bindto);
693 	}
694 
695 #ifdef AF_UNIX
696 out:
697 #endif
698 
699 	if (ret >= 0 && xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC && err == EINPROGRESS) {
700 		/* indicates pending connection */
701 		return 1;
702 	}
703 
704 	return ret;
705 }
706 
php_tcp_sockop_accept(php_stream * stream,php_netstream_data_t * sock,php_stream_xport_param * xparam STREAMS_DC TSRMLS_DC)707 static inline int php_tcp_sockop_accept(php_stream *stream, php_netstream_data_t *sock,
708 		php_stream_xport_param *xparam STREAMS_DC TSRMLS_DC)
709 {
710 	int clisock;
711 
712 	xparam->outputs.client = NULL;
713 
714 	clisock = php_network_accept_incoming(sock->socket,
715 			xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
716 			xparam->want_textaddr ? &xparam->outputs.textaddrlen : NULL,
717 			xparam->want_addr ? &xparam->outputs.addr : NULL,
718 			xparam->want_addr ? &xparam->outputs.addrlen : NULL,
719 			xparam->inputs.timeout,
720 			xparam->want_errortext ? &xparam->outputs.error_text : NULL,
721 			&xparam->outputs.error_code
722 			TSRMLS_CC);
723 
724 	if (clisock >= 0) {
725 		php_netstream_data_t *clisockdata;
726 
727 		clisockdata = emalloc(sizeof(*clisockdata));
728 
729 		if (clisockdata == NULL) {
730 			close(clisock);
731 			/* technically a fatal error */
732 		} else {
733 			memcpy(clisockdata, sock, sizeof(*clisockdata));
734 			clisockdata->socket = clisock;
735 
736 			xparam->outputs.client = php_stream_alloc_rel(stream->ops, clisockdata, NULL, "r+");
737 			if (xparam->outputs.client) {
738 				xparam->outputs.client->context = stream->context;
739 				if (stream->context) {
740 					zend_list_addref(stream->context->rsrc_id);
741 				}
742 			}
743 		}
744 	}
745 
746 	return xparam->outputs.client == NULL ? -1 : 0;
747 }
748 
php_tcp_sockop_set_option(php_stream * stream,int option,int value,void * ptrparam TSRMLS_DC)749 static int php_tcp_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC)
750 {
751 	php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
752 	php_stream_xport_param *xparam;
753 
754 	switch(option) {
755 		case PHP_STREAM_OPTION_XPORT_API:
756 			xparam = (php_stream_xport_param *)ptrparam;
757 
758 			switch(xparam->op) {
759 				case STREAM_XPORT_OP_CONNECT:
760 				case STREAM_XPORT_OP_CONNECT_ASYNC:
761 					xparam->outputs.returncode = php_tcp_sockop_connect(stream, sock, xparam TSRMLS_CC);
762 					return PHP_STREAM_OPTION_RETURN_OK;
763 
764 				case STREAM_XPORT_OP_BIND:
765 					xparam->outputs.returncode = php_tcp_sockop_bind(stream, sock, xparam TSRMLS_CC);
766 					return PHP_STREAM_OPTION_RETURN_OK;
767 
768 
769 				case STREAM_XPORT_OP_ACCEPT:
770 					xparam->outputs.returncode = php_tcp_sockop_accept(stream, sock, xparam STREAMS_CC TSRMLS_CC);
771 					return PHP_STREAM_OPTION_RETURN_OK;
772 				default:
773 					/* fall through */
774 					;
775 			}
776 	}
777 	return php_sockop_set_option(stream, option, value, ptrparam TSRMLS_CC);
778 }
779 
780 
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)781 PHPAPI php_stream *php_stream_generic_socket_factory(const char *proto, long protolen,
782 		char *resourcename, long resourcenamelen,
783 		const char *persistent_id, int options, int flags,
784 		struct timeval *timeout,
785 		php_stream_context *context STREAMS_DC TSRMLS_DC)
786 {
787 	php_stream *stream = NULL;
788 	php_netstream_data_t *sock;
789 	php_stream_ops *ops;
790 
791 	/* which type of socket ? */
792 	if (strncmp(proto, "tcp", protolen) == 0) {
793 		ops = &php_stream_socket_ops;
794 	} else if (strncmp(proto, "udp", protolen) == 0) {
795 		ops = &php_stream_udp_socket_ops;
796 	}
797 #ifdef AF_UNIX
798 	else if (strncmp(proto, "unix", protolen) == 0) {
799 		ops = &php_stream_unix_socket_ops;
800 	} else if (strncmp(proto, "udg", protolen) == 0) {
801 		ops = &php_stream_unixdg_socket_ops;
802 	}
803 #endif
804 	else {
805 		/* should never happen */
806 		return NULL;
807 	}
808 
809 	sock = pemalloc(sizeof(php_netstream_data_t), persistent_id ? 1 : 0);
810 	memset(sock, 0, sizeof(php_netstream_data_t));
811 
812 	sock->is_blocked = 1;
813 	sock->timeout.tv_sec = FG(default_socket_timeout);
814 	sock->timeout.tv_usec = 0;
815 
816 	/* we don't know the socket until we have determined if we are binding or
817 	 * connecting */
818 	sock->socket = -1;
819 
820 	stream = php_stream_alloc_rel(ops, sock, persistent_id, "r+");
821 
822 	if (stream == NULL)	{
823 		pefree(sock, persistent_id ? 1 : 0);
824 		return NULL;
825 	}
826 
827 	if (flags == 0) {
828 		return stream;
829 	}
830 
831 	return stream;
832 }
833 
834 
835 /*
836  * Local variables:
837  * tab-width: 4
838  * c-basic-offset: 4
839  * End:
840  * vim600: noet sw=4 ts=4 fdm=marker
841  * vim<600: noet sw=4 ts=4
842  */
843