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