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