1 : /*
2 : +----------------------------------------------------------------------+
3 : | PHP Version 5 |
4 : +----------------------------------------------------------------------+
5 : | Copyright (c) 1997-2007 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: xp_socket.c,v 1.33.2.2.2.4 2007/01/01 09:36:12 sebastian Exp $ */
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 : php_stream_ops php_stream_generic_socket_ops;
35 : PHPAPI php_stream_ops php_stream_socket_ops;
36 : php_stream_ops php_stream_udp_socket_ops;
37 : #ifdef AF_UNIX
38 : php_stream_ops php_stream_unix_socket_ops;
39 : php_stream_ops php_stream_unixdg_socket_ops;
40 : #endif
41 :
42 :
43 : static int php_tcp_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC);
44 :
45 : /* {{{ Generic socket stream operations */
46 : static size_t php_sockop_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
47 0 : {
48 0 : php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
49 : int didwrite;
50 : struct timeval *ptimeout;
51 :
52 0 : if (sock->socket == -1) {
53 0 : return 0;
54 : }
55 :
56 0 : if (sock->timeout.tv_sec == -1)
57 0 : ptimeout = NULL;
58 : else
59 0 : ptimeout = &sock->timeout;
60 :
61 0 : retry:
62 0 : didwrite = send(sock->socket, buf, count, 0);
63 :
64 0 : if (didwrite <= 0) {
65 0 : long err = php_socket_errno();
66 : char *estr;
67 :
68 0 : if (sock->is_blocked && err == EWOULDBLOCK) {
69 : int retval;
70 :
71 0 : sock->timeout_event = 0;
72 :
73 : do {
74 0 : retval = php_pollfd_for(sock->socket, POLLOUT, ptimeout);
75 :
76 0 : if (retval == 0) {
77 0 : sock->timeout_event = 1;
78 0 : break;
79 : }
80 :
81 0 : if (retval > 0) {
82 : /* writable now; retry */
83 0 : goto retry;
84 : }
85 :
86 0 : err = php_socket_errno();
87 0 : } while (err == EINTR);
88 : }
89 0 : estr = php_socket_strerror(err, NULL, 0);
90 0 : php_error_docref(NULL TSRMLS_CC, E_NOTICE, "send of %ld bytes failed with errno=%ld %s",
91 : (long)count, err, estr);
92 0 : efree(estr);
93 : }
94 :
95 0 : if (didwrite > 0) {
96 0 : php_stream_notify_progress_increment(stream->context, didwrite, 0);
97 : }
98 :
99 0 : if (didwrite < 0) {
100 0 : didwrite = 0;
101 : }
102 :
103 0 : return didwrite;
104 : }
105 :
106 : static void php_sock_stream_wait_for_data(php_stream *stream, php_netstream_data_t *sock TSRMLS_DC)
107 0 : {
108 : int retval;
109 : struct timeval *ptimeout;
110 :
111 0 : if (sock->socket == -1) {
112 0 : return;
113 : }
114 :
115 0 : sock->timeout_event = 0;
116 :
117 0 : if (sock->timeout.tv_sec == -1)
118 0 : ptimeout = NULL;
119 : else
120 0 : ptimeout = &sock->timeout;
121 :
122 : while(1) {
123 0 : retval = php_pollfd_for(sock->socket, PHP_POLLREADABLE, ptimeout);
124 :
125 0 : if (retval == 0)
126 0 : sock->timeout_event = 1;
127 :
128 0 : if (retval >= 0)
129 0 : break;
130 :
131 0 : if (php_socket_errno() != EINTR)
132 0 : break;
133 0 : }
134 : }
135 :
136 : static size_t php_sockop_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
137 0 : {
138 0 : php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
139 0 : int nr_bytes = 0;
140 :
141 0 : if (sock->socket == -1) {
142 0 : return 0;
143 : }
144 :
145 0 : if (sock->is_blocked) {
146 0 : php_sock_stream_wait_for_data(stream, sock TSRMLS_CC);
147 0 : if (sock->timeout_event)
148 0 : return 0;
149 : }
150 :
151 0 : nr_bytes = recv(sock->socket, buf, count, 0);
152 :
153 0 : stream->eof = (nr_bytes == 0 || (nr_bytes == -1 && php_socket_errno() != EWOULDBLOCK));
154 :
155 0 : if (nr_bytes > 0) {
156 0 : php_stream_notify_progress_increment(stream->context, nr_bytes, 0);
157 : }
158 :
159 0 : if (nr_bytes < 0) {
160 0 : nr_bytes = 0;
161 : }
162 :
163 0 : return nr_bytes;
164 : }
165 :
166 :
167 : static int php_sockop_close(php_stream *stream, int close_handle TSRMLS_DC)
168 14 : {
169 14 : php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
170 : #ifdef PHP_WIN32
171 : int n;
172 : #endif
173 :
174 14 : if (close_handle) {
175 :
176 14 : if (sock->socket != SOCK_ERR) {
177 : #ifdef PHP_WIN32
178 : /* prevent more data from coming in */
179 : shutdown(sock->socket, SHUT_RD);
180 :
181 : /* try to make sure that the OS sends all data before we close the connection.
182 : * Essentially, we are waiting for the socket to become writeable, which means
183 : * that all pending data has been sent.
184 : * We use a small timeout which should encourage the OS to send the data,
185 : * but at the same time avoid hanging indefintely.
186 : * */
187 : do {
188 : n = php_pollfd_for_ms(sock->socket, POLLOUT, 500);
189 : } while (n == -1 && php_socket_errno() == EINTR);
190 : #endif
191 14 : closesocket(sock->socket);
192 14 : sock->socket = SOCK_ERR;
193 : }
194 :
195 : }
196 :
197 14 : pefree(sock, php_stream_is_persistent(stream));
198 :
199 14 : return 0;
200 : }
201 :
202 : static int php_sockop_flush(php_stream *stream TSRMLS_DC)
203 14 : {
204 : #if 0
205 : php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
206 : return fsync(sock->socket);
207 : #endif
208 14 : return 0;
209 : }
210 :
211 : static int php_sockop_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC)
212 0 : {
213 0 : php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
214 0 : return fstat(sock->socket, &ssb->sb);
215 : }
216 :
217 : static inline int sock_sendto(php_netstream_data_t *sock, char *buf, size_t buflen, int flags,
218 : struct sockaddr *addr, socklen_t addrlen
219 : TSRMLS_DC)
220 0 : {
221 0 : if (addr) {
222 0 : return sendto(sock->socket, buf, buflen, flags, addr, addrlen);
223 : }
224 0 : return send(sock->socket, buf, buflen, flags);
225 : }
226 :
227 : static inline int sock_recvfrom(php_netstream_data_t *sock, char *buf, size_t buflen, int flags,
228 : char **textaddr, long *textaddrlen,
229 : struct sockaddr **addr, socklen_t *addrlen
230 : TSRMLS_DC)
231 0 : {
232 : php_sockaddr_storage sa;
233 0 : socklen_t sl = sizeof(sa);
234 : int ret;
235 0 : int want_addr = textaddr || addr;
236 :
237 0 : if (want_addr) {
238 0 : ret = recvfrom(sock->socket, buf, buflen, flags, (struct sockaddr*)&sa, &sl);
239 0 : php_network_populate_name_from_sockaddr((struct sockaddr*)&sa, sl,
240 : textaddr, textaddrlen, addr, addrlen TSRMLS_CC);
241 : } else {
242 0 : ret = recv(sock->socket, buf, buflen, flags);
243 : }
244 :
245 0 : return ret;
246 : }
247 :
248 : static int php_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC)
249 0 : {
250 : int oldmode, flags;
251 0 : php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
252 : php_stream_xport_param *xparam;
253 :
254 0 : switch(option) {
255 : case PHP_STREAM_OPTION_CHECK_LIVENESS:
256 : {
257 : struct timeval tv;
258 : char buf;
259 0 : int alive = 1;
260 :
261 0 : if (value == -1) {
262 0 : if (sock->timeout.tv_sec == -1) {
263 0 : tv.tv_sec = FG(default_socket_timeout);
264 0 : tv.tv_usec = 0;
265 : } else {
266 0 : tv = sock->timeout;
267 : }
268 : } else {
269 0 : tv.tv_sec = value;
270 0 : tv.tv_usec = 0;
271 : }
272 :
273 0 : if (sock->socket == -1) {
274 0 : alive = 0;
275 0 : } else if (php_pollfd_for(sock->socket, PHP_POLLREADABLE|POLLPRI, &tv) > 0) {
276 0 : if (0 == recv(sock->socket, &buf, sizeof(buf), MSG_PEEK) && php_socket_errno() != EAGAIN) {
277 0 : alive = 0;
278 : }
279 : }
280 0 : return alive ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR;
281 : }
282 :
283 : case PHP_STREAM_OPTION_BLOCKING:
284 :
285 0 : oldmode = sock->is_blocked;
286 :
287 : /* no need to change anything */
288 0 : if (value == oldmode)
289 0 : return oldmode;
290 :
291 0 : if (SUCCESS == php_set_sock_blocking(sock->socket, value TSRMLS_CC)) {
292 0 : sock->is_blocked = value;
293 0 : return oldmode;
294 : }
295 :
296 0 : return PHP_STREAM_OPTION_RETURN_ERR;
297 :
298 : case PHP_STREAM_OPTION_READ_TIMEOUT:
299 0 : sock->timeout = *(struct timeval*)ptrparam;
300 0 : sock->timeout_event = 0;
301 0 : return PHP_STREAM_OPTION_RETURN_OK;
302 :
303 : case PHP_STREAM_OPTION_META_DATA_API:
304 0 : add_assoc_bool((zval *)ptrparam, "timed_out", sock->timeout_event);
305 0 : add_assoc_bool((zval *)ptrparam, "blocked", sock->is_blocked);
306 0 : add_assoc_bool((zval *)ptrparam, "eof", stream->eof);
307 0 : return PHP_STREAM_OPTION_RETURN_OK;
308 :
309 : case PHP_STREAM_OPTION_XPORT_API:
310 0 : xparam = (php_stream_xport_param *)ptrparam;
311 :
312 0 : switch (xparam->op) {
313 : case STREAM_XPORT_OP_LISTEN:
314 0 : xparam->outputs.returncode = listen(sock->socket, 5);
315 0 : return PHP_STREAM_OPTION_RETURN_OK;
316 :
317 : case STREAM_XPORT_OP_GET_NAME:
318 0 : xparam->outputs.returncode = php_network_get_sock_name(sock->socket,
319 : xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
320 : xparam->want_textaddr ? &xparam->outputs.textaddrlen : NULL,
321 : xparam->want_addr ? &xparam->outputs.addr : NULL,
322 : xparam->want_addr ? &xparam->outputs.addrlen : NULL
323 : TSRMLS_CC);
324 0 : return PHP_STREAM_OPTION_RETURN_OK;
325 :
326 : case STREAM_XPORT_OP_GET_PEER_NAME:
327 0 : xparam->outputs.returncode = php_network_get_peer_name(sock->socket,
328 : xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
329 : xparam->want_textaddr ? &xparam->outputs.textaddrlen : NULL,
330 : xparam->want_addr ? &xparam->outputs.addr : NULL,
331 : xparam->want_addr ? &xparam->outputs.addrlen : NULL
332 : TSRMLS_CC);
333 0 : return PHP_STREAM_OPTION_RETURN_OK;
334 :
335 : case STREAM_XPORT_OP_SEND:
336 0 : flags = 0;
337 0 : if ((xparam->inputs.flags & STREAM_OOB) == STREAM_OOB) {
338 0 : flags |= MSG_OOB;
339 : }
340 0 : xparam->outputs.returncode = sock_sendto(sock,
341 : xparam->inputs.buf, xparam->inputs.buflen,
342 : flags,
343 : xparam->inputs.addr,
344 : xparam->inputs.addrlen TSRMLS_CC);
345 0 : if (xparam->outputs.returncode == -1) {
346 0 : char *err = php_socket_strerror(php_socket_errno(), NULL, 0);
347 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING,
348 : "%s\n", err);
349 0 : efree(err);
350 : }
351 0 : return PHP_STREAM_OPTION_RETURN_OK;
352 :
353 : case STREAM_XPORT_OP_RECV:
354 0 : flags = 0;
355 0 : if ((xparam->inputs.flags & STREAM_OOB) == STREAM_OOB) {
356 0 : flags |= MSG_OOB;
357 : }
358 0 : if ((xparam->inputs.flags & STREAM_PEEK) == STREAM_PEEK) {
359 0 : flags |= MSG_PEEK;
360 : }
361 0 : xparam->outputs.returncode = sock_recvfrom(sock,
362 : xparam->inputs.buf, xparam->inputs.buflen,
363 : flags,
364 : xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
365 : xparam->want_textaddr ? &xparam->outputs.textaddrlen : NULL,
366 : xparam->want_addr ? &xparam->outputs.addr : NULL,
367 : xparam->want_addr ? &xparam->outputs.addrlen : NULL
368 : TSRMLS_CC);
369 0 : return PHP_STREAM_OPTION_RETURN_OK;
370 :
371 :
372 : #ifdef HAVE_SHUTDOWN
373 : # ifndef SHUT_RD
374 : # define SHUT_RD 0
375 : # endif
376 : # ifndef SHUT_WR
377 : # define SHUT_WR 1
378 : # endif
379 : # ifndef SHUT_RDWR
380 : # define SHUT_RDWR 2
381 : # endif
382 : case STREAM_XPORT_OP_SHUTDOWN: {
383 : static const int shutdown_how[] = {SHUT_RD, SHUT_WR, SHUT_RDWR};
384 :
385 0 : xparam->outputs.returncode = shutdown(sock->socket, shutdown_how[xparam->how]);
386 0 : return PHP_STREAM_OPTION_RETURN_OK;
387 : }
388 : #endif
389 :
390 : default:
391 0 : return PHP_STREAM_OPTION_RETURN_NOTIMPL;
392 : }
393 :
394 : default:
395 0 : return PHP_STREAM_OPTION_RETURN_NOTIMPL;
396 : }
397 : }
398 :
399 : static int php_sockop_cast(php_stream *stream, int castas, void **ret TSRMLS_DC)
400 0 : {
401 0 : php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
402 :
403 0 : switch(castas) {
404 : case PHP_STREAM_AS_STDIO:
405 0 : if (ret) {
406 0 : *(FILE**)ret = fdopen(sock->socket, stream->mode);
407 0 : if (*ret)
408 0 : return SUCCESS;
409 0 : return FAILURE;
410 : }
411 0 : return SUCCESS;
412 : case PHP_STREAM_AS_FD_FOR_SELECT:
413 : case PHP_STREAM_AS_FD:
414 : case PHP_STREAM_AS_SOCKETD:
415 0 : if (ret)
416 0 : *(int*)ret = sock->socket;
417 0 : return SUCCESS;
418 : default:
419 0 : return FAILURE;
420 : }
421 : }
422 : /* }}} */
423 :
424 : /* These may look identical, but we need them this way so that
425 : * we can determine which type of socket we are dealing with
426 : * by inspecting stream->ops.
427 : * A "useful" side-effect is that the user's scripts can then
428 : * make similar decisions using stream_get_meta_data.
429 : * */
430 : php_stream_ops php_stream_generic_socket_ops = {
431 : php_sockop_write, php_sockop_read,
432 : php_sockop_close, php_sockop_flush,
433 : "generic_socket",
434 : NULL, /* seek */
435 : php_sockop_cast,
436 : php_sockop_stat,
437 : php_sockop_set_option,
438 : };
439 :
440 :
441 : php_stream_ops php_stream_socket_ops = {
442 : php_sockop_write, php_sockop_read,
443 : php_sockop_close, php_sockop_flush,
444 : "tcp_socket",
445 : NULL, /* seek */
446 : php_sockop_cast,
447 : php_sockop_stat,
448 : php_tcp_sockop_set_option,
449 : };
450 :
451 : php_stream_ops php_stream_udp_socket_ops = {
452 : php_sockop_write, php_sockop_read,
453 : php_sockop_close, php_sockop_flush,
454 : "udp_socket",
455 : NULL, /* seek */
456 : php_sockop_cast,
457 : php_sockop_stat,
458 : php_tcp_sockop_set_option,
459 : };
460 :
461 : #ifdef AF_UNIX
462 : php_stream_ops php_stream_unix_socket_ops = {
463 : php_sockop_write, php_sockop_read,
464 : php_sockop_close, php_sockop_flush,
465 : "unix_socket",
466 : NULL, /* seek */
467 : php_sockop_cast,
468 : php_sockop_stat,
469 : php_tcp_sockop_set_option,
470 : };
471 : php_stream_ops php_stream_unixdg_socket_ops = {
472 : php_sockop_write, php_sockop_read,
473 : php_sockop_close, php_sockop_flush,
474 : "udg_socket",
475 : NULL, /* seek */
476 : php_sockop_cast,
477 : php_sockop_stat,
478 : php_tcp_sockop_set_option,
479 : };
480 : #endif
481 :
482 :
483 : /* network socket operations */
484 :
485 : #ifdef AF_UNIX
486 : static inline int parse_unix_address(php_stream_xport_param *xparam, struct sockaddr_un *unix_addr TSRMLS_DC)
487 0 : {
488 0 : memset(unix_addr, 0, sizeof(*unix_addr));
489 0 : unix_addr->sun_family = AF_UNIX;
490 :
491 : /* we need to be binary safe on systems that support an abstract
492 : * namespace */
493 0 : if (xparam->inputs.namelen >= sizeof(unix_addr->sun_path)) {
494 : /* On linux, when the path begins with a NUL byte we are
495 : * referring to an abstract namespace. In theory we should
496 : * allow an extra byte below, since we don't need the NULL.
497 : * BUT, to get into this branch of code, the name is too long,
498 : * so we don't care. */
499 0 : xparam->inputs.namelen = sizeof(unix_addr->sun_path) - 1;
500 : }
501 :
502 0 : memcpy(unix_addr->sun_path, xparam->inputs.name, xparam->inputs.namelen);
503 :
504 0 : return 1;
505 : }
506 : #endif
507 :
508 : static inline char *parse_ip_address_ex(const char *str, int str_len, int *portno, int get_err, char **err TSRMLS_DC)
509 14 : {
510 : char *colon;
511 14 : char *host = NULL;
512 :
513 : #ifdef HAVE_IPV6
514 : char *p;
515 :
516 14 : if (*(str) == '[' && str_len > 1) {
517 : /* IPV6 notation to specify raw address with port (i.e. [fe80::1]:80) */
518 0 : p = memchr(str + 1, ']', str_len - 2);
519 0 : if (!p || *(p + 1) != ':') {
520 0 : if (get_err) {
521 0 : spprintf(err, 0, "Failed to parse IPv6 address \"%s\"", str);
522 : }
523 0 : return NULL;
524 : }
525 0 : *portno = atoi(p + 2);
526 0 : return estrndup(str + 1, p - str - 1);
527 : }
528 : #endif
529 14 : if (str_len) {
530 14 : colon = memchr(str, ':', str_len - 1);
531 : } else {
532 0 : colon = NULL;
533 : }
534 14 : if (colon) {
535 14 : *portno = atoi(colon + 1);
536 14 : host = estrndup(str, colon - str);
537 : } else {
538 0 : if (get_err) {
539 0 : spprintf(err, 0, "Failed to parse address \"%s\"", str);
540 : }
541 0 : return NULL;
542 : }
543 :
544 14 : return host;
545 : }
546 :
547 : static inline char *parse_ip_address(php_stream_xport_param *xparam, int *portno TSRMLS_DC)
548 14 : {
549 14 : return parse_ip_address_ex(xparam->inputs.name, xparam->inputs.namelen, portno, xparam->want_errortext, &xparam->outputs.error_text TSRMLS_CC);
550 : }
551 :
552 : static inline int php_tcp_sockop_bind(php_stream *stream, php_netstream_data_t *sock,
553 : php_stream_xport_param *xparam TSRMLS_DC)
554 0 : {
555 0 : char *host = NULL;
556 : int portno, err;
557 :
558 : #ifdef AF_UNIX
559 0 : if (stream->ops == &php_stream_unix_socket_ops || stream->ops == &php_stream_unixdg_socket_ops) {
560 : struct sockaddr_un unix_addr;
561 :
562 0 : sock->socket = socket(PF_UNIX, stream->ops == &php_stream_unix_socket_ops ? SOCK_STREAM : SOCK_DGRAM, 0);
563 :
564 0 : if (sock->socket == SOCK_ERR) {
565 0 : if (xparam->want_errortext) {
566 0 : spprintf(&xparam->outputs.error_text, 0, "Failed to create unix%s socket %s",
567 : stream->ops == &php_stream_unix_socket_ops ? "" : "datagram",
568 : strerror(errno));
569 : }
570 0 : return -1;
571 : }
572 :
573 0 : parse_unix_address(xparam, &unix_addr TSRMLS_CC);
574 :
575 0 : return bind(sock->socket, (struct sockaddr *)&unix_addr, sizeof(unix_addr));
576 : }
577 : #endif
578 :
579 0 : host = parse_ip_address(xparam, &portno TSRMLS_CC);
580 :
581 0 : if (host == NULL) {
582 0 : return -1;
583 : }
584 :
585 0 : sock->socket = php_network_bind_socket_to_local_addr(host, portno,
586 : stream->ops == &php_stream_udp_socket_ops ? SOCK_DGRAM : SOCK_STREAM,
587 : xparam->want_errortext ? &xparam->outputs.error_text : NULL,
588 : &err
589 : TSRMLS_CC);
590 :
591 0 : if (host) {
592 0 : efree(host);
593 : }
594 :
595 0 : return sock->socket == -1 ? -1 : 0;
596 : }
597 :
598 : static inline int php_tcp_sockop_connect(php_stream *stream, php_netstream_data_t *sock,
599 : php_stream_xport_param *xparam TSRMLS_DC)
600 14 : {
601 14 : char *host = NULL, *bindto = NULL;
602 14 : int portno, bindport = 0;
603 : int err;
604 : int ret;
605 14 : zval **tmpzval = NULL;
606 :
607 : #ifdef AF_UNIX
608 14 : if (stream->ops == &php_stream_unix_socket_ops || stream->ops == &php_stream_unixdg_socket_ops) {
609 : struct sockaddr_un unix_addr;
610 :
611 0 : sock->socket = socket(PF_UNIX, stream->ops == &php_stream_unix_socket_ops ? SOCK_STREAM : SOCK_DGRAM, 0);
612 :
613 0 : if (sock->socket == SOCK_ERR) {
614 0 : if (xparam->want_errortext) {
615 0 : spprintf(&xparam->outputs.error_text, 0, "Failed to create unix socket");
616 : }
617 0 : return -1;
618 : }
619 :
620 0 : parse_unix_address(xparam, &unix_addr TSRMLS_CC);
621 :
622 0 : ret = php_network_connect_socket(sock->socket,
623 : (const struct sockaddr *)&unix_addr, (socklen_t)sizeof(unix_addr),
624 : xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC, xparam->inputs.timeout,
625 : xparam->want_errortext ? &xparam->outputs.error_text : NULL,
626 : &err);
627 :
628 0 : xparam->outputs.error_code = err;
629 :
630 0 : goto out;
631 : }
632 : #endif
633 :
634 14 : host = parse_ip_address(xparam, &portno TSRMLS_CC);
635 :
636 14 : if (host == NULL) {
637 0 : return -1;
638 : }
639 :
640 14 : if (stream->context && php_stream_context_get_option(stream->context, "socket", "bindto", &tmpzval) == SUCCESS) {
641 0 : if (Z_TYPE_PP(tmpzval) != IS_STRING) {
642 0 : if (xparam->want_errortext) {
643 0 : spprintf(&xparam->outputs.error_text, 0, "local_addr context option is not a string.");
644 : }
645 0 : efree(host);
646 0 : return -1;
647 : }
648 0 : bindto = parse_ip_address_ex(Z_STRVAL_PP(tmpzval), Z_STRLEN_PP(tmpzval), &bindport, xparam->want_errortext, &xparam->outputs.error_text TSRMLS_CC);
649 : }
650 :
651 : /* Note: the test here for php_stream_udp_socket_ops is important, because we
652 : * want the default to be TCP sockets so that the openssl extension can
653 : * re-use this code. */
654 :
655 14 : sock->socket = php_network_connect_socket_to_host(host, portno,
656 : stream->ops == &php_stream_udp_socket_ops ? SOCK_DGRAM : SOCK_STREAM,
657 : xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC,
658 : xparam->inputs.timeout,
659 : xparam->want_errortext ? &xparam->outputs.error_text : NULL,
660 : &err,
661 : bindto,
662 : bindport
663 : TSRMLS_CC);
664 :
665 14 : ret = sock->socket == -1 ? -1 : 0;
666 14 : xparam->outputs.error_code = err;
667 :
668 14 : if (host) {
669 14 : efree(host);
670 : }
671 14 : if (bindto) {
672 0 : efree(bindto);
673 : }
674 :
675 : #ifdef AF_UNIX
676 14 : out:
677 : #endif
678 :
679 14 : if (ret >= 0 && xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC && err == EINPROGRESS) {
680 : /* indicates pending connection */
681 0 : return 1;
682 : }
683 :
684 14 : return ret;
685 : }
686 :
687 : static inline int php_tcp_sockop_accept(php_stream *stream, php_netstream_data_t *sock,
688 : php_stream_xport_param *xparam STREAMS_DC TSRMLS_DC)
689 0 : {
690 : int clisock;
691 :
692 0 : xparam->outputs.client = NULL;
693 :
694 0 : clisock = php_network_accept_incoming(sock->socket,
695 : xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
696 : xparam->want_textaddr ? &xparam->outputs.textaddrlen : NULL,
697 : xparam->want_addr ? &xparam->outputs.addr : NULL,
698 : xparam->want_addr ? &xparam->outputs.addrlen : NULL,
699 : xparam->inputs.timeout,
700 : xparam->want_errortext ? &xparam->outputs.error_text : NULL,
701 : &xparam->outputs.error_code
702 : TSRMLS_CC);
703 :
704 0 : if (clisock >= 0) {
705 : php_netstream_data_t *clisockdata;
706 :
707 0 : clisockdata = emalloc(sizeof(*clisockdata));
708 :
709 0 : if (clisockdata == NULL) {
710 0 : close(clisock);
711 : /* technically a fatal error */
712 : } else {
713 0 : memcpy(clisockdata, sock, sizeof(*clisockdata));
714 0 : clisockdata->socket = clisock;
715 :
716 0 : xparam->outputs.client = php_stream_alloc_rel(stream->ops, clisockdata, NULL, "r+");
717 0 : if (xparam->outputs.client) {
718 : /* TODO: addref ? */
719 0 : xparam->outputs.client->context = stream->context;
720 : }
721 : }
722 : }
723 :
724 0 : return xparam->outputs.client == NULL ? -1 : 0;
725 : }
726 :
727 : static int php_tcp_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC)
728 14 : {
729 14 : php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
730 : php_stream_xport_param *xparam;
731 :
732 14 : switch(option) {
733 : case PHP_STREAM_OPTION_XPORT_API:
734 14 : xparam = (php_stream_xport_param *)ptrparam;
735 :
736 14 : switch(xparam->op) {
737 : case STREAM_XPORT_OP_CONNECT:
738 : case STREAM_XPORT_OP_CONNECT_ASYNC:
739 14 : xparam->outputs.returncode = php_tcp_sockop_connect(stream, sock, xparam TSRMLS_CC);
740 14 : return PHP_STREAM_OPTION_RETURN_OK;
741 :
742 : case STREAM_XPORT_OP_BIND:
743 0 : xparam->outputs.returncode = php_tcp_sockop_bind(stream, sock, xparam TSRMLS_CC);
744 0 : return PHP_STREAM_OPTION_RETURN_OK;
745 :
746 :
747 : case STREAM_XPORT_OP_ACCEPT:
748 0 : xparam->outputs.returncode = php_tcp_sockop_accept(stream, sock, xparam STREAMS_CC TSRMLS_CC);
749 0 : return PHP_STREAM_OPTION_RETURN_OK;
750 : default:
751 : /* fall through */
752 : ;
753 : }
754 :
755 : /* fall through */
756 : default:
757 0 : return php_sockop_set_option(stream, option, value, ptrparam TSRMLS_CC);
758 : }
759 : }
760 :
761 :
762 : PHPAPI php_stream *php_stream_generic_socket_factory(const char *proto, long protolen,
763 : char *resourcename, long resourcenamelen,
764 : const char *persistent_id, int options, int flags,
765 : struct timeval *timeout,
766 : php_stream_context *context STREAMS_DC TSRMLS_DC)
767 14 : {
768 14 : php_stream *stream = NULL;
769 : php_netstream_data_t *sock;
770 : php_stream_ops *ops;
771 :
772 : /* which type of socket ? */
773 14 : if (strncmp(proto, "tcp", protolen) == 0) {
774 14 : ops = &php_stream_socket_ops;
775 0 : } else if (strncmp(proto, "udp", protolen) == 0) {
776 0 : ops = &php_stream_udp_socket_ops;
777 : }
778 : #ifdef AF_UNIX
779 0 : else if (strncmp(proto, "unix", protolen) == 0) {
780 0 : ops = &php_stream_unix_socket_ops;
781 0 : } else if (strncmp(proto, "udg", protolen) == 0) {
782 0 : ops = &php_stream_unixdg_socket_ops;
783 : }
784 : #endif
785 : else {
786 : /* should never happen */
787 0 : return NULL;
788 : }
789 :
790 14 : sock = pemalloc(sizeof(php_netstream_data_t), persistent_id ? 1 : 0);
791 14 : memset(sock, 0, sizeof(php_netstream_data_t));
792 :
793 14 : sock->is_blocked = 1;
794 14 : sock->timeout.tv_sec = FG(default_socket_timeout);
795 14 : sock->timeout.tv_usec = 0;
796 :
797 : /* we don't know the socket until we have determined if we are binding or
798 : * connecting */
799 14 : sock->socket = -1;
800 :
801 14 : stream = php_stream_alloc_rel(ops, sock, persistent_id, "r+");
802 :
803 14 : if (stream == NULL) {
804 0 : pefree(sock, persistent_id ? 1 : 0);
805 0 : return NULL;
806 : }
807 :
808 14 : if (flags == 0) {
809 0 : return stream;
810 : }
811 :
812 14 : return stream;
813 : }
814 :
815 :
816 : /*
817 : * Local variables:
818 : * tab-width: 4
819 : * c-basic-offset: 4
820 : * End:
821 : * vim600: noet sw=4 ts=4 fdm=marker
822 : * vim<600: noet sw=4 ts=4
823 : */
|