diff options
| author | Wez Furlong <wez@php.net> | 2003-02-27 17:43:38 +0000 |
|---|---|---|
| committer | Wez Furlong <wez@php.net> | 2003-02-27 17:43:38 +0000 |
| commit | fd61f69077f6156ca71dde60ecfd9ed9765a02db (patch) | |
| tree | 7285ad393cdb5a85107a3329d1ab2bcafe89f051 /main/streams | |
| parent | 560e33968de93250377606782949f5004affca83 (diff) | |
| download | php-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.c | 22 | ||||
| -rw-r--r-- | main/streams/mmap.c | 52 | ||||
| -rw-r--r-- | main/streams/php_stream_mmap.h | 82 | ||||
| -rw-r--r-- | main/streams/php_stream_transport.h | 153 | ||||
| -rw-r--r-- | main/streams/php_streams_int.h | 2 | ||||
| -rw-r--r-- | main/streams/plain_wrapper.c | 90 | ||||
| -rwxr-xr-x | main/streams/streams.c | 200 | ||||
| -rw-r--r-- | main/streams/transports.c | 341 | ||||
| -rw-r--r-- | main/streams/xp_socket.c | 502 |
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(¶m, 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, ¶m); + + 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(¶m, 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, ¶m); + + 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(¶m, 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, ¶m); + + 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(¶m, 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, ¶m); + + 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(¶m, 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, ¶m); + + 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(¶m, 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, ¶m); + + 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 + */ |
