1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2014 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 *(int*)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