File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / php / main / streams / transports.c
Revision 1.1.1.3 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Mon Jul 22 01:32:11 2013 UTC (11 years, 2 months ago) by misho
Branches: php, MAIN
CVS tags: v5_4_29p0, v5_4_20p0, v5_4_20, v5_4_17, HEAD
5.4.17

    1: /*
    2:   +----------------------------------------------------------------------+
    3:   | PHP Version 5                                                        |
    4:   +----------------------------------------------------------------------+
    5:   | Copyright (c) 1997-2013 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,v 1.1.1.3 2013/07/22 01:32:11 misho Exp $ */
   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(&param, 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, &param);
  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(&param, 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, &param);
  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(&param, 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, &param);
  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(&param, 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, &param);
  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(&param, 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, &param);
  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(&param, 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, &param);
  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(&param, 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, &param);
  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(&param, 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, &param);
  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(&param, 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, &param);
  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(&param, 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, &param);
  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>