Annotation of embedaddon/php/main/streams/xp_socket.c, revision 1.1
1.1 ! misho 1: /*
! 2: +----------------------------------------------------------------------+
! 3: | PHP Version 5 |
! 4: +----------------------------------------------------------------------+
! 5: | Copyright (c) 1997-2012 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 321634 2012-01-01 13:15:04Z felipe $ */
! 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: {
! 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:
! 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:
! 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:
! 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:
! 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:
! 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:
! 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:
! 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:
! 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:
! 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
! 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: }
! 518:
! 519: memcpy(unix_addr->sun_path, xparam->inputs.name, xparam->inputs.namelen);
! 520:
! 521: return 1;
! 522: }
! 523: #endif
! 524:
! 525: static inline char *parse_ip_address_ex(const char *str, int str_len, int *portno, int get_err, char **err TSRMLS_DC)
! 526: {
! 527: char *colon;
! 528: char *host = NULL;
! 529:
! 530: #ifdef HAVE_IPV6
! 531: char *p;
! 532:
! 533: if (*(str) == '[' && str_len > 1) {
! 534: /* IPV6 notation to specify raw address with port (i.e. [fe80::1]:80) */
! 535: p = memchr(str + 1, ']', str_len - 2);
! 536: if (!p || *(p + 1) != ':') {
! 537: if (get_err) {
! 538: spprintf(err, 0, "Failed to parse IPv6 address \"%s\"", str);
! 539: }
! 540: return NULL;
! 541: }
! 542: *portno = atoi(p + 2);
! 543: return estrndup(str + 1, p - str - 1);
! 544: }
! 545: #endif
! 546: if (str_len) {
! 547: colon = memchr(str, ':', str_len - 1);
! 548: } else {
! 549: colon = NULL;
! 550: }
! 551: if (colon) {
! 552: *portno = atoi(colon + 1);
! 553: host = estrndup(str, colon - str);
! 554: } else {
! 555: if (get_err) {
! 556: spprintf(err, 0, "Failed to parse address \"%s\"", str);
! 557: }
! 558: return NULL;
! 559: }
! 560:
! 561: return host;
! 562: }
! 563:
! 564: static inline char *parse_ip_address(php_stream_xport_param *xparam, int *portno TSRMLS_DC)
! 565: {
! 566: return parse_ip_address_ex(xparam->inputs.name, xparam->inputs.namelen, portno, xparam->want_errortext, &xparam->outputs.error_text TSRMLS_CC);
! 567: }
! 568:
! 569: static inline int php_tcp_sockop_bind(php_stream *stream, php_netstream_data_t *sock,
! 570: php_stream_xport_param *xparam TSRMLS_DC)
! 571: {
! 572: char *host = NULL;
! 573: int portno, err;
! 574:
! 575: #ifdef AF_UNIX
! 576: if (stream->ops == &php_stream_unix_socket_ops || stream->ops == &php_stream_unixdg_socket_ops) {
! 577: struct sockaddr_un unix_addr;
! 578:
! 579: sock->socket = socket(PF_UNIX, stream->ops == &php_stream_unix_socket_ops ? SOCK_STREAM : SOCK_DGRAM, 0);
! 580:
! 581: if (sock->socket == SOCK_ERR) {
! 582: if (xparam->want_errortext) {
! 583: spprintf(&xparam->outputs.error_text, 0, "Failed to create unix%s socket %s",
! 584: stream->ops == &php_stream_unix_socket_ops ? "" : "datagram",
! 585: strerror(errno));
! 586: }
! 587: return -1;
! 588: }
! 589:
! 590: parse_unix_address(xparam, &unix_addr TSRMLS_CC);
! 591:
! 592: return bind(sock->socket, (struct sockaddr *)&unix_addr, sizeof(unix_addr));
! 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:
! 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:
! 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:
! 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:
! 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: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>