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