Annotation of embedaddon/php/main/streams/transports.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: transports.c 321634 2012-01-01 13:15:04Z felipe $ */
! 20:
! 21: #include "php.h"
! 22: #include "php_streams_int.h"
! 23: #include "ext/standard/file.h"
! 24:
! 25: static HashTable xport_hash;
! 26:
! 27: PHPAPI HashTable *php_stream_xport_get_hash(void)
! 28: {
! 29: return &xport_hash;
! 30: }
! 31:
! 32: PHPAPI int php_stream_xport_register(char *protocol, php_stream_transport_factory factory TSRMLS_DC)
! 33: {
! 34: return zend_hash_update(&xport_hash, protocol, strlen(protocol) + 1, &factory, sizeof(factory), NULL);
! 35: }
! 36:
! 37: PHPAPI int php_stream_xport_unregister(char *protocol TSRMLS_DC)
! 38: {
! 39: return zend_hash_del(&xport_hash, protocol, strlen(protocol) + 1);
! 40: }
! 41:
! 42: #define ERR_REPORT(out_err, fmt, arg) \
! 43: if (out_err) { spprintf(out_err, 0, fmt, arg); } \
! 44: else { php_error_docref(NULL TSRMLS_CC, E_WARNING, fmt, arg); }
! 45:
! 46: #define ERR_RETURN(out_err, local_err, fmt) \
! 47: if (out_err) { *out_err = local_err; } \
! 48: else { php_error_docref(NULL TSRMLS_CC, E_WARNING, fmt, local_err ? local_err : "Unspecified error"); \
! 49: if (local_err) { efree(local_err); local_err = NULL; } \
! 50: }
! 51:
! 52: PHPAPI php_stream *_php_stream_xport_create(const char *name, long namelen, int options,
! 53: int flags, const char *persistent_id,
! 54: struct timeval *timeout,
! 55: php_stream_context *context,
! 56: char **error_string,
! 57: int *error_code
! 58: STREAMS_DC TSRMLS_DC)
! 59: {
! 60: php_stream *stream = NULL;
! 61: php_stream_transport_factory *factory = NULL;
! 62: const char *p, *protocol = NULL;
! 63: int n = 0, failed = 0;
! 64: char *error_text = NULL;
! 65: struct timeval default_timeout = { 0, 0 };
! 66:
! 67: default_timeout.tv_sec = FG(default_socket_timeout);
! 68:
! 69: if (timeout == NULL) {
! 70: timeout = &default_timeout;
! 71: }
! 72:
! 73: /* check for a cached persistent socket */
! 74: if (persistent_id) {
! 75: switch(php_stream_from_persistent_id(persistent_id, &stream TSRMLS_CC)) {
! 76: case PHP_STREAM_PERSISTENT_SUCCESS:
! 77: /* use a 0 second timeout when checking if the socket
! 78: * has already died */
! 79: if (PHP_STREAM_OPTION_RETURN_OK == php_stream_set_option(stream, PHP_STREAM_OPTION_CHECK_LIVENESS, 0, NULL)) {
! 80: return stream;
! 81: }
! 82: /* dead - kill it */
! 83: php_stream_pclose(stream);
! 84: stream = NULL;
! 85:
! 86: /* fall through */
! 87:
! 88: case PHP_STREAM_PERSISTENT_FAILURE:
! 89: default:
! 90: /* failed; get a new one */
! 91: ;
! 92: }
! 93: }
! 94:
! 95: for (p = name; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++) {
! 96: n++;
! 97: }
! 98:
! 99: if ((*p == ':') && (n > 1) && !strncmp("://", p, 3)) {
! 100: protocol = name;
! 101: name = p + 3;
! 102: namelen -= n + 3;
! 103: } else {
! 104: protocol = "tcp";
! 105: n = 3;
! 106: }
! 107:
! 108: if (protocol) {
! 109: char *tmp = estrndup(protocol, n);
! 110: if (FAILURE == zend_hash_find(&xport_hash, (char*)tmp, n + 1, (void**)&factory)) {
! 111: char wrapper_name[32];
! 112:
! 113: if (n >= sizeof(wrapper_name))
! 114: n = sizeof(wrapper_name) - 1;
! 115: PHP_STRLCPY(wrapper_name, protocol, sizeof(wrapper_name), n);
! 116:
! 117: ERR_REPORT(error_string, "Unable to find the socket transport \"%s\" - did you forget to enable it when you configured PHP?",
! 118: wrapper_name);
! 119:
! 120: efree(tmp);
! 121: return NULL;
! 122: }
! 123: efree(tmp);
! 124: }
! 125:
! 126: if (factory == NULL) {
! 127: /* should never happen */
! 128: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not find a factory !?");
! 129: return NULL;
! 130: }
! 131:
! 132: stream = (*factory)(protocol, n,
! 133: (char*)name, namelen, persistent_id, options, flags, timeout,
! 134: context STREAMS_REL_CC TSRMLS_CC);
! 135:
! 136: if (stream) {
! 137: php_stream_context_set(stream, context);
! 138:
! 139: if ((flags & STREAM_XPORT_SERVER) == 0) {
! 140: /* client */
! 141:
! 142: if (flags & (STREAM_XPORT_CONNECT|STREAM_XPORT_CONNECT_ASYNC)) {
! 143: if (-1 == php_stream_xport_connect(stream, name, namelen,
! 144: flags & STREAM_XPORT_CONNECT_ASYNC ? 1 : 0,
! 145: timeout, &error_text, error_code TSRMLS_CC)) {
! 146:
! 147: ERR_RETURN(error_string, error_text, "connect() failed: %s");
! 148:
! 149: failed = 1;
! 150: }
! 151: }
! 152:
! 153: } else {
! 154: /* server */
! 155: if (flags & STREAM_XPORT_BIND) {
! 156: if (0 != php_stream_xport_bind(stream, name, namelen, &error_text TSRMLS_CC)) {
! 157: ERR_RETURN(error_string, error_text, "bind() failed: %s");
! 158: failed = 1;
! 159: } else if (flags & STREAM_XPORT_LISTEN) {
! 160: zval **zbacklog = NULL;
! 161: int backlog = 32;
! 162:
! 163: if (stream->context && php_stream_context_get_option(stream->context, "socket", "backlog", &zbacklog) == SUCCESS) {
! 164: zval *ztmp = *zbacklog;
! 165:
! 166: convert_to_long_ex(&ztmp);
! 167: backlog = Z_LVAL_P(ztmp);
! 168: if (ztmp != *zbacklog) {
! 169: zval_ptr_dtor(&ztmp);
! 170: }
! 171: }
! 172:
! 173: if (0 != php_stream_xport_listen(stream, backlog, &error_text TSRMLS_CC)) {
! 174: ERR_RETURN(error_string, error_text, "listen() failed: %s");
! 175: failed = 1;
! 176: }
! 177: }
! 178: }
! 179: }
! 180: }
! 181:
! 182: if (failed) {
! 183: /* failure means that they don't get a stream to play with */
! 184: if (persistent_id) {
! 185: php_stream_pclose(stream);
! 186: } else {
! 187: php_stream_close(stream);
! 188: }
! 189: stream = NULL;
! 190: }
! 191:
! 192: return stream;
! 193: }
! 194:
! 195: /* Bind the stream to a local address */
! 196: PHPAPI int php_stream_xport_bind(php_stream *stream,
! 197: const char *name, long namelen,
! 198: char **error_text
! 199: TSRMLS_DC)
! 200: {
! 201: php_stream_xport_param param;
! 202: int ret;
! 203:
! 204: memset(¶m, 0, sizeof(param));
! 205: param.op = STREAM_XPORT_OP_BIND;
! 206: param.inputs.name = (char*)name;
! 207: param.inputs.namelen = namelen;
! 208: param.want_errortext = error_text ? 1 : 0;
! 209:
! 210: ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, ¶m);
! 211:
! 212: if (ret == PHP_STREAM_OPTION_RETURN_OK) {
! 213: if (error_text) {
! 214: *error_text = param.outputs.error_text;
! 215: }
! 216:
! 217: return param.outputs.returncode;
! 218: }
! 219:
! 220: return ret;
! 221: }
! 222:
! 223: /* Connect to a remote address */
! 224: PHPAPI int php_stream_xport_connect(php_stream *stream,
! 225: const char *name, long namelen,
! 226: int asynchronous,
! 227: struct timeval *timeout,
! 228: char **error_text,
! 229: int *error_code
! 230: TSRMLS_DC)
! 231: {
! 232: php_stream_xport_param param;
! 233: int ret;
! 234:
! 235: memset(¶m, 0, sizeof(param));
! 236: param.op = asynchronous ? STREAM_XPORT_OP_CONNECT_ASYNC: STREAM_XPORT_OP_CONNECT;
! 237: param.inputs.name = (char*)name;
! 238: param.inputs.namelen = namelen;
! 239: param.inputs.timeout = timeout;
! 240:
! 241: param.want_errortext = error_text ? 1 : 0;
! 242:
! 243: ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, ¶m);
! 244:
! 245: if (ret == PHP_STREAM_OPTION_RETURN_OK) {
! 246: if (error_text) {
! 247: *error_text = param.outputs.error_text;
! 248: }
! 249: if (error_code) {
! 250: *error_code = param.outputs.error_code;
! 251: }
! 252: return param.outputs.returncode;
! 253: }
! 254:
! 255: return ret;
! 256:
! 257: }
! 258:
! 259: /* Prepare to listen */
! 260: PHPAPI int php_stream_xport_listen(php_stream *stream, int backlog, char **error_text TSRMLS_DC)
! 261: {
! 262: php_stream_xport_param param;
! 263: int ret;
! 264:
! 265: memset(¶m, 0, sizeof(param));
! 266: param.op = STREAM_XPORT_OP_LISTEN;
! 267: param.inputs.backlog = backlog;
! 268: param.want_errortext = error_text ? 1 : 0;
! 269:
! 270: ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, ¶m);
! 271:
! 272: if (ret == PHP_STREAM_OPTION_RETURN_OK) {
! 273: if (error_text) {
! 274: *error_text = param.outputs.error_text;
! 275: }
! 276:
! 277: return param.outputs.returncode;
! 278: }
! 279:
! 280: return ret;
! 281: }
! 282:
! 283: /* Get the next client and their address (as a string) */
! 284: PHPAPI int php_stream_xport_accept(php_stream *stream, php_stream **client,
! 285: char **textaddr, int *textaddrlen,
! 286: void **addr, socklen_t *addrlen,
! 287: struct timeval *timeout,
! 288: char **error_text
! 289: TSRMLS_DC)
! 290: {
! 291: php_stream_xport_param param;
! 292: int ret;
! 293:
! 294: memset(¶m, 0, sizeof(param));
! 295:
! 296: param.op = STREAM_XPORT_OP_ACCEPT;
! 297: param.inputs.timeout = timeout;
! 298: param.want_addr = addr ? 1 : 0;
! 299: param.want_textaddr = textaddr ? 1 : 0;
! 300: param.want_errortext = error_text ? 1 : 0;
! 301:
! 302: ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, ¶m);
! 303:
! 304: if (ret == PHP_STREAM_OPTION_RETURN_OK) {
! 305: *client = param.outputs.client;
! 306: if (addr) {
! 307: *addr = param.outputs.addr;
! 308: *addrlen = param.outputs.addrlen;
! 309: }
! 310: if (textaddr) {
! 311: *textaddr = param.outputs.textaddr;
! 312: *textaddrlen = param.outputs.textaddrlen;
! 313: }
! 314: if (error_text) {
! 315: *error_text = param.outputs.error_text;
! 316: }
! 317:
! 318: return param.outputs.returncode;
! 319: }
! 320: return ret;
! 321: }
! 322:
! 323: PHPAPI int php_stream_xport_get_name(php_stream *stream, int want_peer,
! 324: char **textaddr, int *textaddrlen,
! 325: void **addr, socklen_t *addrlen
! 326: TSRMLS_DC)
! 327: {
! 328: php_stream_xport_param param;
! 329: int ret;
! 330:
! 331: memset(¶m, 0, sizeof(param));
! 332:
! 333: param.op = want_peer ? STREAM_XPORT_OP_GET_PEER_NAME : STREAM_XPORT_OP_GET_NAME;
! 334: param.want_addr = addr ? 1 : 0;
! 335: param.want_textaddr = textaddr ? 1 : 0;
! 336:
! 337: ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, ¶m);
! 338:
! 339: if (ret == PHP_STREAM_OPTION_RETURN_OK) {
! 340: if (addr) {
! 341: *addr = param.outputs.addr;
! 342: *addrlen = param.outputs.addrlen;
! 343: }
! 344: if (textaddr) {
! 345: *textaddr = param.outputs.textaddr;
! 346: *textaddrlen = param.outputs.textaddrlen;
! 347: }
! 348:
! 349: return param.outputs.returncode;
! 350: }
! 351: return ret;
! 352: }
! 353:
! 354: PHPAPI int php_stream_xport_crypto_setup(php_stream *stream, php_stream_xport_crypt_method_t crypto_method, php_stream *session_stream TSRMLS_DC)
! 355: {
! 356: php_stream_xport_crypto_param param;
! 357: int ret;
! 358:
! 359: memset(¶m, 0, sizeof(param));
! 360: param.op = STREAM_XPORT_CRYPTO_OP_SETUP;
! 361: param.inputs.method = crypto_method;
! 362: param.inputs.session = session_stream;
! 363:
! 364: ret = php_stream_set_option(stream, PHP_STREAM_OPTION_CRYPTO_API, 0, ¶m);
! 365:
! 366: if (ret == PHP_STREAM_OPTION_RETURN_OK) {
! 367: return param.outputs.returncode;
! 368: }
! 369:
! 370: php_error_docref("streams.crypto" TSRMLS_CC, E_WARNING, "this stream does not support SSL/crypto");
! 371:
! 372: return ret;
! 373: }
! 374:
! 375: PHPAPI int php_stream_xport_crypto_enable(php_stream *stream, int activate TSRMLS_DC)
! 376: {
! 377: php_stream_xport_crypto_param param;
! 378: int ret;
! 379:
! 380: memset(¶m, 0, sizeof(param));
! 381: param.op = STREAM_XPORT_CRYPTO_OP_ENABLE;
! 382: param.inputs.activate = activate;
! 383:
! 384: ret = php_stream_set_option(stream, PHP_STREAM_OPTION_CRYPTO_API, 0, ¶m);
! 385:
! 386: if (ret == PHP_STREAM_OPTION_RETURN_OK) {
! 387: return param.outputs.returncode;
! 388: }
! 389:
! 390: php_error_docref("streams.crypto" TSRMLS_CC, E_WARNING, "this stream does not support SSL/crypto");
! 391:
! 392: return ret;
! 393: }
! 394:
! 395: /* Similar to recv() system call; read data from the stream, optionally
! 396: * peeking, optionally retrieving OOB data */
! 397: PHPAPI int php_stream_xport_recvfrom(php_stream *stream, char *buf, size_t buflen,
! 398: long flags, void **addr, socklen_t *addrlen, char **textaddr, int *textaddrlen
! 399: TSRMLS_DC)
! 400: {
! 401: php_stream_xport_param param;
! 402: int ret = 0;
! 403: int recvd_len = 0;
! 404: #if 0
! 405: int oob;
! 406:
! 407: if (flags == 0 && addr == NULL) {
! 408: return php_stream_read(stream, buf, buflen);
! 409: }
! 410:
! 411: if (stream->readfilters.head) {
! 412: php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot peek or fetch OOB data from a filtered stream");
! 413: return -1;
! 414: }
! 415:
! 416: oob = (flags & STREAM_OOB) == STREAM_OOB;
! 417:
! 418: if (!oob && addr == NULL) {
! 419: /* must be peeking at regular data; copy content from the buffer
! 420: * first, then adjust the pointer/len before handing off to the
! 421: * stream */
! 422: recvd_len = stream->writepos - stream->readpos;
! 423: if (recvd_len > buflen) {
! 424: recvd_len = buflen;
! 425: }
! 426: if (recvd_len) {
! 427: memcpy(buf, stream->readbuf, recvd_len);
! 428: buf += recvd_len;
! 429: buflen -= recvd_len;
! 430: }
! 431: /* if we filled their buffer, return */
! 432: if (buflen == 0) {
! 433: return recvd_len;
! 434: }
! 435: }
! 436: #endif
! 437:
! 438: /* otherwise, we are going to bypass the buffer */
! 439:
! 440: memset(¶m, 0, sizeof(param));
! 441:
! 442: param.op = STREAM_XPORT_OP_RECV;
! 443: param.want_addr = addr ? 1 : 0;
! 444: param.want_textaddr = textaddr ? 1 : 0;
! 445: param.inputs.buf = buf;
! 446: param.inputs.buflen = buflen;
! 447: param.inputs.flags = flags;
! 448:
! 449: ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, ¶m);
! 450:
! 451: if (ret == PHP_STREAM_OPTION_RETURN_OK) {
! 452: if (addr) {
! 453: *addr = param.outputs.addr;
! 454: *addrlen = param.outputs.addrlen;
! 455: }
! 456: if (textaddr) {
! 457: *textaddr = param.outputs.textaddr;
! 458: *textaddrlen = param.outputs.textaddrlen;
! 459: }
! 460: return recvd_len + param.outputs.returncode;
! 461: }
! 462: return recvd_len ? recvd_len : -1;
! 463: }
! 464:
! 465: /* Similar to send() system call; send data to the stream, optionally
! 466: * sending it as OOB data */
! 467: PHPAPI int php_stream_xport_sendto(php_stream *stream, const char *buf, size_t buflen,
! 468: long flags, void *addr, socklen_t addrlen TSRMLS_DC)
! 469: {
! 470: php_stream_xport_param param;
! 471: int ret = 0;
! 472: int oob;
! 473:
! 474: #if 0
! 475: if (flags == 0 && addr == NULL) {
! 476: return php_stream_write(stream, buf, buflen);
! 477: }
! 478: #endif
! 479:
! 480: oob = (flags & STREAM_OOB) == STREAM_OOB;
! 481:
! 482: if ((oob || addr) && stream->writefilters.head) {
! 483: php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot write OOB data, or data to a targeted address on a filtered stream");
! 484: return -1;
! 485: }
! 486:
! 487: memset(¶m, 0, sizeof(param));
! 488:
! 489: param.op = STREAM_XPORT_OP_SEND;
! 490: param.want_addr = addr ? 1 : 0;
! 491: param.inputs.buf = (char*)buf;
! 492: param.inputs.buflen = buflen;
! 493: param.inputs.flags = flags;
! 494: param.inputs.addr = addr;
! 495: param.inputs.addrlen = addrlen;
! 496:
! 497: ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, ¶m);
! 498:
! 499: if (ret == PHP_STREAM_OPTION_RETURN_OK) {
! 500: return param.outputs.returncode;
! 501: }
! 502: return -1;
! 503: }
! 504:
! 505: /* Similar to shutdown() system call; shut down part of a full-duplex
! 506: * connection */
! 507: PHPAPI int php_stream_xport_shutdown(php_stream *stream, stream_shutdown_t how TSRMLS_DC)
! 508: {
! 509: php_stream_xport_param param;
! 510: int ret = 0;
! 511:
! 512: memset(¶m, 0, sizeof(param));
! 513:
! 514: param.op = STREAM_XPORT_OP_SHUTDOWN;
! 515: param.how = how;
! 516:
! 517: ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, ¶m);
! 518:
! 519: if (ret == PHP_STREAM_OPTION_RETURN_OK) {
! 520: return param.outputs.returncode;
! 521: }
! 522: return -1;
! 523: }
! 524:
! 525: /*
! 526: * Local variables:
! 527: * tab-width: 4
! 528: * c-basic-offset: 4
! 529: * End:
! 530: * vim600: noet sw=4 ts=4 fdm=marker
! 531: * vim<600: noet sw=4 ts=4
! 532: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>