Return to transports.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / php / main / streams |
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: */