summaryrefslogtreecommitdiff
path: root/main/streams/transports.c
diff options
context:
space:
mode:
Diffstat (limited to 'main/streams/transports.c')
-rw-r--r--main/streams/transports.c532
1 files changed, 532 insertions, 0 deletions
diff --git a/main/streams/transports.c b/main/streams/transports.c
new file mode 100644
index 0000000..c24bf97
--- /dev/null
+++ b/main/streams/transports.c
@@ -0,0 +1,532 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Wez Furlong <wez@thebrainroom.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#include "php.h"
+#include "php_streams_int.h"
+#include "ext/standard/file.h"
+
+static HashTable xport_hash;
+
+PHPAPI HashTable *php_stream_xport_get_hash(void)
+{
+ return &xport_hash;
+}
+
+PHPAPI int php_stream_xport_register(char *protocol, php_stream_transport_factory factory TSRMLS_DC)
+{
+ return zend_hash_update(&xport_hash, protocol, strlen(protocol) + 1, &factory, sizeof(factory), NULL);
+}
+
+PHPAPI int php_stream_xport_unregister(char *protocol TSRMLS_DC)
+{
+ return zend_hash_del(&xport_hash, protocol, strlen(protocol) + 1);
+}
+
+#define ERR_REPORT(out_err, fmt, arg) \
+ if (out_err) { spprintf(out_err, 0, fmt, arg); } \
+ else { php_error_docref(NULL TSRMLS_CC, E_WARNING, fmt, arg); }
+
+#define ERR_RETURN(out_err, local_err, fmt) \
+ if (out_err) { *out_err = local_err; } \
+ else { php_error_docref(NULL TSRMLS_CC, E_WARNING, fmt, local_err ? local_err : "Unspecified error"); \
+ if (local_err) { efree(local_err); local_err = NULL; } \
+ }
+
+PHPAPI php_stream *_php_stream_xport_create(const char *name, long namelen, int options,
+ int flags, const char *persistent_id,
+ struct timeval *timeout,
+ php_stream_context *context,
+ char **error_string,
+ int *error_code
+ STREAMS_DC TSRMLS_DC)
+{
+ php_stream *stream = NULL;
+ php_stream_transport_factory *factory = NULL;
+ const char *p, *protocol = NULL;
+ int n = 0, failed = 0;
+ char *error_text = NULL;
+ struct timeval default_timeout = { 0, 0 };
+
+ default_timeout.tv_sec = FG(default_socket_timeout);
+
+ if (timeout == NULL) {
+ timeout = &default_timeout;
+ }
+
+ /* check for a cached persistent socket */
+ if (persistent_id) {
+ switch(php_stream_from_persistent_id(persistent_id, &stream TSRMLS_CC)) {
+ case PHP_STREAM_PERSISTENT_SUCCESS:
+ /* use a 0 second timeout when checking if the socket
+ * has already died */
+ if (PHP_STREAM_OPTION_RETURN_OK == php_stream_set_option(stream, PHP_STREAM_OPTION_CHECK_LIVENESS, 0, NULL)) {
+ return stream;
+ }
+ /* dead - kill it */
+ php_stream_pclose(stream);
+ stream = NULL;
+
+ /* fall through */
+
+ case PHP_STREAM_PERSISTENT_FAILURE:
+ default:
+ /* failed; get a new one */
+ ;
+ }
+ }
+
+ for (p = name; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++) {
+ n++;
+ }
+
+ if ((*p == ':') && (n > 1) && !strncmp("://", p, 3)) {
+ protocol = name;
+ name = p + 3;
+ namelen -= n + 3;
+ } else {
+ protocol = "tcp";
+ n = 3;
+ }
+
+ if (protocol) {
+ char *tmp = estrndup(protocol, n);
+ if (FAILURE == zend_hash_find(&xport_hash, (char*)tmp, n + 1, (void**)&factory)) {
+ char wrapper_name[32];
+
+ if (n >= sizeof(wrapper_name))
+ n = sizeof(wrapper_name) - 1;
+ PHP_STRLCPY(wrapper_name, protocol, sizeof(wrapper_name), n);
+
+ ERR_REPORT(error_string, "Unable to find the socket transport \"%s\" - did you forget to enable it when you configured PHP?",
+ wrapper_name);
+
+ efree(tmp);
+ return NULL;
+ }
+ efree(tmp);
+ }
+
+ if (factory == NULL) {
+ /* should never happen */
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not find a factory !?");
+ return NULL;
+ }
+
+ stream = (*factory)(protocol, n,
+ (char*)name, namelen, persistent_id, options, flags, timeout,
+ context STREAMS_REL_CC TSRMLS_CC);
+
+ if (stream) {
+ php_stream_context_set(stream, context);
+
+ if ((flags & STREAM_XPORT_SERVER) == 0) {
+ /* client */
+
+ if (flags & (STREAM_XPORT_CONNECT|STREAM_XPORT_CONNECT_ASYNC)) {
+ if (-1 == php_stream_xport_connect(stream, name, namelen,
+ flags & STREAM_XPORT_CONNECT_ASYNC ? 1 : 0,
+ timeout, &error_text, error_code TSRMLS_CC)) {
+
+ ERR_RETURN(error_string, error_text, "connect() failed: %s");
+
+ failed = 1;
+ }
+ }
+
+ } else {
+ /* server */
+ if (flags & STREAM_XPORT_BIND) {
+ if (0 != php_stream_xport_bind(stream, name, namelen, &error_text TSRMLS_CC)) {
+ ERR_RETURN(error_string, error_text, "bind() failed: %s");
+ failed = 1;
+ } else if (flags & STREAM_XPORT_LISTEN) {
+ zval **zbacklog = NULL;
+ int backlog = 32;
+
+ if (stream->context && php_stream_context_get_option(stream->context, "socket", "backlog", &zbacklog) == SUCCESS) {
+ zval *ztmp = *zbacklog;
+
+ convert_to_long_ex(&ztmp);
+ backlog = Z_LVAL_P(ztmp);
+ if (ztmp != *zbacklog) {
+ zval_ptr_dtor(&ztmp);
+ }
+ }
+
+ if (0 != php_stream_xport_listen(stream, backlog, &error_text TSRMLS_CC)) {
+ ERR_RETURN(error_string, error_text, "listen() failed: %s");
+ failed = 1;
+ }
+ }
+ }
+ }
+ }
+
+ if (failed) {
+ /* failure means that they don't get a stream to play with */
+ if (persistent_id) {
+ php_stream_pclose(stream);
+ } else {
+ php_stream_close(stream);
+ }
+ stream = NULL;
+ }
+
+ return stream;
+}
+
+/* Bind the stream to a local address */
+PHPAPI int php_stream_xport_bind(php_stream *stream,
+ const char *name, long namelen,
+ char **error_text
+ TSRMLS_DC)
+{
+ php_stream_xport_param param;
+ int ret;
+
+ memset(&param, 0, sizeof(param));
+ param.op = STREAM_XPORT_OP_BIND;
+ param.inputs.name = (char*)name;
+ param.inputs.namelen = namelen;
+ param.want_errortext = error_text ? 1 : 0;
+
+ ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, &param);
+
+ if (ret == PHP_STREAM_OPTION_RETURN_OK) {
+ if (error_text) {
+ *error_text = param.outputs.error_text;
+ }
+
+ return param.outputs.returncode;
+ }
+
+ return ret;
+}
+
+/* Connect to a remote address */
+PHPAPI int php_stream_xport_connect(php_stream *stream,
+ const char *name, long namelen,
+ int asynchronous,
+ struct timeval *timeout,
+ char **error_text,
+ int *error_code
+ TSRMLS_DC)
+{
+ php_stream_xport_param param;
+ int ret;
+
+ memset(&param, 0, sizeof(param));
+ param.op = asynchronous ? STREAM_XPORT_OP_CONNECT_ASYNC: STREAM_XPORT_OP_CONNECT;
+ param.inputs.name = (char*)name;
+ param.inputs.namelen = namelen;
+ param.inputs.timeout = timeout;
+
+ param.want_errortext = error_text ? 1 : 0;
+
+ ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, &param);
+
+ if (ret == PHP_STREAM_OPTION_RETURN_OK) {
+ if (error_text) {
+ *error_text = param.outputs.error_text;
+ }
+ if (error_code) {
+ *error_code = param.outputs.error_code;
+ }
+ return param.outputs.returncode;
+ }
+
+ return ret;
+
+}
+
+/* Prepare to listen */
+PHPAPI int php_stream_xport_listen(php_stream *stream, int backlog, char **error_text TSRMLS_DC)
+{
+ php_stream_xport_param param;
+ int ret;
+
+ memset(&param, 0, sizeof(param));
+ param.op = STREAM_XPORT_OP_LISTEN;
+ param.inputs.backlog = backlog;
+ param.want_errortext = error_text ? 1 : 0;
+
+ ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, &param);
+
+ if (ret == PHP_STREAM_OPTION_RETURN_OK) {
+ if (error_text) {
+ *error_text = param.outputs.error_text;
+ }
+
+ return param.outputs.returncode;
+ }
+
+ return ret;
+}
+
+/* Get the next client and their address (as a string) */
+PHPAPI int php_stream_xport_accept(php_stream *stream, php_stream **client,
+ char **textaddr, int *textaddrlen,
+ void **addr, socklen_t *addrlen,
+ struct timeval *timeout,
+ char **error_text
+ TSRMLS_DC)
+{
+ php_stream_xport_param param;
+ int ret;
+
+ memset(&param, 0, sizeof(param));
+
+ param.op = STREAM_XPORT_OP_ACCEPT;
+ param.inputs.timeout = timeout;
+ param.want_addr = addr ? 1 : 0;
+ param.want_textaddr = textaddr ? 1 : 0;
+ param.want_errortext = error_text ? 1 : 0;
+
+ ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, &param);
+
+ if (ret == PHP_STREAM_OPTION_RETURN_OK) {
+ *client = param.outputs.client;
+ if (addr) {
+ *addr = param.outputs.addr;
+ *addrlen = param.outputs.addrlen;
+ }
+ if (textaddr) {
+ *textaddr = param.outputs.textaddr;
+ *textaddrlen = param.outputs.textaddrlen;
+ }
+ if (error_text) {
+ *error_text = param.outputs.error_text;
+ }
+
+ return param.outputs.returncode;
+ }
+ return ret;
+}
+
+PHPAPI int php_stream_xport_get_name(php_stream *stream, int want_peer,
+ char **textaddr, int *textaddrlen,
+ void **addr, socklen_t *addrlen
+ TSRMLS_DC)
+{
+ php_stream_xport_param param;
+ int ret;
+
+ memset(&param, 0, sizeof(param));
+
+ param.op = want_peer ? STREAM_XPORT_OP_GET_PEER_NAME : STREAM_XPORT_OP_GET_NAME;
+ param.want_addr = addr ? 1 : 0;
+ param.want_textaddr = textaddr ? 1 : 0;
+
+ ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, &param);
+
+ if (ret == PHP_STREAM_OPTION_RETURN_OK) {
+ if (addr) {
+ *addr = param.outputs.addr;
+ *addrlen = param.outputs.addrlen;
+ }
+ if (textaddr) {
+ *textaddr = param.outputs.textaddr;
+ *textaddrlen = param.outputs.textaddrlen;
+ }
+
+ return param.outputs.returncode;
+ }
+ return ret;
+}
+
+PHPAPI int php_stream_xport_crypto_setup(php_stream *stream, php_stream_xport_crypt_method_t crypto_method, php_stream *session_stream TSRMLS_DC)
+{
+ php_stream_xport_crypto_param param;
+ int ret;
+
+ memset(&param, 0, sizeof(param));
+ param.op = STREAM_XPORT_CRYPTO_OP_SETUP;
+ param.inputs.method = crypto_method;
+ param.inputs.session = session_stream;
+
+ ret = php_stream_set_option(stream, PHP_STREAM_OPTION_CRYPTO_API, 0, &param);
+
+ if (ret == PHP_STREAM_OPTION_RETURN_OK) {
+ return param.outputs.returncode;
+ }
+
+ php_error_docref("streams.crypto" TSRMLS_CC, E_WARNING, "this stream does not support SSL/crypto");
+
+ return ret;
+}
+
+PHPAPI int php_stream_xport_crypto_enable(php_stream *stream, int activate TSRMLS_DC)
+{
+ php_stream_xport_crypto_param param;
+ int ret;
+
+ memset(&param, 0, sizeof(param));
+ param.op = STREAM_XPORT_CRYPTO_OP_ENABLE;
+ param.inputs.activate = activate;
+
+ ret = php_stream_set_option(stream, PHP_STREAM_OPTION_CRYPTO_API, 0, &param);
+
+ if (ret == PHP_STREAM_OPTION_RETURN_OK) {
+ return param.outputs.returncode;
+ }
+
+ php_error_docref("streams.crypto" TSRMLS_CC, E_WARNING, "this stream does not support SSL/crypto");
+
+ return ret;
+}
+
+/* Similar to recv() system call; read data from the stream, optionally
+ * peeking, optionally retrieving OOB data */
+PHPAPI int php_stream_xport_recvfrom(php_stream *stream, char *buf, size_t buflen,
+ long flags, void **addr, socklen_t *addrlen, char **textaddr, int *textaddrlen
+ TSRMLS_DC)
+{
+ php_stream_xport_param param;
+ int ret = 0;
+ int recvd_len = 0;
+#if 0
+ int oob;
+
+ if (flags == 0 && addr == NULL) {
+ return php_stream_read(stream, buf, buflen);
+ }
+
+ if (stream->readfilters.head) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot peek or fetch OOB data from a filtered stream");
+ return -1;
+ }
+
+ oob = (flags & STREAM_OOB) == STREAM_OOB;
+
+ if (!oob && addr == NULL) {
+ /* must be peeking at regular data; copy content from the buffer
+ * first, then adjust the pointer/len before handing off to the
+ * stream */
+ recvd_len = stream->writepos - stream->readpos;
+ if (recvd_len > buflen) {
+ recvd_len = buflen;
+ }
+ if (recvd_len) {
+ memcpy(buf, stream->readbuf, recvd_len);
+ buf += recvd_len;
+ buflen -= recvd_len;
+ }
+ /* if we filled their buffer, return */
+ if (buflen == 0) {
+ return recvd_len;
+ }
+ }
+#endif
+
+ /* otherwise, we are going to bypass the buffer */
+
+ memset(&param, 0, sizeof(param));
+
+ param.op = STREAM_XPORT_OP_RECV;
+ param.want_addr = addr ? 1 : 0;
+ param.want_textaddr = textaddr ? 1 : 0;
+ param.inputs.buf = buf;
+ param.inputs.buflen = buflen;
+ param.inputs.flags = flags;
+
+ ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, &param);
+
+ if (ret == PHP_STREAM_OPTION_RETURN_OK) {
+ if (addr) {
+ *addr = param.outputs.addr;
+ *addrlen = param.outputs.addrlen;
+ }
+ if (textaddr) {
+ *textaddr = param.outputs.textaddr;
+ *textaddrlen = param.outputs.textaddrlen;
+ }
+ return recvd_len + param.outputs.returncode;
+ }
+ return recvd_len ? recvd_len : -1;
+}
+
+/* Similar to send() system call; send data to the stream, optionally
+ * sending it as OOB data */
+PHPAPI int php_stream_xport_sendto(php_stream *stream, const char *buf, size_t buflen,
+ long flags, void *addr, socklen_t addrlen TSRMLS_DC)
+{
+ php_stream_xport_param param;
+ int ret = 0;
+ int oob;
+
+#if 0
+ if (flags == 0 && addr == NULL) {
+ return php_stream_write(stream, buf, buflen);
+ }
+#endif
+
+ oob = (flags & STREAM_OOB) == STREAM_OOB;
+
+ if ((oob || addr) && stream->writefilters.head) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot write OOB data, or data to a targeted address on a filtered stream");
+ return -1;
+ }
+
+ memset(&param, 0, sizeof(param));
+
+ param.op = STREAM_XPORT_OP_SEND;
+ param.want_addr = addr ? 1 : 0;
+ param.inputs.buf = (char*)buf;
+ param.inputs.buflen = buflen;
+ param.inputs.flags = flags;
+ param.inputs.addr = addr;
+ param.inputs.addrlen = addrlen;
+
+ ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, &param);
+
+ if (ret == PHP_STREAM_OPTION_RETURN_OK) {
+ return param.outputs.returncode;
+ }
+ return -1;
+}
+
+/* Similar to shutdown() system call; shut down part of a full-duplex
+ * connection */
+PHPAPI int php_stream_xport_shutdown(php_stream *stream, stream_shutdown_t how TSRMLS_DC)
+{
+ php_stream_xport_param param;
+ int ret = 0;
+
+ memset(&param, 0, sizeof(param));
+
+ param.op = STREAM_XPORT_OP_SHUTDOWN;
+ param.how = how;
+
+ ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, &param);
+
+ if (ret == PHP_STREAM_OPTION_RETURN_OK) {
+ return param.outputs.returncode;
+ }
+ return -1;
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */