summaryrefslogtreecommitdiff
path: root/main/streams
diff options
context:
space:
mode:
authorWez Furlong <wez@php.net>2003-02-27 17:43:38 +0000
committerWez Furlong <wez@php.net>2003-02-27 17:43:38 +0000
commitfd61f69077f6156ca71dde60ecfd9ed9765a02db (patch)
tree7285ad393cdb5a85107a3329d1ab2bcafe89f051 /main/streams
parent560e33968de93250377606782949f5004affca83 (diff)
downloadphp-git-fd61f69077f6156ca71dde60ecfd9ed9765a02db.tar.gz
Another big commit (tm).
Main Changes: - Implement a socket transport layer for use by all code that needs to open some kind of "special" socket for network or IPC. - Extensions can register (and override) transports. - Implement ftruncate() on streams via the ioctl-alike option interface. - Implement mmap() on streams via the ioctl-alike option interface. - Implement generic crypto API via the ioctl-alike option interface. (currently only supports OpenSSL, but could support other SSL toolkits, and other crypto transport protocols). Impact: - tcp sockets can be overloaded by the openssl capable sockets at runtime, removing the link-time requirement for ssl:// and https:// sockets and streams. - checking stream types using PHP_STREAM_IS_SOCKET is deprecated, since there are now a range of possible socket-type streams. Working towards: - socket servers using the new transport layer - mmap support under win32 - Cleaner code. # I will be updating the win32 build to add the new files shortly # after this commit.
Diffstat (limited to 'main/streams')
-rw-r--r--main/streams/cast.c22
-rw-r--r--main/streams/mmap.c52
-rw-r--r--main/streams/php_stream_mmap.h82
-rw-r--r--main/streams/php_stream_transport.h153
-rw-r--r--main/streams/php_streams_int.h2
-rw-r--r--main/streams/plain_wrapper.c90
-rwxr-xr-xmain/streams/streams.c200
-rw-r--r--main/streams/transports.c341
-rw-r--r--main/streams/xp_socket.c502
9 files changed, 1290 insertions, 154 deletions
diff --git a/main/streams/cast.c b/main/streams/cast.c
index aa6e6d12b2..1ebc6af877 100644
--- a/main/streams/cast.c
+++ b/main/streams/cast.c
@@ -296,28 +296,6 @@ PHPAPI FILE * _php_stream_open_wrapper_as_file(char *path, char *mode, int optio
if (stream == NULL)
return NULL;
-
-#ifdef PHP_WIN32
- /* Avoid possible strange problems when working with socket based streams */
- if ((options & STREAM_OPEN_FOR_INCLUDE) && php_stream_is(stream, PHP_STREAM_IS_SOCKET)) {
- char buf[CHUNK_SIZE];
-
- fp = php_open_temporary_file(NULL, "php", NULL TSRMLS_CC);
- if (fp) {
- while (!php_stream_eof(stream)) {
- size_t didread = php_stream_read(stream, buf, sizeof(buf));
- if (didread > 0) {
- fwrite(buf, 1, didread, fp);
- } else {
- break;
- }
- }
- php_stream_close(stream);
- rewind(fp);
- return fp;
- }
- }
-#endif
if (php_stream_cast(stream, PHP_STREAM_AS_STDIO|PHP_STREAM_CAST_TRY_HARD|PHP_STREAM_CAST_RELEASE,
(void**)&fp, REPORT_ERRORS) == FAILURE)
diff --git a/main/streams/mmap.c b/main/streams/mmap.c
new file mode 100644
index 0000000000..791b96436f
--- /dev/null
+++ b/main/streams/mmap.c
@@ -0,0 +1,52 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 4 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2003 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 2.02 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available at through the world-wide-web at |
+ | http://www.php.net/license/2_02.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$ */
+
+/* Memory Mapping interface for streams */
+#include "php.h"
+#include "php_streams_int.h"
+
+PHPAPI char *_php_stream_mmap_range(php_stream *stream, size_t offset, size_t length, php_stream_mmap_operation_t mode, size_t *mapped_len TSRMLS_DC)
+{
+ php_stream_mmap_range range = { offset, length, mode, NULL };
+
+ /* TODO: Enforce system policy and limits for mmap sizes ? */
+
+ if (PHP_STREAM_OPTION_RETURN_OK == php_stream_set_option(stream, PHP_STREAM_OPTION_MMAP_API, PHP_STREAM_MMAP_MAP_RANGE, &range)) {
+ if (mapped_len) {
+ *mapped_len = range.length;
+ }
+ return range.mapped;
+ }
+ return NULL;
+}
+
+PHPAPI int _php_stream_mmap_unmap(php_stream *stream TSRMLS_DC)
+{
+ return php_stream_set_option(stream, PHP_STREAM_OPTION_MMAP_API, PHP_STREAM_MMAP_UNMAP, NULL) == PHP_STREAM_OPTION_RETURN_OK ? 1 : 0;
+}
+
+/*
+ * 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
+ */
diff --git a/main/streams/php_stream_mmap.h b/main/streams/php_stream_mmap.h
new file mode 100644
index 0000000000..ccfc767246
--- /dev/null
+++ b/main/streams/php_stream_mmap.h
@@ -0,0 +1,82 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 4 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2003 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 2.02 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available at through the world-wide-web at |
+ | http://www.php.net/license/2_02.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$ */
+
+/* Memory Mapping interface for streams.
+ * The intention is to provide a uniform interface over the most common
+ * operations that are used within PHP itself, rather than a complete
+ * API for all memory mapping needs.
+ *
+ * ATM, we support only mmap(), but win32 memory mapping support will
+ * follow soon.
+ * */
+
+typedef enum {
+ /* Does the stream support mmap ? */
+ PHP_STREAM_MMAP_SUPPORTED,
+ /* Request a range and offset to be mapped;
+ * while mapped, you MUST NOT use any read/write functions
+ * on the stream (win9x compatibility) */
+ PHP_STREAM_MMAP_MAP_RANGE,
+ /* Unmap the last range that was mapped for the stream */
+ PHP_STREAM_MMAP_UNMAP
+} php_stream_mmap_operation_t;
+
+typedef enum {
+ PHP_STREAM_MAP_MODE_READONLY,
+ PHP_STREAM_MAP_MODE_READWRITE,
+ PHP_STREAM_MAP_MODE_SHARED_READONLY,
+ PHP_STREAM_MAP_MODE_SHARED_READWRITE
+} php_stream_mmap_access_t;
+
+typedef struct {
+ /* requested offset and length.
+ * If length is 0, the whole file is mapped */
+ size_t offset;
+ size_t length;
+
+ php_stream_mmap_access_t mode;
+
+ /* returned mapped address */
+ char *mapped;
+
+} php_stream_mmap_range;
+
+#define php_stream_mmap_supported(stream) (_php_stream_set_option((stream), PHP_STREAM_OPTION_MMAP_API, PHP_STREAM_MMAP_SUPPORTED, NULL TSRMLS_CC) == 0 ? 1 : 0)
+
+/* Returns 1 if the stream in its current state can be memory mapped,
+ * 0 otherwise */
+#define php_stream_mmap_possible(stream) (!php_stream_is_filtered((stream)) && php_stream_mmap_supported((stream)))
+
+PHPAPI char *_php_stream_mmap_range(php_stream *stream, size_t offset, size_t length, php_stream_mmap_operation_t mode, size_t *mapped_len TSRMLS_DC);
+#define php_stream_mmap_range(stream, offset, length, mode, mapped_len) _php_stream_mmap_range((stream), (offset), (length), (mode), (mapped_len) TSRMLS_CC)
+
+/* un-maps the last mapped range */
+PHPAPI int _php_stream_mmap_unmap(php_stream *stream TSRMLS_DC);
+#define php_stream_mmap_unmap(stream) _php_stream_mmap_unmap((stream) TSRMLS_CC)
+
+
+/*
+ * 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
+ */
diff --git a/main/streams/php_stream_transport.h b/main/streams/php_stream_transport.h
new file mode 100644
index 0000000000..89642b5652
--- /dev/null
+++ b/main/streams/php_stream_transport.h
@@ -0,0 +1,153 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 4 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2003 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 2.02 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available at through the world-wide-web at |
+ | http://www.php.net/license/2_02.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$ */
+
+typedef php_stream *(php_stream_transport_factory_func)(const char *proto, long protolen,
+ char *resourcename, long resourcenamelen,
+ const char *persistent_id, int options, int flags,
+ struct timeval *timeout,
+ php_stream_context *context STREAMS_DC TSRMLS_DC);
+typedef php_stream_transport_factory_func *php_stream_transport_factory;
+
+PHPAPI int php_stream_xport_register(char *protocol, php_stream_transport_factory factory TSRMLS_DC);
+PHPAPI int php_stream_xport_unregister(char *protocol TSRMLS_DC);
+
+#define STREAM_XPORT_CLIENT 0
+#define STREAM_XPORT_SERVER 1
+
+#define STREAM_XPORT_CONNECT 2
+#define STREAM_XPORT_BIND 4
+#define STREAM_XPORT_LISTEN 8
+#define STREAM_XPORT_CONNECT_ASYNC 16
+
+/* Open a client or server socket connection */
+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);
+
+#define php_stream_xport_create(name, namelen, options, flags, persistent_id, timeout, context, estr, ecode) \
+ _php_stream_xport_create(name, namelen, options, flags, persistent_id, timeout, context, estr, ecode STREAMS_CC TSRMLS_CC)
+
+/* 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);
+
+/* 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);
+
+/* Prepare to listen */
+PHPAPI int php_stream_xport_listen(php_stream *stream,
+ int backlog,
+ char **error_text
+ TSRMLS_DC);
+
+/* Get the next client and their address as a string, or the underlying address
+ * structure. You must efree either of these if you request them */
+PHPAPI int php_stream_xport_accept(php_stream *stream, php_stream **client,
+ char **textaddr, long *textaddrlen,
+ void **addr, size_t *addrlen,
+ struct timeval *timeout,
+ char **error_text
+ TSRMLS_DC);
+
+/* Structure definition for the set_option interface that the above functions wrap */
+
+typedef struct _php_stream_xport_param {
+ enum {
+ STREAM_XPORT_OP_BIND, STREAM_XPORT_OP_CONNECT,
+ STREAM_XPORT_OP_LISTEN, STREAM_XPORT_OP_ACCEPT,
+ STREAM_XPORT_OP_CONNECT_ASYNC
+ } op;
+ int want_addr:1;
+ int want_textaddr:1;
+ int want_errortext:1;
+
+ struct {
+ char *name;
+ long namelen;
+ int backlog;
+ struct timeval *timeout;
+ } inputs;
+ struct {
+ php_stream *client;
+ int returncode;
+ void *addr;
+ size_t addrlen;
+ char *textaddr;
+ long textaddrlen;
+
+ char *error_text;
+ int error_code;
+ } outputs;
+} php_stream_xport_param;
+
+
+/* These functions provide crypto support on the underlying transport */
+typedef enum {
+ STREAM_CRYPTO_METHOD_SSLv2_CLIENT,
+ STREAM_CRYPTO_METHOD_SSLv3_CLIENT,
+ STREAM_CRYPTO_METHOD_SSLv23_CLIENT,
+ STREAM_CRYPTO_METHOD_TLS_CLIENT,
+ STREAM_CRYPTO_METHOD_SSLv2_SERVER,
+ STREAM_CRYPTO_METHOD_SSLv3_SERVER,
+ STREAM_CRYPTO_METHOD_SSLv23_SERVER,
+ STREAM_CRYPTO_METHOD_TLS_SERVER
+} php_stream_xport_crypt_method_t;
+
+PHPAPI int php_stream_xport_crypto_setup(php_stream *stream, php_stream_xport_crypt_method_t crypto_method, php_stream *session_stream TSRMLS_DC);
+PHPAPI int php_stream_xport_crypto_enable(php_stream *stream, int activate TSRMLS_DC);
+
+typedef struct _php_stream_xport_crypto_param {
+ enum {
+ STREAM_XPORT_CRYPTO_OP_SETUP,
+ STREAM_XPORT_CRYPTO_OP_ENABLE
+ } op;
+ struct {
+ int activate;
+ php_stream_xport_crypt_method_t method;
+ php_stream *session;
+ } inputs;
+ struct {
+ int returncode;
+ } outputs;
+} php_stream_xport_crypto_param;
+
+PHPAPI HashTable *php_stream_xport_get_hash(void);
+PHPAPI php_stream_transport_factory_func php_stream_generic_socket_factory;
+
+/*
+ * 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
+ */
diff --git a/main/streams/php_streams_int.h b/main/streams/php_streams_int.h
index 05ad1a0dc8..ddf569399d 100644
--- a/main/streams/php_streams_int.h
+++ b/main/streams/php_streams_int.h
@@ -56,4 +56,6 @@ extern php_stream_wrapper php_plain_files_wrapper;
#define S_ISREG(mode) (((mode)&S_IFMT) == S_IFREG)
#endif
+void php_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper TSRMLS_DC);
+void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper, const char *path, const char *caption TSRMLS_DC);
diff --git a/main/streams/plain_wrapper.c b/main/streams/plain_wrapper.c
index f73c2642aa..f121b8ce15 100644
--- a/main/streams/plain_wrapper.c
+++ b/main/streams/plain_wrapper.c
@@ -32,6 +32,9 @@
#if HAVE_SYS_FILE_H
#include <sys/file.h>
#endif
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
#include "php_streams_int.h"
@@ -135,12 +138,17 @@ typedef struct {
int fd; /* underlying file descriptor */
int is_process_pipe; /* use pclose instead of fclose */
int is_pipe; /* don't try and seek */
- int lock_flag; /* stores the lock state */
+ int lock_flag; /* stores the lock state */
char *temp_file_name; /* if non-null, this is the path to a temporary file that
* is to be deleted when the stream is closed */
#if HAVE_FLUSHIO
char last_op;
#endif
+
+#if HAVE_MMAP
+ char *last_mapped_addr;
+ size_t last_mapped_len;
+#endif
} php_stdio_stream_data;
PHPAPI php_stream *_php_stream_fopen_temporary_file(const char *dir, const char *pfx, char **opened_path STREAMS_DC TSRMLS_DC)
@@ -348,6 +356,13 @@ static int php_stdiop_close(php_stream *stream, int close_handle TSRMLS_DC)
php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract;
assert(data != NULL);
+
+#if HAVE_MMAP
+ if (data->last_mapped_addr) {
+ munmap(data->last_mapped_addr, data->last_mapped_len);
+ data->last_mapped_addr = NULL;
+ }
+#endif
if (close_handle) {
if (data->lock_flag != LOCK_UN) {
@@ -572,8 +587,76 @@ static int php_stdiop_set_option(php_stream *stream, int option, int value, void
}
break;
+ case PHP_STREAM_OPTION_MMAP_API:
+#if HAVE_MMAP
+ {
+ php_stream_mmap_range *range = (php_stream_mmap_range*)ptrparam;
+ struct stat sbuf;
+ int prot, flags;
+
+ switch (value) {
+ case PHP_STREAM_MMAP_SUPPORTED:
+ return fd == -1 ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK;
+
+ case PHP_STREAM_MMAP_MAP_RANGE:
+ fstat(fd, &sbuf);
+ if (range->length == 0 || range->length > sbuf.st_size) {
+ range->length = sbuf.st_size;
+ }
+ switch (range->mode) {
+ case PHP_STREAM_MAP_MODE_READONLY:
+ prot = PROT_READ;
+ flags = MAP_PRIVATE;
+ break;
+ case PHP_STREAM_MAP_MODE_READWRITE:
+ prot = PROT_READ | PROT_WRITE;
+ flags = MAP_PRIVATE;
+ break;
+ case PHP_STREAM_MAP_MODE_SHARED_READONLY:
+ prot = PROT_READ;
+ flags = MAP_SHARED;
+ break;
+ case PHP_STREAM_MAP_MODE_SHARED_READWRITE:
+ prot = PROT_READ | PROT_WRITE;
+ flags = MAP_SHARED;
+ break;
+ default:
+ return PHP_STREAM_OPTION_RETURN_ERR;
+ }
+ range->mapped = (char*)mmap(NULL, range->length, prot, flags, fd, range->offset);
+ if (range->mapped == (char*)MAP_FAILED) {
+ range->mapped = NULL;
+ return PHP_STREAM_OPTION_RETURN_ERR;
+ }
+ /* remember the mapping */
+ data->last_mapped_addr = range->mapped;
+ data->last_mapped_len = range->length;
+ return PHP_STREAM_OPTION_RETURN_OK;
+
+ case PHP_STREAM_MMAP_UNMAP:
+ if (data->last_mapped_addr) {
+ munmap(data->last_mapped_addr, data->last_mapped_len);
+ data->last_mapped_addr = NULL;
+
+ return PHP_STREAM_OPTION_RETURN_OK;
+ }
+ return PHP_STREAM_OPTION_RETURN_ERR;
+ }
+ }
+#endif
+ return PHP_STREAM_OPTION_RETURN_NOTIMPL;
+
+ case PHP_STREAM_OPTION_TRUNCATE_API:
+ switch (value) {
+ case PHP_STREAM_TRUNCATE_SUPPORTED:
+ return fd == -1 ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK;
+
+ case PHP_STREAM_TRUNCATE_SET_SIZE:
+ return ftruncate(fd, *(size_t*)ptrparam) == 0 ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR;
+ }
+
default:
- return -1;
+ return PHP_STREAM_OPTION_RETURN_NOTIMPL;
}
}
@@ -588,6 +671,7 @@ PHPAPI php_stream_ops php_stream_stdio_ops = {
};
/* }}} */
+/* {{{ plain files opendir/readdir implementation */
static size_t php_plain_files_dirstream_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
{
DIR *dir = (DIR*)stream->abstract;
@@ -658,7 +742,7 @@ static php_stream *php_plain_files_dir_opener(php_stream_wrapper *wrapper, char
return stream;
}
-
+/* }}} */
static php_stream *php_plain_files_stream_opener(php_stream_wrapper *wrapper, char *path, char *mode,
diff --git a/main/streams/streams.c b/main/streams/streams.c
index e798684d18..b46dcc97aa 100755
--- a/main/streams/streams.c
+++ b/main/streams/streams.c
@@ -29,9 +29,6 @@
#include "ext/standard/file.h"
#include "ext/standard/basic_functions.h" /* for BG(mmap_file) (not strictly required) */
#include "ext/standard/php_string.h" /* for php_memnstr, used by php_stream_get_record() */
-#ifdef HAVE_SYS_MMAN_H
-#include <sys/mman.h>
-#endif
#include <stddef.h>
#include <fcntl.h>
#include "php_streams_int.h"
@@ -103,7 +100,7 @@ PHPAPI int php_stream_from_persistent_id(const char *persistent_id, php_stream *
/* }}} */
/* {{{ wrapper error reporting */
-static void display_wrapper_errors(php_stream_wrapper *wrapper, const char *path, const char *caption TSRMLS_DC)
+void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper, const char *path, const char *caption TSRMLS_DC)
{
char *tmp = estrdup(path);
char *msg;
@@ -152,7 +149,7 @@ static void display_wrapper_errors(php_stream_wrapper *wrapper, const char *path
efree(msg);
}
-static void tidy_wrapper_error_log(php_stream_wrapper *wrapper TSRMLS_DC)
+void php_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper TSRMLS_DC)
{
if (wrapper) {
/* tidy up the error stack */
@@ -1061,52 +1058,37 @@ PHPAPI int _php_stream_set_option(php_stream *stream, int option, int value, voi
return ret;
}
+PHPAPI int _php_stream_truncate_set_size(php_stream *stream, size_t newsize TSRMLS_DC)
+{
+ return php_stream_set_option(stream, PHP_STREAM_OPTION_TRUNCATE_API, PHP_STREAM_TRUNCATE_SET_SIZE, &newsize);
+}
+
PHPAPI size_t _php_stream_passthru(php_stream * stream STREAMS_DC TSRMLS_DC)
{
size_t bcount = 0;
- int ready = 0;
char buf[8192];
-#ifdef HAVE_MMAP
- int fd;
-#endif
+ int b;
-#ifdef HAVE_MMAP
- if (!php_stream_is(stream, PHP_STREAM_IS_SOCKET)
- && !php_stream_is_filtered(stream)
- && php_stream_tell(stream) == 0
- && SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD, (void*)&fd, 0))
- {
- struct stat sbuf;
- off_t off;
- void *p;
- size_t len;
-
- fstat(fd, &sbuf);
-
- if (sbuf.st_size > sizeof(buf)) {
- off = php_stream_tell(stream);
- len = sbuf.st_size - off;
- p = mmap(0, len, PROT_READ, MAP_SHARED, fd, off);
- if (p != (void *) MAP_FAILED) {
- BG(mmap_file) = p;
- BG(mmap_len) = len;
- PHPWRITE(p, len);
- BG(mmap_file) = NULL;
- munmap(p, len);
- bcount += len;
- ready = 1;
- }
+ if (php_stream_mmap_possible(stream)) {
+ char *p;
+ size_t mapped;
+
+ p = php_stream_mmap_range(stream, php_stream_tell(stream), 0, PHP_STREAM_MAP_MODE_SHARED_READONLY, &mapped);
+
+ if (p) {
+ PHPWRITE(p, mapped);
+
+ php_stream_mmap_unmap(stream);
+
+ return mapped;
}
}
-#endif
- if(!ready) {
- int b;
- while ((b = php_stream_read(stream, buf, sizeof(buf))) > 0) {
- PHPWRITE(buf, b);
- bcount += b;
- }
+ while ((b = php_stream_read(stream, buf, sizeof(buf))) > 0) {
+ PHPWRITE(buf, b);
+ bcount += b;
}
+
return bcount;
}
@@ -1118,9 +1100,6 @@ PHPAPI size_t _php_stream_copy_to_mem(php_stream *src, char **buf, size_t maxlen
size_t len = 0, max_len;
int step = CHUNK_SIZE;
int min_room = CHUNK_SIZE / 4;
-#if HAVE_MMAP
- int srcfd;
-#endif
if (buf)
*buf = NULL;
@@ -1131,50 +1110,25 @@ PHPAPI size_t _php_stream_copy_to_mem(php_stream *src, char **buf, size_t maxlen
if (maxlen == PHP_STREAM_COPY_ALL)
maxlen = 0;
-#if HAVE_MMAP
- /* try and optimize the case where we are copying from the start of a plain file.
- * We could probably make this work in more situations, but I don't trust the stdio
- * buffering layer.
- * */
- if ( php_stream_is(src, PHP_STREAM_IS_STDIO) &&
- !php_stream_is_filtered(src) &&
- php_stream_tell(src) == 0 &&
- SUCCESS == php_stream_cast(src, PHP_STREAM_AS_FD, (void**)&srcfd, 0))
- {
- struct stat sbuf;
+ if (php_stream_mmap_possible(src)) {
+ char *p;
+ size_t mapped;
- if (fstat(srcfd, &sbuf) == 0) {
- void *srcfile;
+ p = php_stream_mmap_range(src, php_stream_tell(src), maxlen, PHP_STREAM_MAP_MODE_SHARED_READONLY, &mapped);
-#if STREAM_DEBUG
- fprintf(stderr, "mmap attempt: maxlen=%d filesize=%ld\n", maxlen, sbuf.st_size);
-#endif
-
- if (maxlen > sbuf.st_size || maxlen == 0)
- maxlen = sbuf.st_size;
-#if STREAM_DEBUG
- fprintf(stderr, "mmap attempt: will map maxlen=%d\n", maxlen);
-#endif
-
- srcfile = mmap(NULL, maxlen, PROT_READ, MAP_SHARED, srcfd, 0);
- if (srcfile != (void*)MAP_FAILED) {
+ if (p) {
+ *buf = pemalloc_rel_orig(mapped + 1, persistent);
- *buf = pemalloc_rel_orig(maxlen + 1, persistent);
-
- if (*buf) {
- memcpy(*buf, srcfile, maxlen);
- (*buf)[maxlen] = '\0';
- ret = maxlen;
- }
+ if (*buf) {
+ memcpy(*buf, p, mapped);
+ (*buf)[mapped] = '\0';
+ }
- munmap(srcfile, maxlen);
+ php_stream_mmap_unmap(src);
- return ret;
- }
+ return mapped;
}
- /* fall through - we might be able to copy in smaller chunks */
}
-#endif
ptr = *buf = pemalloc_rel_orig(step, persistent);
max_len = step;
@@ -1204,9 +1158,6 @@ PHPAPI size_t _php_stream_copy_to_stream(php_stream *src, php_stream *dest, size
size_t haveread = 0;
size_t didread;
php_stream_statbuf ssbuf;
-#if HAVE_MMAP
- int srcfd;
-#endif
if (maxlen == 0)
return 0;
@@ -1214,41 +1165,6 @@ PHPAPI size_t _php_stream_copy_to_stream(php_stream *src, php_stream *dest, size
if (maxlen == PHP_STREAM_COPY_ALL)
maxlen = 0;
-#if HAVE_MMAP
- /* try and optimize the case where we are copying from the start of a plain file.
- * We could probably make this work in more situations, but I don't trust the stdio
- * buffering layer.
- * */
- if ( php_stream_is(src, PHP_STREAM_IS_STDIO) &&
- !php_stream_is_filtered(src) &&
- php_stream_tell(src) == 0 &&
- SUCCESS == php_stream_cast(src, PHP_STREAM_AS_FD, (void**)&srcfd, 0))
- {
- struct stat sbuf;
-
- if (fstat(srcfd, &sbuf) == 0) {
- void *srcfile;
-
- /* in the event that the source file is 0 bytes, return 1 to indicate success
- * because opening the file to write had already created a copy */
-
- if(sbuf.st_size ==0)
- return 1;
-
- if (maxlen > sbuf.st_size || maxlen == 0)
- maxlen = sbuf.st_size;
-
- srcfile = mmap(NULL, maxlen, PROT_READ, MAP_SHARED, srcfd, 0);
- if (srcfile != (void*)MAP_FAILED) {
- haveread = php_stream_write(dest, srcfile, maxlen);
- munmap(srcfile, maxlen);
- return haveread;
- }
- }
- /* fall through - we might be able to copy in smaller chunks */
- }
-#endif
-
if (php_stream_stat(src, &ssbuf) == 0) {
/* in the event that the source file is 0 bytes, return 1 to indicate success
* because opening the file to write had already created a copy */
@@ -1257,6 +1173,21 @@ PHPAPI size_t _php_stream_copy_to_stream(php_stream *src, php_stream *dest, size
}
}
+ if (php_stream_mmap_possible(src)) {
+ char *p;
+ size_t mapped;
+
+ p = php_stream_mmap_range(src, php_stream_tell(src), maxlen, PHP_STREAM_MAP_MODE_SHARED_READONLY, &mapped);
+
+ if (p) {
+ haveread = php_stream_write(dest, p, mapped);
+
+ php_stream_mmap_unmap(src);
+
+ return mapped;
+ }
+ }
+
while(1) {
readchunk = sizeof(buf);
@@ -1323,6 +1254,18 @@ int php_init_stream_wrappers(int module_number TSRMLS_DC)
zend_hash_init(&url_stream_wrappers_hash, 0, NULL, NULL, 1) == SUCCESS
&&
zend_hash_init(php_get_stream_filters_hash(), 0, NULL, NULL, 1) == SUCCESS
+ &&
+ zend_hash_init(php_stream_xport_get_hash(), 0, NULL, NULL, 1) == SUCCESS
+ &&
+ php_stream_xport_register("tcp", php_stream_generic_socket_factory TSRMLS_CC) == SUCCESS
+ &&
+ php_stream_xport_register("udp", php_stream_generic_socket_factory TSRMLS_CC) == SUCCESS
+#ifdef AF_UNIX
+ &&
+ php_stream_xport_register("unix", php_stream_generic_socket_factory TSRMLS_CC) == SUCCESS
+ &&
+ php_stream_xport_register("udg", php_stream_generic_socket_factory TSRMLS_CC) == SUCCESS
+#endif
) ? SUCCESS : FAILURE;
}
@@ -1330,6 +1273,7 @@ int php_shutdown_stream_wrappers(int module_number TSRMLS_DC)
{
zend_hash_destroy(&url_stream_wrappers_hash);
zend_hash_destroy(php_get_stream_filters_hash());
+ zend_hash_destroy(php_stream_xport_get_hash());
return SUCCESS;
}
@@ -1451,9 +1395,9 @@ PHPAPI php_stream *_php_stream_opendir(char *path, int options,
php_stream_wrapper_log_error(wrapper, options ^ REPORT_ERRORS TSRMLS_CC, "not implemented");
}
if (stream == NULL && (options & REPORT_ERRORS)) {
- display_wrapper_errors(wrapper, path, "failed to open dir" TSRMLS_CC);
+ php_stream_display_wrapper_errors(wrapper, path, "failed to open dir" TSRMLS_CC);
}
- tidy_wrapper_error_log(wrapper TSRMLS_CC);
+ php_stream_tidy_wrapper_error_log(wrapper TSRMLS_CC);
return stream;
}
@@ -1498,15 +1442,13 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(char *path, char *mode, int optio
if (wrapper) {
- /* prepare error stack */
- wrapper->err_count = 0;
- wrapper->err_stack = NULL;
-
stream = wrapper->wops->stream_opener(wrapper,
path_to_open, mode, options ^ REPORT_ERRORS,
opened_path, context STREAMS_REL_CC TSRMLS_CC);
- if (stream)
+
+ if (stream) {
stream->wrapper = wrapper;
+ }
}
#if ZEND_DEBUG
@@ -1554,9 +1496,9 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(char *path, char *mode, int optio
}
if (stream == NULL && (options & REPORT_ERRORS)) {
- display_wrapper_errors(wrapper, path, "failed to open stream" TSRMLS_CC);
+ php_stream_display_wrapper_errors(wrapper, path, "failed to open stream" TSRMLS_CC);
}
- tidy_wrapper_error_log(wrapper TSRMLS_CC);
+ php_stream_tidy_wrapper_error_log(wrapper TSRMLS_CC);
#if ZEND_DEBUG
if (stream == NULL && copy_of_path != NULL) {
efree(copy_of_path);
diff --git a/main/streams/transports.c b/main/streams/transports.c
new file mode 100644
index 0000000000..dc8c5a942e
--- /dev/null
+++ b/main/streams/transports.c
@@ -0,0 +1,341 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 4 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2003 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 2.02 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available at through the world-wide-web at |
+ | http://www.php.net/license/2_02.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), &factory, sizeof(factory), NULL);
+}
+
+PHPAPI int php_stream_xport_unregister(char *protocol TSRMLS_DC)
+{
+ return zend_hash_del(&xport_hash, protocol, strlen(protocol));
+}
+
+#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 = { FG(default_socket_timeout), 0 };
+
+ 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:
+ /* TODO: check if the socket is still live */
+ return stream;
+
+ 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) {
+ if (FAILURE == zend_hash_find(&xport_hash, (char*)protocol, n, (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);
+
+ return NULL;
+ }
+ }
+
+ 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) {
+ stream->context = context;
+
+ if ((flags & STREAM_XPORT_SERVER) == 0) {
+ /* client */
+
+ if (flags & STREAM_XPORT_CONNECT) {
+ if (0 != php_stream_xport_connect(stream, name, namelen,
+ flags & STREAM_XPORT_OP_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) {
+ if (0 != php_stream_xport_listen(stream, 5, &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 */
+ 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, long *textaddrlen,
+ void **addr, size_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_BIND;
+ 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_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;
+}
+
+
+/*
+ * 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
+ */
diff --git a/main/streams/xp_socket.c b/main/streams/xp_socket.c
new file mode 100644
index 0000000000..01861701e8
--- /dev/null
+++ b/main/streams/xp_socket.c
@@ -0,0 +1,502 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 4 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2003 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 2.02 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available at through the world-wide-web at |
+ | http://www.php.net/license/2_02.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 "ext/standard/file.h"
+#include "streams/php_streams_int.h"
+#include "php_network.h"
+
+#if defined(AF_UNIX)
+#include <sys/un.h>
+#endif
+
+
+static int php_tcp_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC);
+
+/* {{{ Generic socket stream operations */
+static size_t php_sockop_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
+{
+ php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
+ size_t didwrite;
+
+ if (sock->socket == -1) {
+ return 0;
+ }
+
+ didwrite = send(sock->socket, buf, count, 0);
+
+ if (didwrite <= 0) {
+ char *estr = php_socket_strerror(php_socket_errno(), NULL, 0);
+
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "send of %d bytes failed with errno=%d %s",
+ count, php_socket_errno(), estr);
+ efree(estr);
+ }
+
+ if (didwrite > 0) {
+ php_stream_notify_progress_increment(stream->context, didwrite, 0);
+ }
+
+ return didwrite;
+}
+
+static void php_sock_stream_wait_for_data(php_stream *stream, php_netstream_data_t *sock TSRMLS_DC)
+{
+ fd_set fdr, tfdr;
+ int retval;
+ struct timeval timeout, *ptimeout;
+
+ if (sock->socket == -1) {
+ return;
+ }
+
+ FD_ZERO(&fdr);
+ FD_SET(sock->socket, &fdr);
+ sock->timeout_event = 0;
+
+ if (sock->timeout.tv_sec == -1)
+ ptimeout = NULL;
+ else
+ ptimeout = &timeout;
+
+
+ while(1) {
+ tfdr = fdr;
+ timeout = sock->timeout;
+
+ retval = select(sock->socket + 1, &tfdr, NULL, NULL, ptimeout);
+
+ if (retval == 0)
+ sock->timeout_event = 1;
+
+ if (retval >= 0)
+ break;
+ }
+}
+
+static size_t php_sockop_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
+{
+ php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
+ int nr_bytes = 0;
+
+ if (sock->socket == -1) {
+ return 0;
+ }
+
+ if (sock->is_blocked) {
+ php_sock_stream_wait_for_data(stream, sock TSRMLS_CC);
+ if (sock->timeout_event)
+ return 0;
+ }
+
+ nr_bytes = recv(sock->socket, buf, count, 0);
+
+ if (nr_bytes == 0 || (nr_bytes == -1 && php_socket_errno() != EWOULDBLOCK)) {
+ stream->eof = 1;
+ }
+
+ if (nr_bytes > 0)
+ php_stream_notify_progress_increment(stream->context, nr_bytes, 0);
+
+ return nr_bytes;
+}
+
+
+static int php_sockop_close(php_stream *stream, int close_handle TSRMLS_DC)
+{
+ php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
+ fd_set wrfds, efds;
+ int n;
+ struct timeval timeout;
+
+ if (close_handle) {
+
+ if (sock->socket != -1) {
+ /* prevent more data from coming in */
+ shutdown(sock->socket, SHUT_RD);
+
+ /* try to make sure that the OS sends all data before we close the connection.
+ * Essentially, we are waiting for the socket to become writeable, which means
+ * that all pending data has been sent.
+ * We use a small timeout which should encourage the OS to send the data,
+ * but at the same time avoid hanging indefintely.
+ * */
+ do {
+ FD_ZERO(&wrfds);
+ FD_SET(sock->socket, &wrfds);
+ efds = wrfds;
+
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 5000; /* arbitrary */
+
+ n = select(sock->socket + 1, NULL, &wrfds, &efds, &timeout);
+ } while (n == -1 && php_socket_errno() == EINTR);
+
+ closesocket(sock->socket);
+ sock->socket = -1;
+ }
+
+ }
+
+ pefree(sock, php_stream_is_persistent(stream));
+
+ return 0;
+}
+
+static int php_sockop_flush(php_stream *stream TSRMLS_DC)
+{
+ php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
+ return fsync(sock->socket);
+}
+
+static int php_sockop_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC)
+{
+ php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
+ return fstat(sock->socket, &ssb->sb);
+}
+
+static int php_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC)
+{
+ int oldmode;
+ php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
+ php_stream_xport_param *xparam;
+
+ switch(option) {
+ case PHP_STREAM_OPTION_BLOCKING:
+
+ oldmode = sock->is_blocked;
+
+ /* no need to change anything */
+ if (value == oldmode)
+ return oldmode;
+
+ if (SUCCESS == php_set_sock_blocking(sock->socket, value TSRMLS_CC)) {
+ sock->is_blocked = value;
+ return oldmode;
+ }
+
+ return PHP_STREAM_OPTION_RETURN_ERR;
+
+ case PHP_STREAM_OPTION_READ_TIMEOUT:
+ sock->timeout = *(struct timeval*)ptrparam;
+ sock->timeout_event = 0;
+ return PHP_STREAM_OPTION_RETURN_OK;
+
+ case PHP_STREAM_OPTION_XPORT_API:
+ xparam = (php_stream_xport_param *)ptrparam;
+
+ switch (xparam->op) {
+ case STREAM_XPORT_OP_LISTEN:
+ xparam->outputs.returncode = listen(sock->socket, 5);
+ return PHP_STREAM_OPTION_RETURN_OK;
+
+ default:
+ return PHP_STREAM_OPTION_RETURN_NOTIMPL;
+ }
+
+ default:
+ return PHP_STREAM_OPTION_RETURN_NOTIMPL;
+ }
+}
+
+static int php_sockop_cast(php_stream *stream, int castas, void **ret TSRMLS_DC)
+{
+ php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
+
+ switch(castas) {
+ case PHP_STREAM_AS_STDIO:
+ if (ret) {
+ *ret = fdopen(sock->socket, stream->mode);
+ if (*ret)
+ return SUCCESS;
+ return FAILURE;
+ }
+ return SUCCESS;
+ case PHP_STREAM_AS_FD:
+ case PHP_STREAM_AS_SOCKETD:
+ if (ret)
+ *ret = (void*)sock->socket;
+ return SUCCESS;
+ default:
+ return FAILURE;
+ }
+}
+/* }}} */
+
+/* These may look identical, but we need them this way so that
+ * we can determine which type of socket we are dealing with
+ * by inspecting stream->ops.
+ * A "useful" side-effect is that the user's scripts can then
+ * make similar decisions using stream_get_meta_data.
+ * */
+php_stream_ops php_stream_generic_socket_ops = {
+ php_sockop_write, php_sockop_read,
+ php_sockop_close, php_sockop_flush,
+ "generic_socket",
+ NULL, /* seek */
+ php_sockop_cast,
+ php_sockop_stat,
+ php_sockop_set_option,
+};
+
+
+php_stream_ops php_stream_socket_ops = {
+ php_sockop_write, php_sockop_read,
+ php_sockop_close, php_sockop_flush,
+ "tcp_socket",
+ NULL, /* seek */
+ php_sockop_cast,
+ php_sockop_stat,
+ php_tcp_sockop_set_option,
+};
+
+php_stream_ops php_stream_udp_socket_ops = {
+ php_sockop_write, php_sockop_read,
+ php_sockop_close, php_sockop_flush,
+ "udp_socket",
+ NULL, /* seek */
+ php_sockop_cast,
+ php_sockop_stat,
+ php_tcp_sockop_set_option,
+};
+
+#ifdef AF_UNIX
+php_stream_ops php_stream_unix_socket_ops = {
+ php_sockop_write, php_sockop_read,
+ php_sockop_close, php_sockop_flush,
+ "unix_socket",
+ NULL, /* seek */
+ php_sockop_cast,
+ php_sockop_stat,
+ php_tcp_sockop_set_option,
+};
+php_stream_ops php_stream_unixdg_socket_ops = {
+ php_sockop_write, php_sockop_read,
+ php_sockop_close, php_sockop_flush,
+ "udg_socket",
+ NULL, /* seek */
+ php_sockop_cast,
+ php_sockop_stat,
+ php_tcp_sockop_set_option,
+};
+#endif
+
+
+/* network socket operations */
+
+static inline int php_tcp_sockop_bind(php_stream *stream, php_netstream_data_t *sock,
+ php_stream_xport_param *xparam TSRMLS_DC)
+{
+
+ return -1;
+}
+
+static inline int php_tcp_sockop_connect(php_stream *stream, php_netstream_data_t *sock,
+ php_stream_xport_param *xparam TSRMLS_DC)
+{
+ char *colon;
+ char *host = NULL;
+ int portno, err;
+ int ret;
+
+#ifdef AF_UNIX
+ if (stream->ops == &php_stream_unix_socket_ops || stream->ops == &php_stream_unixdg_socket_ops) {
+ struct sockaddr_un unix_addr;
+
+ sock->socket = socket(PF_UNIX, stream->ops == &php_stream_unix_socket_ops ? SOCK_STREAM : SOCK_DGRAM, 0);
+
+ if (sock->socket == SOCK_ERR) {
+ if (xparam->want_errortext) {
+ spprintf(&xparam->outputs.error_text, 0, "Failed to create unix socket");
+ }
+ return -1;
+ }
+
+ memset(&unix_addr, 0, sizeof(unix_addr));
+ unix_addr.sun_family = AF_UNIX;
+
+ /* we need to be binary safe on systems that support an abstract
+ * namespace */
+ if (xparam->inputs.namelen >= sizeof(unix_addr.sun_path)) {
+ /* On linux, when the path begins with a NUL byte we are
+ * referring to an abstract namespace. In theory we should
+ * allow an extra byte below, since we don't need the NULL.
+ * BUT, to get into this branch of code, the name is too long,
+ * so we don't care. */
+ xparam->inputs.namelen = sizeof(unix_addr.sun_path) - 1;
+ }
+
+ memcpy(unix_addr.sun_path, xparam->inputs.name, xparam->inputs.namelen);
+
+ ret = php_network_connect_socket(sock->socket,
+ (const struct sockaddr *)&unix_addr, sizeof(unix_addr),
+ xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC, xparam->inputs.timeout,
+ xparam->want_errortext ? &xparam->outputs.error_text : NULL,
+ &err);
+
+ xparam->outputs.error_code = err;
+
+ goto out;
+ }
+#endif
+
+ colon = memchr(xparam->inputs.name, ':', xparam->inputs.namelen);
+ if (colon) {
+ portno = atoi(colon + 1);
+ host = estrndup(xparam->inputs.name, colon - xparam->inputs.name);
+ } else {
+ if (xparam->want_errortext) {
+ spprintf(&xparam->outputs.error_text, 0, "Failed to parse address \"%s\"", xparam->inputs.name);
+ }
+ return -1;
+ }
+
+ /* Note: the test here for php_stream_udp_socket_ops is important, because we
+ * want the default to be TCP sockets so that the openssl extension can
+ * re-use this code. */
+
+ sock->socket = php_network_connect_socket_to_host(host, portno,
+ stream->ops == &php_stream_udp_socket_ops ? SOCK_DGRAM : SOCK_STREAM,
+ xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC,
+ xparam->inputs.timeout, xparam->want_errortext ? &xparam->outputs.error_text : NULL,
+ &err
+ TSRMLS_CC);
+
+ ret = sock->socket == -1 ? -1 : 0;
+ xparam->outputs.error_code = err;
+
+ if (host) {
+ efree(host);
+ }
+
+#ifdef AF_UNIX
+out:
+#endif
+
+ if (ret >= 0 && xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC && err == EINPROGRESS) {
+ /* indicates pending connection */
+ return 1;
+ }
+
+ return ret;
+}
+
+static inline int php_tcp_sockop_accept(php_stream *stream, php_netstream_data_t *sock,
+ php_stream_xport_param *xparam TSRMLS_DC)
+{
+ return -1;
+}
+
+static int php_tcp_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC)
+{
+ php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
+ php_stream_xport_param *xparam;
+
+ switch(option) {
+ case PHP_STREAM_OPTION_XPORT_API:
+ xparam = (php_stream_xport_param *)ptrparam;
+
+ switch(xparam->op) {
+ case STREAM_XPORT_OP_CONNECT:
+ case STREAM_XPORT_OP_CONNECT_ASYNC:
+ xparam->outputs.returncode = php_tcp_sockop_connect(stream, sock, xparam TSRMLS_CC);
+ return PHP_STREAM_OPTION_RETURN_OK;
+
+ case STREAM_XPORT_OP_BIND:
+ xparam->outputs.returncode = php_tcp_sockop_bind(stream, sock, xparam TSRMLS_CC);
+ return PHP_STREAM_OPTION_RETURN_OK;
+
+
+ case STREAM_XPORT_OP_ACCEPT:
+ xparam->outputs.returncode = php_tcp_sockop_accept(stream, sock, xparam TSRMLS_CC);
+ return PHP_STREAM_OPTION_RETURN_OK;
+ default:
+ /* fall through */
+ }
+
+ /* fall through */
+ default:
+ return php_sockop_set_option(stream, option, value, ptrparam TSRMLS_CC);
+ }
+}
+
+
+PHPAPI php_stream *php_stream_generic_socket_factory(const char *proto, long protolen,
+ char *resourcename, long resourcenamelen,
+ const char *persistent_id, int options, int flags,
+ struct timeval *timeout,
+ php_stream_context *context STREAMS_DC TSRMLS_DC)
+{
+ php_stream *stream = NULL;
+ php_netstream_data_t *sock;
+ php_stream_ops *ops;
+
+ /* which type of socket ? */
+ if (strncmp(proto, "tcp", protolen) == 0) {
+ ops = &php_stream_socket_ops;
+ } else if (strncmp(proto, "udp", protolen) == 0) {
+ ops = &php_stream_udp_socket_ops;
+ }
+#ifdef AF_UNIX
+ else if (strncmp(proto, "unix", protolen) == 0) {
+ ops = &php_stream_unix_socket_ops;
+ } else if (strncmp(proto, "udg", protolen) == 0) {
+ ops = &php_stream_unixdg_socket_ops;
+ }
+#endif
+ else {
+ /* should never happen */
+ return NULL;
+ }
+
+ sock = pemalloc(sizeof(php_netstream_data_t), persistent_id ? 1 : 0);
+ memset(sock, 0, sizeof(php_netstream_data_t));
+
+ sock->is_blocked = 1;
+ sock->timeout.tv_sec = FG(default_socket_timeout);
+ sock->timeout.tv_usec = 0;
+
+ /* we don't know the socket until we have determined if we are binding or
+ * connecting */
+ sock->socket = -1;
+
+ stream = php_stream_alloc_rel(ops, sock, persistent_id, "r+");
+
+ if (stream == NULL) {
+ pefree(sock, persistent_id ? 1 : 0);
+ return NULL;
+ }
+
+ if (flags == 0) {
+ return stream;
+ }
+
+ return stream;
+}
+
+
+/*
+ * 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
+ */