]> granicus.if.org Git - php/commitdiff
Another big commit (tm).
authorWez Furlong <wez@php.net>
Thu, 27 Feb 2003 17:43:38 +0000 (17:43 +0000)
committerWez Furlong <wez@php.net>
Thu, 27 Feb 2003 17:43:38 +0000 (17:43 +0000)
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.

24 files changed:
configure.in
ext/dba/libinifile/inifile.c
ext/openssl/config.m4
ext/openssl/openssl.c
ext/openssl/php_openssl.h
ext/openssl/xp_ssl.c [new file with mode: 0644]
ext/standard/basic_functions.c
ext/standard/file.c
ext/standard/fsock.c
ext/standard/fsock.h
ext/standard/ftp_fopen_wrapper.c
ext/standard/http_fopen_wrapper.c
main/network.c
main/php_network.h
main/php_streams.h
main/streams/cast.c
main/streams/mmap.c [new file with mode: 0644]
main/streams/php_stream_mmap.h [new file with mode: 0644]
main/streams/php_stream_transport.h [new file with mode: 0644]
main/streams/php_streams_int.h
main/streams/plain_wrapper.c
main/streams/streams.c
main/streams/transports.c [new file with mode: 0644]
main/streams/xp_socket.c [new file with mode: 0644]

index 87be5ece141b38a6fb3b200f8f9af2d27e38a2f7..7232d34eabecc6fc4ecd2ec6c73d2e94a9e0616b 100644 (file)
@@ -1113,7 +1113,7 @@ PHP_ADD_SOURCES(main, main.c snprintf.c spprintf.c php_sprintf.c \
        output.c )
 
 PHP_ADD_SOURCES(main/streams, streams.c cast.c memory.c filter.c \
-       plain_wrapper.c userspace.c)
+       plain_wrapper.c userspace.c transports.c xp_socket.c mmap.c)
 
 PHP_ADD_SOURCES(/main, internal_functions.c,, sapi)
 PHP_ADD_SOURCES(/main, internal_functions_cli.c,, cli)
index 2a50c9e884dadc67eeb55b6eae76263165f1db45..6e3d94dc4a34dc1e53f608b02d144fd2974085f0 100644 (file)
@@ -87,8 +87,8 @@ inifile * inifile_alloc(php_stream *fp, int readonly, int persistent TSRMLS_DC)
        int fd = 0;
 
        if (!readonly) {
-               if (php_stream_is(fp, PHP_STREAM_IS_SOCKET)) {
-                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can't truncate sockets");
+               if (!php_stream_truncate_supported(fp)) {
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can't truncate this stream");
                        return NULL;
                }
                if (SUCCESS != php_stream_cast(fp, PHP_STREAM_AS_FD, (void*)&fd, 1)) {
@@ -320,7 +320,7 @@ static int inifile_truncate(inifile *dba, size_t size TSRMLS_DC)
 {
        int res;
 
-       if ((res=ftruncate(dba->fd, size)) != 0) {
+       if ((res=php_stream_truncate_set_size(dba->fp, size)) != 0) {
                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error in ftruncate: %d", res);
                return FAILURE;
        }
index 93c5d052fd9b1f6b5bfb50eaf485f0a5c028450d..6cc9568428236e5da91b585db0d3d6bdf1e8a2d8 100644 (file)
@@ -3,7 +3,7 @@ dnl $Id$
 dnl
 
 if test "$PHP_OPENSSL" != "no"; then
-  PHP_NEW_EXTENSION(openssl, openssl.c, $ext_openssl_shared)
+  PHP_NEW_EXTENSION(openssl, openssl.c xp_ssl.c, $ext_openssl_shared)
   OPENSSL_SHARED_LIBADD="-lcrypto -lssl"
   PHP_SUBST(OPENSSL_SHARED_LIBADD)
   AC_DEFINE(HAVE_OPENSSL_EXT,1,[ ])
index 976023e32942c1bea3ed19d2b3d1dd881482a21c..7fc9da3f5dca258f94e129b96c0a8aaf15405081 100644 (file)
@@ -608,6 +608,13 @@ PHP_MINIT_FUNCTION(openssl)
        } else {
                strlcpy(default_ssl_conf_filename, config_filename, sizeof(default_ssl_conf_filename));
        }
+
+       php_stream_xport_register("ssl", php_openssl_ssl_socket_factory TSRMLS_CC);
+       php_stream_xport_register("tls", php_openssl_ssl_socket_factory TSRMLS_CC);
+
+       /* override the default tcp socket provider */
+       php_stream_xport_register("tcp", php_openssl_ssl_socket_factory TSRMLS_CC);
+       
        return SUCCESS;
 }
 /* }}} */
@@ -628,6 +635,13 @@ PHP_MINFO_FUNCTION(openssl)
 PHP_MSHUTDOWN_FUNCTION(openssl)
 {
        EVP_cleanup();
+
+       php_stream_xport_unregister("ssl" TSRMLS_CC);
+       php_stream_xport_unregister("tls" TSRMLS_CC);
+
+       /* reinstate the default tcp handler */
+       php_stream_xport_register("tcp", php_stream_generic_socket_factory TSRMLS_CC);
+
        return SUCCESS;
 }
 /* }}} */
index 89da3167e18c0e6b1692d9fdf36594b519b68c9c..2c7dde6bb63a08b9a1d76eb7d573d53b65fdbc64 100644 (file)
@@ -26,6 +26,8 @@
 extern zend_module_entry openssl_module_entry;
 #define phpext_openssl_ptr &openssl_module_entry
 
+php_stream_transport_factory_func php_openssl_ssl_socket_factory;
+
 PHP_MINIT_FUNCTION(openssl);
 PHP_MSHUTDOWN_FUNCTION(openssl);
 PHP_MINFO_FUNCTION(openssl);
diff --git a/ext/openssl/xp_ssl.c b/ext/openssl/xp_ssl.c
new file mode 100644 (file)
index 0000000..eb44927
--- /dev/null
@@ -0,0 +1,498 @@
+/*
+  +----------------------------------------------------------------------+
+  | 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"
+#include "php_openssl.h"
+#include <openssl/err.h>
+
+
+/* This implementation is very closely tied to the that of the native
+ * sockets implemented in the core.
+ * Don't try this technique in other extensions!
+ * */
+
+typedef struct _php_openssl_netstream_data_t {
+       php_netstream_data_t s;
+       SSL *ssl_handle;
+       int enable_on_connect;
+       int is_client;
+       int ssl_active;
+       php_stream_xport_crypt_method_t method;
+} php_openssl_netstream_data_t;
+
+php_stream_ops php_openssl_socket_ops;
+
+static int handle_ssl_error(php_stream *stream, int nr_bytes TSRMLS_DC)
+{
+       php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
+       int err = SSL_get_error(sslsock->ssl_handle, nr_bytes);
+       char esbuf[512];
+       char *ebuf = NULL, *wptr = NULL;
+       size_t ebuf_size = 0;
+       unsigned long code;
+       int retry = 1;
+
+       switch(err) {
+               case SSL_ERROR_ZERO_RETURN:
+                       /* SSL terminated (but socket may still be active) */
+                       retry = 0;
+                       break;
+               case SSL_ERROR_WANT_READ:
+               case SSL_ERROR_WANT_WRITE:
+                       /* re-negotiation, or perhaps the SSL layer needs more
+                        * packets: retry in next iteration */
+                       break;
+               case SSL_ERROR_SYSCALL:
+                       if (ERR_peek_error() == 0) {
+                               if (nr_bytes == 0) {
+                                       php_error_docref(NULL TSRMLS_CC, E_WARNING,
+                                                       "SSL: fatal protocol error");
+                                       stream->eof = 1;
+                                       retry = 0;
+                               } else {
+                                       char *estr = php_socket_strerror(php_socket_errno(), NULL, 0);
+
+                                       php_error_docref(NULL TSRMLS_CC, E_WARNING,
+                                                       "SSL: %s", estr);
+
+                                       efree(estr);
+                                       retry = 0;
+                               }
+                               break;
+                       }
+                       /* fall through */
+               default:
+                       /* some other error */
+                       while ((code = ERR_get_error()) != 0) {
+                               /* allow room for a NUL and an optional \n */
+                               if (ebuf) {
+                                       esbuf[0] = '\n';
+                                       esbuf[1] = '\0';
+                                       ERR_error_string_n(code, esbuf + 1, sizeof(esbuf) - 2);
+                               } else {
+                                       esbuf[0] = '\0';
+                                       ERR_error_string_n(code, esbuf, sizeof(esbuf) - 1);
+                               }
+                               code = strlen(esbuf);
+                               esbuf[code] = '\0';
+
+                               ebuf = erealloc(ebuf, ebuf_size + code + 1);
+                               if (wptr == NULL) {
+                                       wptr = ebuf;
+                               }       
+
+                               /* also copies the NUL */
+                               memcpy(wptr, esbuf, code + 1);
+                               wptr += code;
+                       }
+
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING,
+                                       "SSL operation failed with code %d.%s%s",
+                                       err,
+                                       ebuf ? "OpenSSL Error messages:\n" : "",
+                                       ebuf ? ebuf : "");
+                               
+                       retry = 0;
+       }
+       return retry;
+}
+
+
+static size_t php_openssl_sockop_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
+{
+       php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
+       size_t didwrite;
+       
+       if (sslsock->ssl_active) {
+               int retry = 1;
+
+               do {
+                       didwrite = SSL_write(sslsock->ssl_handle, buf, count);
+
+                       if (didwrite <= 0) {
+                               retry = handle_ssl_error(stream, didwrite TSRMLS_CC);
+                       } else {
+                               break;
+                       }
+               } while(retry);
+               
+       } else {
+               didwrite = php_stream_socket_ops.write(stream, buf, count TSRMLS_CC);
+       }
+       
+       if (didwrite > 0)
+               php_stream_notify_progress_increment(stream->context, didwrite, 0);
+
+       return didwrite;
+}
+
+static size_t php_openssl_sockop_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
+{
+       php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
+       int nr_bytes = 0;
+
+       if (sslsock->ssl_active) {
+               int retry = 1;
+
+               do {
+                       nr_bytes = SSL_read(sslsock->ssl_handle, buf, count);
+
+                       if (nr_bytes <= 0) {
+                               retry = handle_ssl_error(stream, nr_bytes TSRMLS_CC);
+                               if (retry == 0 && !SSL_pending(sslsock->ssl_handle)) {
+                                       stream->eof = 1;
+                               }
+                       } else {
+                               /* we got the data */
+                               break;
+                       }
+               } while (retry);
+       }
+       else
+       {
+               nr_bytes = php_stream_socket_ops.read(stream, buf, count TSRMLS_CC);
+       }
+
+       if (nr_bytes > 0)
+               php_stream_notify_progress_increment(stream->context, nr_bytes, 0);
+
+       return nr_bytes;
+}
+
+
+static int php_openssl_sockop_close(php_stream *stream, int close_handle TSRMLS_DC)
+{
+       php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
+       fd_set wrfds, efds;
+       int n;
+       struct timeval timeout;
+
+       if (close_handle) {
+               if (sslsock->ssl_active) {
+                       SSL_shutdown(sslsock->ssl_handle);
+                       sslsock->ssl_active = 0;
+               }
+               if (sslsock->ssl_handle) {
+                       SSL_free(sslsock->ssl_handle);
+                       sslsock->ssl_handle = NULL;
+               }
+               if (sslsock->s.socket != -1) {
+                       /* prevent more data from coming in */
+                       shutdown(sslsock->s.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(sslsock->s.socket, &wrfds);
+                               efds = wrfds;
+
+                               timeout.tv_sec = 0;
+                               timeout.tv_usec = 5000; /* arbitrary */
+
+                               n = select(sslsock->s.socket + 1, NULL, &wrfds, &efds, &timeout);
+                       } while (n == -1 && php_socket_errno() == EINTR);
+
+                       closesocket(sslsock->s.socket);
+                       sslsock->s.socket = -1;
+               }
+       }
+
+       pefree(sslsock, php_stream_is_persistent(stream));
+       
+       return 0;
+}
+
+static int php_openssl_sockop_flush(php_stream *stream TSRMLS_DC)
+{
+       return php_stream_socket_ops.flush(stream TSRMLS_CC);
+}
+
+static int php_openssl_sockop_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC)
+{
+       return php_stream_socket_ops.stat(stream, ssb TSRMLS_CC);
+}
+
+static inline int php_openssl_setup_crypto(php_stream *stream,
+               php_openssl_netstream_data_t *sslsock,
+               php_stream_xport_crypto_param *cparam
+               TSRMLS_DC)
+{
+       SSL_CTX *ctx;
+       SSL_METHOD *method;
+       
+       if (sslsock->ssl_handle) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSL/TLS already set-up for this stream");
+               return -1;
+       }
+
+       /* need to do slightly different things, based on client/server method,
+        * so lets remember which method was selected */
+
+       switch (cparam->inputs.method) {
+               case STREAM_CRYPTO_METHOD_SSLv23_CLIENT:
+                       sslsock->is_client = 1;
+                       method = SSLv23_client_method();
+                       break;
+               case STREAM_CRYPTO_METHOD_SSLv2_CLIENT:
+                       sslsock->is_client = 1;
+                       method = SSLv2_client_method();
+                       break;
+               case STREAM_CRYPTO_METHOD_SSLv3_CLIENT:
+                       sslsock->is_client = 1;
+                       method = SSLv3_client_method();
+                       break;
+               case STREAM_CRYPTO_METHOD_TLS_CLIENT:
+                       sslsock->is_client = 1;
+                       method = TLSv1_client_method();
+                       break;
+               case STREAM_CRYPTO_METHOD_SSLv23_SERVER:
+                       sslsock->is_client = 0;
+                       method = SSLv23_server_method();
+                       break;
+               case STREAM_CRYPTO_METHOD_SSLv3_SERVER:
+                       sslsock->is_client = 0;
+                       method = SSLv3_server_method();
+                       break;
+               case STREAM_CRYPTO_METHOD_SSLv2_SERVER:
+                       sslsock->is_client = 0;
+                       method = SSLv2_server_method();
+                       break;
+               case STREAM_CRYPTO_METHOD_TLS_SERVER:
+                       sslsock->is_client = 0;
+                       method = TLSv1_server_method();
+                       break;
+               default:
+                       printf("unknown method\n");
+                       return -1;
+
+       }
+
+       ctx = SSL_CTX_new(method);
+       if (ctx == NULL) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create an SSL context");
+               return -1;
+       }
+
+       sslsock->ssl_handle = SSL_new(ctx);
+       if (sslsock->ssl_handle == NULL) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create an SSL handle");
+               SSL_CTX_free(ctx);
+               return -1;
+       }
+
+       if (!SSL_set_fd(sslsock->ssl_handle, sslsock->s.socket)) {
+               printf("failed to set fd %d\n", sslsock->s.socket);
+               handle_ssl_error(stream, 0 TSRMLS_CC);
+       }
+
+       if (cparam->inputs.session) {
+               printf("sess=%p\n", cparam->inputs.session);
+               if (cparam->inputs.session->ops != &php_openssl_socket_ops) {
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied session stream must be an SSL enabled stream");
+               } else {
+                       SSL_copy_session_id(sslsock->ssl_handle, ((php_openssl_netstream_data_t*)cparam->inputs.session->abstract)->ssl_handle);
+               }
+       }
+printf("crypto prepared for fd=%d\n", sslsock->s.socket);
+       return 0;
+}
+
+static inline int php_openssl_enable_crypto(php_stream *stream,
+               php_openssl_netstream_data_t *sslsock,
+               php_stream_xport_crypto_param *cparam
+               TSRMLS_DC)
+{
+       int n, retry = 1;
+
+       if (cparam->inputs.activate) {
+               if (sslsock->is_client) {
+                       do {
+                               n = SSL_connect(sslsock->ssl_handle);
+
+                               if (n <= 0) {
+                                       retry = handle_ssl_error(stream, n TSRMLS_CC);
+                                       printf("error; retry = %d\n", retry);
+                               } else {
+                                       break;
+                               }
+                       } while (retry);
+
+                       printf("enabled_crypto: n=%d\n", n);
+                       if (n == 1) {
+                               sslsock->ssl_active = 1;
+                       }
+                       
+                       return n;
+                       
+               } else {
+                       
+               }
+       } else {
+               /* deactivate - common for server/client */
+       }
+       return -1;
+}
+
+static int php_openssl_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC)
+{
+       php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
+       php_stream_xport_crypto_param *cparam = (php_stream_xport_crypto_param *)ptrparam;
+       php_stream_xport_param *xparam = (php_stream_xport_param *)ptrparam;
+
+       switch (option) {
+               case PHP_STREAM_OPTION_CRYPTO_API:
+
+                       switch(cparam->op) {
+
+                               case STREAM_XPORT_CRYPTO_OP_SETUP:
+                                       cparam->outputs.returncode = php_openssl_setup_crypto(stream, sslsock, cparam TSRMLS_CC);
+                                       return PHP_STREAM_OPTION_RETURN_OK;
+                               case STREAM_XPORT_CRYPTO_OP_ENABLE:
+                                       cparam->outputs.returncode = php_openssl_enable_crypto(stream, sslsock, cparam TSRMLS_CC);
+                                       return PHP_STREAM_OPTION_RETURN_OK;
+                               default:
+                                       /* fall through */
+                       }
+
+                       break;
+
+               case PHP_STREAM_OPTION_XPORT_API:
+                       switch(xparam->op) {
+
+                               case STREAM_XPORT_OP_CONNECT:
+                               case STREAM_XPORT_OP_CONNECT_ASYNC:
+                                       /* TODO: Async connects need to check the enable_on_connect option when
+                                        * we notice that the connect has actually been established */
+                                       php_stream_socket_ops.set_option(stream, option, value, ptrparam TSRMLS_CC);
+
+                                       if (xparam->outputs.returncode == 0 && sslsock->enable_on_connect) {
+                                               if (php_stream_xport_crypto_setup(stream, sslsock->method, NULL TSRMLS_CC) < 0 ||
+                                                               php_stream_xport_crypto_enable(stream, 1 TSRMLS_CC) < 0) {
+                                                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to enable crypto");
+                                                       xparam->outputs.returncode = -1;
+                                               }
+                                       }
+                                       return PHP_STREAM_OPTION_RETURN_OK;
+                               default:
+                                       /* fall through */
+                       }
+       }
+
+       return php_stream_socket_ops.set_option(stream, option, value, ptrparam TSRMLS_CC);
+}
+
+static int php_openssl_sockop_cast(php_stream *stream, int castas, void **ret TSRMLS_DC)
+{
+       php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
+
+       switch(castas)  {
+               case PHP_STREAM_AS_STDIO:
+                       if (sslsock->ssl_active) {
+                               return FAILURE;
+                       }
+                       if (ret)        {
+                               *ret = fdopen(sslsock->s.socket, stream->mode);
+                               if (*ret) {
+                                       return SUCCESS;
+                               }
+                               return FAILURE;
+                       }
+                       return SUCCESS;
+               case PHP_STREAM_AS_FD:
+               case PHP_STREAM_AS_SOCKETD:
+                       if (sslsock->ssl_active) {
+                               return FAILURE;
+                       }
+                       if (ret) {
+                               *ret = (void*)sslsock->s.socket;
+                       }
+                       return SUCCESS;
+               default:
+                       return FAILURE;
+       }
+}
+
+php_stream_ops php_openssl_socket_ops = {
+       php_openssl_sockop_write, php_openssl_sockop_read,
+       php_openssl_sockop_close, php_openssl_sockop_flush,
+       "tcp_socket/ssl",
+       NULL, /* seek */
+       php_openssl_sockop_cast,
+       php_openssl_sockop_stat,
+       php_openssl_sockop_set_option,
+};
+
+
+PHPAPI php_stream *php_openssl_ssl_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_openssl_netstream_data_t *sslsock = NULL;
+       
+       sslsock = pemalloc(sizeof(php_openssl_netstream_data_t), persistent_id ? 1 : 0);
+       memset(sslsock, 0, sizeof(php_openssl_netstream_data_t));
+
+       sslsock->s.is_blocked = 1;
+       sslsock->s.timeout.tv_sec = FG(default_socket_timeout);
+       sslsock->s.timeout.tv_usec = 0;
+
+       /* we don't know the socket until we have determined if we are binding or
+        * connecting */
+       sslsock->s.socket = -1;
+       
+       stream = php_stream_alloc_rel(&php_openssl_socket_ops, sslsock, persistent_id, "r+");
+
+       if (stream == NULL)     {
+               pefree(sslsock, persistent_id ? 1 : 0);
+               return NULL;
+       }
+
+       if (strncmp(proto, "ssl", protolen) == 0) {
+               sslsock->enable_on_connect = 1;
+               sslsock->method = STREAM_CRYPTO_METHOD_SSLv23_CLIENT;
+       } else if (strncmp(proto, "tls", protolen) == 0) {
+               sslsock->enable_on_connect = 1;
+               sslsock->method = STREAM_CRYPTO_METHOD_TLS_CLIENT;
+       }
+       
+       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
+ */
index 9a14905b3a9c166de0fe80511b0f0c703b21229d..84c1347f504340fc944e52cff1e22e82d081b694 100644 (file)
@@ -1166,9 +1166,6 @@ PHP_RINIT_FUNCTION(basic)
        BG(locale_string) = NULL;
        BG(user_compare_func_name) = NULL;
        BG(array_walk_func_name) = NULL;
-#ifdef HAVE_MMAP
-       BG(mmap_file) = NULL;
-#endif
        BG(page_uid) = -1;
        BG(page_gid) = -1;
        BG(page_inode) = -1;
@@ -1219,7 +1216,6 @@ PHP_RSHUTDOWN_FUNCTION(basic)
        }
        STR_FREE(BG(locale_string));
 
-       PHP_RSHUTDOWN(fsock)(SHUTDOWN_FUNC_ARGS_PASSTHRU);
        PHP_RSHUTDOWN(filestat)(SHUTDOWN_FUNC_ARGS_PASSTHRU);
 #ifdef HAVE_SYSLOG_H
        PHP_RSHUTDOWN(syslog)(SHUTDOWN_FUNC_ARGS_PASSTHRU);
@@ -1246,12 +1242,6 @@ PHP_RSHUTDOWN_FUNCTION(basic)
                BG(user_filter_map) = NULL;
        }
        
-#ifdef HAVE_MMAP
-       if (BG(mmap_file)) {
-               munmap(BG(mmap_file), BG(mmap_len));
-       }
-#endif
-
        return SUCCESS;
 }
 
index 0674f4b3fd4a25ee8464898ed345a560289eb17f..9da076e08a6efd20676dcf02c8e3fe546a155124 100644 (file)
@@ -647,6 +647,7 @@ PHP_FUNCTION(stream_get_meta_data)
        
        add_assoc_long(return_value, "unread_bytes", stream->writepos - stream->readpos);
        
+#if 0
        if (php_stream_is(stream, PHP_STREAM_IS_SOCKET))        {
                php_netstream_data_t *sock = PHP_NETSTREAM_DATA_FROM_STREAM(stream);
 
@@ -654,10 +655,13 @@ PHP_FUNCTION(stream_get_meta_data)
                add_assoc_bool(return_value, "blocked", sock->is_blocked);
                add_assoc_bool(return_value, "eof", stream->eof);
        } else {
+#endif
                add_assoc_bool(return_value, "timed_out", 0);
                add_assoc_bool(return_value, "blocked", 1);
                add_assoc_bool(return_value, "eof", php_stream_eof(stream));
+#if 0
        }
+#endif
 
 }
 /* }}} */
@@ -2237,7 +2241,7 @@ PHP_FUNCTION(unlink)
 }
 /* }}} */
 
-/* {{{ proto int ftruncate(resource fp, int size)
+/* {{{ proto bool ftruncate(resource fp, int size)
    Truncate file to 'size' length */
 PHP_NAMED_FUNCTION(php_if_ftruncate)
 {
@@ -2254,15 +2258,12 @@ PHP_NAMED_FUNCTION(php_if_ftruncate)
 
        convert_to_long_ex(size);
 
-       if (php_stream_is(stream, PHP_STREAM_IS_SOCKET))        {
-               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can't truncate sockets!");
+       if (!php_stream_truncate_supported(stream)) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can't truncate this stream!");
                RETURN_FALSE;
        }
-       if (SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD, (void*)&fd, 1))        {
-               ret = ftruncate(fd, Z_LVAL_PP(size));
-               RETURN_LONG(ret + 1);
-       }
-       RETURN_FALSE;
+       
+       RETURN_BOOL(0 == php_stream_truncate_set_size(stream, Z_LVAL_PP(size)));
 }
 /* }}} */
 
index bbe6d7debbe4b71e78537835b09c5fc39177144c..8f41cf4d3f1606c50ed1f31e03159a2f8a2de9ae 100644 (file)
 
 /* $Id$ */
 
-/* converted to PHP Streams and moved much code to main/network.c [wez] */
-
-/* Synced with php 3.0 revision 1.121 1999-06-18 [ssb] */
-/* Synced with php 3.0 revision 1.133 1999-07-21 [sas] */
-
 #include "php.h"
 #include "php_globals.h"
 #include <stdlib.h>
 #include <stddef.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
-#ifdef HAVE_FCNTL_H
-# include <fcntl.h>
-#endif
-
-#ifdef HAVE_SYS_TIME_H
-# include <sys/time.h>
-#endif
-
-#include <sys/types.h>
-#ifdef HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
-#endif
-#ifdef PHP_WIN32
-#include <winsock2.h>
-#elif defined(NETWARE)
-#ifdef NEW_LIBC
-#ifdef USE_WINSOCK
-#include <novsock2.h>
-#else
-#include <netinet/in.h>
-#include <netdb.h>
-/*#include <sys/socket.h>*/
-#include <sys/select.h>
-/*#else
-#include <sys/socket.h>*/
-#endif
-#endif
-#else
-#include <netinet/in.h>
-#include <netdb.h>
-#if HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-#endif
-#if defined(PHP_WIN32) || defined(__riscos__) || defined(NETWARE)
-#undef AF_UNIX
-#endif
-#if defined(AF_UNIX)
-#include <sys/un.h>
-#endif
-#ifdef HAVE_SYS_SELECT_H
-#include <sys/select.h>
-#endif
-
-#ifndef PF_INET
-#define PF_INET AF_INET
-#endif
-
-#ifndef PF_UNIX
-#define PF_UNIX AF_UNIX
-#endif
-
-#include <string.h>
-#include <errno.h>
-
-#include "base64.h"
-#include "file.h"
-#include "url.h"
-#include "fsock.h"
-
 #include "php_network.h"
-
-#ifdef ZTS
-static int fsock_globals_id;
-#endif
-
-#ifdef PHP_WIN32
-#define EWOULDBLOCK WSAEWOULDBLOCK
-#elif defined(NETWARE)
-#ifdef USE_WINSOCK
-#define EWOULDBLOCK WSAEWOULDBLOCK
-#endif
-#endif
-
-/* {{{ php_lookup_hostname */
-
-/*
- * Converts a host name to an IP address.
- * TODO: This looks like unused code suitable for nuking.
- */
-PHPAPI int php_lookup_hostname(const char *addr, struct in_addr *in)
-{
-       struct hostent *host_info;
-
-       if (!inet_aton(addr, in)) {
-               /* XXX NOT THREAD SAFE */
-               host_info = gethostbyname(addr);
-               if (host_info == 0) {
-                       /* Error: unknown host */
-                       return -1;
-               }
-               *in = *((struct in_addr *) host_info->h_addr);
-       }
-       return 0;
-}
-/* }}} */
+#include "file.h"
 
 /* {{{ php_fsockopen() */
 
 static void php_fsockopen_stream(INTERNAL_FUNCTION_PARAMETERS, int persistent)
 {
        char *host;
-       int host_len;
+       long host_len;
        int port = -1;
        zval *zerrno = NULL, *zerrstr = NULL;
        double timeout = FG(default_socket_timeout);
@@ -144,6 +41,9 @@ static void php_fsockopen_stream(INTERNAL_FUNCTION_PARAMETERS, int persistent)
        char *hashkey = NULL;
        php_stream *stream = NULL;
        int err;
+       char *hostname = NULL;
+       long hostname_len;
+       char *errstr = NULL;
 
        RETVAL_FALSE;
        
@@ -151,22 +51,17 @@ static void php_fsockopen_stream(INTERNAL_FUNCTION_PARAMETERS, int persistent)
                RETURN_FALSE;
        }
 
-
        if (persistent) {
                spprintf(&hashkey, 0, "pfsockopen__%s:%d", host, port);
-
-               switch(php_stream_from_persistent_id(hashkey, &stream TSRMLS_CC)) {
-                       case PHP_STREAM_PERSISTENT_SUCCESS:
-                               /* TODO: could check if the socket is still alive here */
-                               php_stream_to_zval(stream, return_value);
-                               
-                               /* fall through */
-                       case PHP_STREAM_PERSISTENT_FAILURE:
-                               efree(hashkey);
-                               return;
-               }
        }
 
+       if (port > 0) {
+               hostname_len = spprintf(&hostname, 0, "%s:%d", host, port);
+       } else {
+               hostname_len = host_len;
+               hostname = host;
+       }
+       
        /* prepare the timeout value for use */
        conv = (unsigned long) (timeout * 1000000.0);
        tv.tv_sec = conv / 1000000;
@@ -181,88 +76,29 @@ static void php_fsockopen_stream(INTERNAL_FUNCTION_PARAMETERS, int persistent)
                ZVAL_STRING(zerrstr, "", 1);
        }
 
-       if (port > 0)   { /* connect to a host */
-               enum php_sslflags_t { php_ssl_none, php_ssl_v23, php_ssl_tls };
-               enum php_sslflags_t ssl_flags = php_ssl_none;
-               struct {
-                       char *proto;
-                       int protolen;
-                       int socktype;
-                       enum php_sslflags_t ssl_flags;
-                       /* more flags to be added here */
-               } sockmodes[] = {
-                       { "udp://", 6, SOCK_DGRAM,      php_ssl_none },
-                       { "tcp://", 6, SOCK_STREAM,     php_ssl_none },
-                       { "ssl://", 6, SOCK_STREAM, php_ssl_v23 },
-                       { "tls://", 6, SOCK_STREAM, php_ssl_tls },
-                       /* more modes to be added here */
-                       { NULL, 0, 0 }
-               };
-               int socktype = SOCK_STREAM;
-               int i;
-
-               for (i = 0; sockmodes[i].proto != NULL; i++) {
-                       if (strncmp(host, sockmodes[i].proto, sockmodes[i].protolen) == 0) {
-                               ssl_flags = sockmodes[i].ssl_flags;             
-                               socktype = sockmodes[i].socktype;
-                               host += sockmodes[i].protolen;
-                               break;
-                       }
-               }
-#if !HAVE_OPENSSL_EXT
-               if (ssl_flags != php_ssl_none) {
-                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "no SSL support in this build");
-               }
-               else
-#endif
-               stream = php_stream_sock_open_host(host, (unsigned short)port, socktype, &tv, hashkey);
-
-               /* Preserve error */
-               err = php_socket_errno();
+       stream = php_stream_xport_create(hostname, hostname_len, ENFORCE_SAFE_MODE | REPORT_ERRORS,
+                       STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT, hashkey, &tv, NULL, &errstr, &err);
 
-               if (stream == NULL) {
-                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to connect to %s:%d", host, port);
-               }
-               
-#if HAVE_OPENSSL_EXT
-               if (stream && ssl_flags != php_ssl_none) {
-                       int ssl_ret = FAILURE;
-                       switch(ssl_flags)       {
-                               case php_ssl_v23:
-                                       ssl_ret = php_stream_sock_ssl_activate_with_method(stream, 1, SSLv23_client_method(), NULL TSRMLS_CC);
-                                       break;
-                               case php_ssl_tls:
-                                       ssl_ret = php_stream_sock_ssl_activate_with_method(stream, 1, TLSv1_client_method(), NULL TSRMLS_CC);
-                                       break;
-                               default:
-                                       /* unknown ?? */
-                                       break;
-                       }
-                       if (ssl_ret == FAILURE)
-                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to activate SSL mode %d", ssl_flags);
-               }
-#endif
-               
-       } else {
-               /* FIXME: Win32 - this probably does not return sensible errno and errstr */
-               stream = php_stream_sock_open_unix(host, host_len, hashkey, &tv);
-               err = php_socket_errno();
+       if (port > 0) {
+               efree(hostname);
+       }
+       if (stream == NULL) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to connect to %s:%d", host, port);
        }
 
-       if (hashkey)
+       if (hashkey) {
                efree(hashkey);
+       }
        
        if (stream == NULL)     {
                if (zerrno) {
                        zval_dtor(zerrno);
                        ZVAL_LONG(zerrno, err);
                }
-               if (zerrstr) {
-                       char *buf = php_socket_strerror(err, NULL, 0);
-
-                       /* no need to dup; we would only need to efree buf anyway */
+               if (zerrstr && errstr) {
+                       /* no need to dup; we need to efree buf anyway */
                        zval_dtor(zerrstr);
-                       ZVAL_STRING(zerrstr, buf, 0);
+                       ZVAL_STRING(zerrstr, errstr, 0);
                }
                RETURN_FALSE;
        }
@@ -287,12 +123,6 @@ PHP_FUNCTION(pfsockopen)
 }
 /* }}} */
 
-/* {{{ RSHUTDOWN_FUNCTION(fsock) */
-PHP_RSHUTDOWN_FUNCTION(fsock)
-{
-       return SUCCESS;
-}
-/* }}} */
 /*
  * Local variables:
  * tab-width: 4
index 966fc1ecec665bcb9f4f8eccd6914a3352090c7a..7d263c31e167e35141c9780530ba587780cd7d4c 100644 (file)
 PHP_FUNCTION(fsockopen);
 PHP_FUNCTION(pfsockopen);
 
-PHPAPI int php_lookup_hostname(const char *addr, struct in_addr *in);
-
-PHP_RSHUTDOWN_FUNCTION(fsock);
-
 /*
  * Local variables:
  * tab-width: 4
index 98b5a27892b08dcfd217dd68ed10c2fce2737f9a..192786058db2c7138711ecdae81d57ba238da28f 100644 (file)
@@ -142,10 +142,8 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, char *path, ch
        char *scratch;
        int result;
        int i, use_ssl;
-#if HAVE_OPENSSL_EXT   
        int use_ssl_on_data=0;
        php_stream *reuseid=NULL;
-#endif 
        char *tpath, *ttpath, *hoststart=NULL;
        size_t file_size = 0;
 
@@ -182,7 +180,6 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, char *path, ch
                goto errexit;
        }
 
-#if HAVE_OPENSSL_EXT
        if (use_ssl)    {
        
                /* send the AUTH TLS request name */
@@ -212,7 +209,9 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, char *path, ch
        }
        
        if (use_ssl) {
-               if (use_ssl && php_stream_sock_ssl_activate(stream, 1) == FAILURE)      {
+               if (php_stream_xport_crypto_setup(stream,
+                               STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL TSRMLS_CC) < 0
+                               || php_stream_xport_crypto_enable(stream, 1 TSRMLS_CC) < 0) {
                        php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "Unable to activate SSL mode");
                        php_stream_close(stream);
                        stream = NULL;
@@ -240,8 +239,6 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, char *path, ch
 #endif
        }
 
-#endif
-
        /* send the user name */
        php_stream_write_string(stream, "USER ");
        if (resource->user != NULL) {
@@ -433,14 +430,15 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, char *path, ch
        php_stream_context_set(datastream, context);
        php_stream_notify_progress_init(context, 0, file_size);
 
-#if HAVE_OPENSSL_EXT
-       if (use_ssl_on_data && php_stream_sock_ssl_activate_with_method(datastream, 1, SSLv23_method(), reuseid TSRMLS_CC) == FAILURE)  {
+       if (use_ssl_on_data && (php_stream_xport_crypto_setup(stream,
+                       STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL TSRMLS_CC) < 0 ||
+                       php_stream_xport_crypto_enable(stream, 1 TSRMLS_CC) < 0)) {
+
                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "Unable to activate SSL mode");
                php_stream_close(datastream);
                datastream = NULL;
                goto errexit;
        }
-#endif
 
        /* remember control stream */   
        datastream->wrapperdata = (zval *)stream;
index e6da65788277436cd45ca61671b02edb8d2a3e14..dbe7340a9a9e2691d026253f09d5b02329bb8f99 100644 (file)
@@ -99,6 +99,8 @@ php_stream *php_stream_url_wrap_http(php_stream_wrapper *wrapper, char *path, ch
        char tmp_line[128];
        size_t chunk_size = 0, file_size = 0;
        int eol_detect;
+       char *transport_string, *errstr = NULL;
+       int transport_len;
 
        if (strpbrk(mode, "awx+")) {
                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "HTTP wrapper does not support writeable connections.");
@@ -110,14 +112,26 @@ php_stream *php_stream_url_wrap_http(php_stream_wrapper *wrapper, char *path, ch
                return NULL;
        
        use_ssl = resource->scheme && (strlen(resource->scheme) > 4) && resource->scheme[4] == 's';
-
        /* choose default ports */
        if (use_ssl && resource->port == 0)
                resource->port = 443;
        else if (resource->port == 0)
                resource->port = 80;
 
-       stream = php_stream_sock_open_host(resource->host, resource->port, SOCK_STREAM, NULL, 0);
+       transport_len = spprintf(&transport_string, 0, "%s://%s:%d", use_ssl ? "ssl" : "tcp", resource->host, resource->port);
+
+       stream = php_stream_xport_create(transport_string, transport_len, options,
+                       STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT,
+                       NULL, NULL, context, &errstr, NULL);
+                       
+       if (errstr) {
+               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "%s", errstr);
+               efree(errstr);
+               errstr = NULL;
+       }
+
+       efree(transport_string);
+
        if (stream == NULL)     
                goto out;
 
@@ -133,17 +147,6 @@ php_stream *php_stream_url_wrap_http(php_stream_wrapper *wrapper, char *path, ch
        php_stream_context_set(stream, context);
 
        php_stream_notify_info(context, PHP_STREAM_NOTIFY_CONNECT, NULL, 0);
-       
-#if HAVE_OPENSSL_EXT
-       if (use_ssl)    {
-               if (php_stream_sock_ssl_activate(stream, 1) == FAILURE) {
-                       php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "Unable to activate SSL mode");
-                       php_stream_close(stream);
-                       stream = NULL;
-                       goto out;
-               }
-       }
-#endif
 
        scratch_len = strlen(path) + 32;
        scratch = emalloc(scratch_len);
index 8a7f3da006caaa0d7e63fe23e5f6e742d7e5f33c..905af64813c1337d4f6811587b8eb46c75260681 100644 (file)
 #include <fcntl.h>
 #endif
 
-#ifdef HAVE_OPENSSL_EXT
-#include <openssl/err.h>
-#endif
-
 #ifdef HAVE_SYS_SELECT_H
 #include <sys/select.h>
 #endif
@@ -167,100 +163,228 @@ static void php_network_freeaddresses(struct sockaddr **sal)
 /* {{{ php_network_getaddresses
  * Returns number of addresses, 0 for none/error
  */
-static int php_network_getaddresses(const char *host, struct sockaddr ***sal TSRMLS_DC)
+static int php_network_getaddresses(const char *host, struct sockaddr ***sal, char **error_string TSRMLS_DC)
 {
        struct sockaddr **sap;
        int n;
+#ifdef HAVE_GETADDRINFO
+       struct addrinfo hints, *res, *sai;
+#else
+       struct hostent *host_info;
+       struct in_addr in;
+#endif
 
        if (host == NULL) {
                return 0;
        }
 
-       {
 #ifdef HAVE_GETADDRINFO
-               struct addrinfo hints, *res, *sai;
+       memset(&hints, '\0', sizeof(hints));
 
-               memset(&hints, '\0', sizeof(hints));
-#  ifdef HAVE_IPV6
-               hints.ai_family = AF_UNSPEC;
-#  else
-               hints.ai_family = AF_INET;
-#  endif
-               if ((n = getaddrinfo(host, NULL, &hints, &res))) {
-                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_network_getaddresses: getaddrinfo failed: %s", PHP_GAI_STRERROR(n));
-                       return 0;
-               } else if (res == NULL) {
-                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_network_getaddresses: getaddrinfo failed (null result pointer)");
-                       return 0;
+# ifdef HAVE_IPV6
+       hints.ai_family = AF_UNSPEC;
+# else
+       hints.ai_family = AF_INET;
+# endif
+       
+       if ((n = getaddrinfo(host, NULL, &hints, &res)) || res == NULL) {
+               char *str = res == NULL ? "null result pointer" : PHP_GAI_STRERROR(n);
+
+               if (error_string) {
+                       spprintf(error_string, 0, "getaddrinfo: %s", str);
+               } else {
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_network_getaddresses: getaddrinfo failed: %s", str);
                }
+               return 0;
+       }
 
-               sai = res;
-               for (n = 1; (sai = sai->ai_next) != NULL; n++);
-               *sal = emalloc((n + 1) * sizeof(*sal));
-               sai = res;
-               sap = *sal;
-               do {
-                       switch (sai->ai_family) {
-#  ifdef HAVE_IPV6
+       sai = res;
+
+       for (n = 1; (sai = sai->ai_next) != NULL; n++) {
+               ;
+       }
+               
+       *sal = emalloc((n + 1) * sizeof(*sal));
+       sai = res;
+       sap = *sal;
+       do {
+               switch (sai->ai_family) {
+# if HAVE_IPV6
                        case AF_INET6:
                                *sap = emalloc(sizeof(struct sockaddr_in6));
                                *(struct sockaddr_in6 *)*sap =
                                        *((struct sockaddr_in6 *)sai->ai_addr);
                                sap++;
                                break;
-#  endif
+# endif
                        case AF_INET:
                                *sap = emalloc(sizeof(struct sockaddr_in));
                                *(struct sockaddr_in *)*sap =
                                        *((struct sockaddr_in *)sai->ai_addr);
                                sap++;
                                break;
-                       }
-               } while ((sai = sai->ai_next) != NULL);
-               freeaddrinfo(res);
+               }
+       } while ((sai = sai->ai_next) != NULL);
+       freeaddrinfo(res);
 #else
-               struct hostent *host_info;
-               struct in_addr in;
-
-               if (!inet_aton(host, &in)) {
-                       /* XXX NOT THREAD SAFE
-                        * (but it *is* thread safe under win32)
-                        */
-                       host_info = gethostbyname(host);
-                       if (host_info == NULL) {
-                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_network_getaddresses: gethostbyname failed");
-                               return 0;
-                       }
-                       in = *((struct in_addr *) host_info->h_addr);
+
+       if (!inet_aton(host, &in)) {
+               /* XXX NOT THREAD SAFE
+                * (but it *is* thread safe under win32)
+                */
+               host_info = gethostbyname(host);
+               if (host_info == NULL) {
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_network_getaddresses: gethostbyname failed");
+                       return 0;
                }
+               in = *((struct in_addr *) host_info->h_addr);
+       }
 
-               *sal = emalloc(2 * sizeof(*sal));
-               sap = *sal;
-               *sap = emalloc(sizeof(struct sockaddr_in));
-               (*sap)->sa_family = AF_INET;
-               ((struct sockaddr_in *)*sap)->sin_addr = in;
-               sap++;
-               n = 1;
+       *sal = emalloc(2 * sizeof(*sal));
+       sap = *sal;
+       *sap = emalloc(sizeof(struct sockaddr_in));
+       (*sap)->sa_family = AF_INET;
+       ((struct sockaddr_in *)*sap)->sin_addr = in;
+       sap++;
+       n = 1;
 #endif
-       }
+       
        *sap = NULL;
        return n;
 }
 /* }}} */
 
+#ifndef O_NONBLOCK
+#define O_NONBLOCK O_NDELAY
+#endif
+
+#if !defined(__BEOS__)
+# define HAVE_NON_BLOCKING_CONNECT 1
+# ifdef PHP_WIN32
+typedef u_long php_non_blocking_flags_t;
+#  define SET_SOCKET_BLOCKING_MODE(sock, save) \
+     save = TRUE; ioctlsocket(sock, FIONBIO, &save)
+#  define RESTORE_SOCKET_BLOCKING_MODE(sock, save) \
+        ioctlsocket(sock, FIONBIO, &save)
+# else
+typedef int php_non_blocking_flags_t;
+#  define SET_SOCKET_BLOCKING_MODE(sock, save) \
+        save = fcntl(sock, F_GETFL, 0); \
+        fcntl(sock, F_SETFL, save | O_NONBLOCK)
+#  define RESTORE_SOCKET_BLOCKING_MODE(sock, save) \
+        fcntl(sock, F_SETFL, save)
+# endif
+#endif
+
+
+
+
+/* Connect to a socket using an interruptible connect with optional timeout.
+ * Optionally, the connect can be made asynchronously, which will implicitly
+ * enable non-blocking mode on the socket.
+ * */
+/* {{{ php_network_connect_socket */
+PHPAPI int php_network_connect_socket(int sockfd,
+               const struct sockaddr *addr,
+               socklen_t addrlen,
+               int asynchronous,
+               struct timeval *timeout,
+               char **error_string,
+               int *error_code)
+{
+#if HAVE_NON_BLOCKING_CONNECT
+       php_non_blocking_flags_t orig_flags;
+       int n;
+       int error = 0;
+       socklen_t len;
+       int ret = 0;
+       fd_set rset;
+       fd_set wset;
+       fd_set eset;
+
+       SET_SOCKET_BLOCKING_MODE(sockfd, orig_flags);
+       
+       if ((n = connect(sockfd, addr, addrlen)) < 0) {
+               error = php_socket_errno();
+
+               if (error_code) {
+                       *error_code = error;
+               }
+
+               if (error != EINPROGRESS) {
+                       if (error_string) {
+                               *error_string = php_socket_strerror(error, NULL, 0);
+                       }
+
+                       return -1;
+               }
+               if (asynchronous && error == EINPROGRESS) {
+                       /* this is fine by us */
+                       return 0;
+               }
+       }
+
+       if (n == 0) {
+               goto ok;
+       }
+
+       FD_ZERO(&rset);
+       FD_ZERO(&eset);
+       FD_SET(sockfd, &rset);
+       FD_SET(sockfd, &eset);
+
+       wset = rset;
+
+       if ((n = select(sockfd + 1, &rset, &wset, &eset, timeout)) == 0) {
+               error = ETIMEDOUT;
+       }
+
+       if(FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) {
+               len = sizeof(error);
+               /*
+                  BSD-derived systems set errno correctly
+                  Solaris returns -1 from getsockopt in case of error
+                  */
+               if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
+                       ret = -1;
+               }
+       } else {
+               /* whoops: sockfd has disappeared */
+               ret = -1;
+       }
+
+ok:
+       if (!asynchronous) {
+               /* back to blocking mode */
+               RESTORE_SOCKET_BLOCKING_MODE(sockfd, orig_flags);
+       }
+
+       if (error_code) {
+               *error_code = error;
+       }
+
+       if (error && error_string) {
+               *error_string = php_socket_strerror(error, NULL, 0);
+               ret = -1;
+       }
+       return ret;
+#else
+       if (asynchronous) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Asynchronous connect() not supported on this platform");
+       }
+       return connect(sockfd, addr, addrlen);
+#endif
+}
+/* }}} */
+
 /* {{{ php_connect_nonb */
 PHPAPI int php_connect_nonb(int sockfd,
                                                const struct sockaddr *addr,
                                                socklen_t addrlen,
                                                struct timeval *timeout)
 {
-       /* probably won't work on Win32, someone else might try it (read: fix it ;) */
-
 #if (!defined(__BEOS__) && !defined(PHP_WIN32)) && (defined(O_NONBLOCK) || defined(O_NDELAY))
 
-#ifndef O_NONBLOCK
-#define O_NONBLOCK O_NDELAY
-#endif
 
        int flags;
        int n;
@@ -395,6 +519,144 @@ PHPAPI int php_connect_nonb_win32(SOCKET sockfd,
 /* }}} */
 #endif
 
+/* {{{ sub_times */
+static inline void sub_times(struct timeval a, struct timeval b, struct timeval *result)
+{
+       result->tv_usec = a.tv_usec - b.tv_usec;
+       if (result->tv_usec < 0L) {
+               a.tv_sec--;
+               result->tv_usec += 1000000L;
+       }
+       result->tv_sec = a.tv_sec - b.tv_sec;
+       if (result->tv_sec < 0L) {
+               result->tv_sec++;
+               result->tv_usec -= 1000000L;
+       }
+}
+/* }}} */
+
+/* Connect to a remote host using an interruptible connect with optional timeout.
+ * Optionally, the connect can be made asynchronously, which will implicitly
+ * enable non-blocking mode on the socket.
+ * Returns the connected (or connecting) socket, or -1 on failure.
+ * */
+
+/* {{{ php_network_connect_socket_to_host */
+int php_network_connect_socket_to_host(const char *host, unsigned short port,
+               int socktype, int asynchronous, struct timeval *timeout, char **error_string,
+               int *error_code
+               TSRMLS_DC)
+{
+       int num_addrs, sock, n, fatal = 0;
+       struct sockaddr **sal, **psal, *sa;
+       struct timeval working_timeout;
+       socklen_t socklen;
+#if HAVE_GETTIMEOFDAY
+       struct timeval limit_time, time_now;
+#endif
+
+       num_addrs = php_network_getaddresses(host, &psal, error_string TSRMLS_CC);
+
+       if (num_addrs == 0) {
+               /* could not resolve address(es) */
+               return -1;
+       }
+
+       if (timeout) {
+               memcpy(&working_timeout, timeout, sizeof(working_timeout));
+#if HAVE_GETTIMEOFDAY
+               gettimeofday(&limit_time, NULL);
+               limit_time.tv_sec += working_timeout.tv_sec;
+               limit_time.tv_usec += working_timeout.tv_usec;
+               if (limit_time.tv_usec >= 1000000) {
+                       limit_time.tv_usec -= 1000000;
+                       limit_time.tv_sec++;
+               }
+#endif
+       }
+
+       for (sal = psal; !fatal && *sal != NULL; sal++) {
+               sa = *sal;
+
+               /* create a socket for this address */
+               sock = socket(sa->sa_family, socktype, 0);
+
+               if (sock == SOCK_ERR) {
+                       continue;
+               }
+
+               switch (sa->sa_family) {
+#if HAVE_GETADDRINFO && HAVE_IPV6
+                       case AF_INET6:
+                               ((struct sockaddr_in6 *)sa)->sin6_family = sa->sa_family;
+                               ((struct sockaddr_in6 *)sa)->sin6_port = htons(port);
+                               socklen = sizeof(struct sockaddr_in6);
+                               break;
+#endif
+                       case AF_INET:
+                               ((struct sockaddr_in *)sa)->sin_family = sa->sa_family;
+                               ((struct sockaddr_in *)sa)->sin_port = htons(port);
+                               socklen = sizeof(struct sockaddr_in);
+                               break;
+                       default:
+                               /* Unknown family */
+                               sa = NULL;
+               }
+
+               if (sa) {
+                       /* make a connection attempt */
+                       
+                       n = php_network_connect_socket(sock, sa, socklen, asynchronous,
+                                       timeout ? &working_timeout : NULL,
+                                       error_string, error_code);
+
+                       if (n != SOCK_CONN_ERR) {
+                               goto connected;
+                       }
+
+                       /* adjust timeout for next attempt */
+#if HAVE_GETTIMEOFDAY
+                       if (timeout) {
+                               gettimeofday(&time_now, NULL);
+
+                               if (timercmp(&time_now, &limit_time, >=)) {
+                                       /* time limit expired; don't attempt any further connections */
+                                       fatal = 1;
+                               } else {
+                                       /* work out remaining time */
+                                       sub_times(limit_time, time_now, &working_timeout);
+                               }
+                       }
+#else
+                       if (err == PHP_TIMEOUT_ERROR_VALUE) {
+                               /* Don't even bother trying to connect to the next alternative;
+                                * we have no way to determine how long we have already taken
+                                * and it is quite likely that the next attempt will fail too. */
+                               fatal = 1;
+                       } else {
+                               /* re-use the same initial timeout.
+                                * Not the best thing, but in practice it should be good-enough */
+                               if (timeout) {
+                                       memcpy(&working_timeout, timeout, sizeof(working_timeout));
+                               }
+                       }
+#endif
+               }
+
+               close(sock);
+       }
+       sock = -1;
+
+connected:
+
+       php_network_freeaddresses(psal);
+       
+       return sock;
+}
+/* }}} */
+
+
+
 /* {{{ php_hostconnect
  * Creates a socket of type socktype and connects to the given host and
  * port, returns the created socket on success, else returns -1.
@@ -408,7 +670,7 @@ int php_hostconnect(const char *host, unsigned short port, int socktype, struct
        int set_timeout = 0;
        int err;
        
-       n = php_network_getaddresses(host, &sal TSRMLS_CC);
+       n = php_network_getaddresses(host, &sal, NULL TSRMLS_CC);
 
        if (n == 0)
                return -1;
@@ -546,6 +808,12 @@ int php_sockaddr_size(php_sockaddr_storage *addr)
 }
 /* }}} */
 
+/* Given a socket error code, if buf == NULL:
+ *   emallocs storage for the error message and returns
+ * else
+ *   sprintf message into provided buffer and returns buf
+ */
+/* {{{ php_socket_strerror */
 PHPAPI char *php_socket_strerror(long err, char *buf, size_t bufsize)
 {
 #ifndef PHP_WIN32
@@ -589,7 +857,9 @@ PHPAPI char *php_socket_strerror(long err, char *buf, size_t bufsize)
        return buf;
 #endif
 }
+/* }}} */
 
+/* deprecated */
 PHPAPI php_stream *_php_stream_sock_open_from_socket(int socket, const char *persistent_id STREAMS_DC TSRMLS_DC)
 {
        php_stream *stream;
@@ -603,7 +873,7 @@ PHPAPI php_stream *_php_stream_sock_open_from_socket(int socket, const char *per
        sock->timeout.tv_usec = 0;
        sock->socket = socket;
 
-       stream = php_stream_alloc_rel(&php_stream_socket_ops, sock, persistent_id, "r+");
+       stream = php_stream_alloc_rel(&php_stream_generic_socket_ops, sock, persistent_id, "r+");
        stream->flags |= PHP_STREAM_FLAG_AVOID_BLOCKING;
 
        if (stream == NULL)     
@@ -615,125 +885,20 @@ PHPAPI php_stream *_php_stream_sock_open_from_socket(int socket, const char *per
 PHPAPI php_stream *_php_stream_sock_open_host(const char *host, unsigned short port,
                int socktype, struct timeval *timeout, const char *persistent_id STREAMS_DC TSRMLS_DC)
 {
-       int socket;
+       char *res;
+       long reslen;
        php_stream *stream;
 
-       socket = php_hostconnect(host, port, socktype, timeout TSRMLS_CC);
-
-       if (socket == -1)
-               return NULL;
-
-       stream = php_stream_sock_open_from_socket_rel(socket, persistent_id);
-
-       if (stream == NULL)
-               closesocket(socket);
+       reslen = spprintf(&res, 0, "tcp://%s:%d", host, port);
 
-       return stream;
-}
-
-PHPAPI php_stream *_php_stream_sock_open_unix(const char *path, int pathlen, const char *persistent_id,
-               struct timeval *timeout STREAMS_DC TSRMLS_DC)
-{
-#if defined(AF_UNIX)
-       int socketd;
-       struct  sockaddr_un unix_addr;
-       php_stream *stream;
+       stream = php_stream_xport_create(res, reslen, ENFORCE_SAFE_MODE | REPORT_ERRORS,
+                       STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT, persistent_id, timeout, NULL, NULL, NULL);
 
-       socketd = socket(PF_UNIX, SOCK_STREAM, 0);
-       if (socketd == SOCK_ERR)
-               return NULL;
-
-       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 (pathlen >= 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. */
-               pathlen = sizeof(unix_addr.sun_path) - 1;
-       }
-       
-       memcpy(unix_addr.sun_path, path, pathlen);
+       efree(res);
 
-       if (php_connect_nonb(socketd, (struct sockaddr *) &unix_addr, sizeof(unix_addr), timeout) == SOCK_CONN_ERR) 
-               return NULL;
-
-       stream = php_stream_sock_open_from_socket_rel(socketd, persistent_id);
-       if (stream == NULL)
-               closesocket(socketd);
        return stream;
-#else
-       return NULL;
-#endif
-}
-
-#if HAVE_OPENSSL_EXT
-PHPAPI int php_stream_sock_ssl_activate_with_method(php_stream *stream, int activate, SSL_METHOD *method, php_stream *session_stream TSRMLS_DC)
-{
-       php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
-       php_netstream_data_t *psock = NULL;
-       SSL_CTX *ctx = NULL;
-
-
-       if (!php_stream_is(stream, PHP_STREAM_IS_SOCKET)) {
-               php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_stream_sock_ssl_activate_with_method: stream is not a network stream");
-               return FAILURE;
-       }
-
-       if (session_stream) {
-               if (!php_stream_is(session_stream, PHP_STREAM_IS_SOCKET)) {
-                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_stream_sock_ssl_activate_with_method: session_stream is not a network stream");
-                       return FAILURE;
-               }
-               psock = (php_netstream_data_t*)session_stream->abstract;
-       }
-       
-       if (activate == sock->ssl_active)
-               return SUCCESS; /* already in desired mode */
-       
-       if (activate && sock->ssl_handle == NULL)       {
-               ctx = SSL_CTX_new(method);
-               if (ctx == NULL) {
-                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_stream_sock_ssl_activate_with_method: failed to create an SSL context");
-                       return FAILURE;
-               }
-
-               sock->ssl_handle = SSL_new(ctx);
-               if (sock->ssl_handle == NULL)   {
-                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_stream_sock_ssl_activate_with_method: failed to create an SSL handle");
-                       SSL_CTX_free(ctx);
-                       return FAILURE;
-               }
-               
-               SSL_set_fd(sock->ssl_handle, sock->socket);
-               
-               if (psock) {
-                       SSL_copy_session_id(sock->ssl_handle, psock->ssl_handle);
-               }
-       }
-
-       if (activate)   {
-               if (SSL_connect(sock->ssl_handle) <= 0) {
-                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_stream_sock_ssl_activate_with_method: SSL handshake/connection failed");
-                       SSL_shutdown(sock->ssl_handle);
-                       return FAILURE;
-               }
-               sock->ssl_active = activate;
-       }
-       else    {
-               SSL_shutdown(sock->ssl_handle);
-               sock->ssl_active = 0;
-       }
-       return SUCCESS;
 }
 
-#endif
-
-
 PHPAPI int php_set_sock_blocking(int socketd, int block TSRMLS_DC)
 {
       int ret = SUCCESS;
@@ -764,347 +929,6 @@ PHPAPI int php_set_sock_blocking(int socketd, int block TSRMLS_DC)
       return ret;
 }
 
-#if HAVE_OPENSSL_EXT
-static int handle_ssl_error(php_stream *stream, int nr_bytes TSRMLS_DC)
-{
-       php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
-       int err = SSL_get_error(sock->ssl_handle, nr_bytes);
-       char esbuf[512];
-       char *ebuf = NULL, *wptr = NULL;
-       size_t ebuf_size = 0;
-       unsigned long code;
-       int retry = 1;
-
-       switch(err) {
-               case SSL_ERROR_ZERO_RETURN:
-                       /* SSL terminated (but socket may still be active) */
-                       retry = 0;
-                       break;
-               case SSL_ERROR_WANT_READ:
-               case SSL_ERROR_WANT_WRITE:
-                       /* re-negotiation, or perhaps the SSL layer needs more
-                        * packets: retry in next iteration */
-                       break;
-               case SSL_ERROR_SYSCALL:
-                       if (ERR_peek_error() == 0) {
-                               if (nr_bytes == 0) {
-                                       php_error_docref(NULL TSRMLS_CC, E_WARNING,
-                                                       "SSL: fatal protocol error");
-                                       stream->eof = 1;
-                                       retry = 0;
-                               } else {
-                                       char *estr = php_socket_strerror(php_socket_errno(), NULL, 0);
-
-                                       php_error_docref(NULL TSRMLS_CC, E_WARNING,
-                                                       "SSL: %s", estr);
-
-                                       efree(estr);
-                                       retry = 0;
-                               }
-                               break;
-                       }
-                       /* fall through */
-               default:
-                       /* some other error */
-                       while ((code = ERR_get_error()) != 0) {
-                               /* allow room for a NUL and an optional \n */
-                               if (ebuf) {
-                                       esbuf[0] = '\n';
-                                       esbuf[1] = '\0';
-                                       ERR_error_string_n(code, esbuf + 1, sizeof(esbuf) - 2);
-                               } else {
-                                       esbuf[0] = '\0';
-                                       ERR_error_string_n(code, esbuf, sizeof(esbuf) - 1);
-                               }
-                               code = strlen(esbuf);
-                               esbuf[code] = '\0';
-
-                               ebuf = erealloc(ebuf, ebuf_size + code + 1);
-                               if (wptr == NULL) {
-                                       wptr = ebuf;
-                               }       
-
-                               /* also copies the NUL */
-                               memcpy(wptr, esbuf, code + 1);
-                               wptr += code;
-                       }
-
-                       php_error_docref(NULL TSRMLS_CC, E_WARNING,
-                                       "SSL operation failed with code %d.%s%s",
-                                       err,
-                                       ebuf ? "OpenSSL Error messages:\n" : "",
-                                       ebuf ? ebuf : "");
-                               
-                       retry = 0;
-       }
-       return retry;
-}
-#endif
-
-
-
-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 HAVE_OPENSSL_EXT
-       if (sock->ssl_active) {
-               int retry = 1;
-
-               do {
-                       didwrite = SSL_write(sock->ssl_handle, buf, count);
-
-                       if (didwrite <= 0) {
-                               retry = handle_ssl_error(stream, didwrite TSRMLS_CC);
-                       } else {
-                               break;
-                       }
-               } while(retry);
-               
-       } else
-#endif
-       {
-               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;
-}
-
-#if ZEND_DEBUG && DEBUG_MAIN_NETWORK
-static inline void dump_sock_state(char *msg, php_netstream_data_t *sock TSRMLS_DC)
-{
-       printf("%s: blocked=%d timeout_event=%d eof=%d inbuf=%d timeout=%d\n", msg, sock->is_blocked, sock->timeout_event, sock->eof, TOREAD(sock), sock->timeout);
-}
-# define DUMP_SOCK_STATE(msg, sock)    dump_sock_state(msg, sock TSRMLS_CC)
-#else
-# define DUMP_SOCK_STATE(msg, sock)    /* nothing */
-#endif
-
-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;
-
-       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;
-
-DUMP_SOCK_STATE("wait_for_data", sock);
-
-               retval = select(sock->socket + 1, &tfdr, NULL, NULL, ptimeout);
-
-               if (retval == 0)
-                       sock->timeout_event = 1;
-
-               if (retval >= 0)
-                       break;
-       }
-DUMP_SOCK_STATE("wait_for_data: done", sock);
-}
-
-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;
-       size_t nr_bytes = 0;
-
-#if HAVE_OPENSSL_EXT
-       if (sock->ssl_active) {
-               int retry = 1;
-
-               do {
-                       nr_bytes = SSL_read(sock->ssl_handle, buf, count);
-
-                       if (nr_bytes <= 0) {
-                               retry = handle_ssl_error(stream, nr_bytes TSRMLS_CC);
-                               if (retry == 0 && !SSL_pending(sock->ssl_handle)) {
-                                       stream->eof = 1;
-                               }
-                       } else {
-                               /* we got the data */
-                               break;
-                       }
-               } while (retry);
-       }
-       else
-#endif
-       {
-               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 HAVE_OPENSSL_EXT
-               if (sock->ssl_active) {
-                       SSL_shutdown(sock->ssl_handle);
-                       sock->ssl_active = 0;
-               }
-               if (sock->ssl_handle) {
-                       SSL_free(sock->ssl_handle);
-                       sock->ssl_handle = NULL;
-               }
-#endif
-
-               /* 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);
-
-       }
-
-       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;
-
-       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;
-
-               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 HAVE_OPENSSL_EXT
-                       if (sock->ssl_active)
-                               return FAILURE;
-#endif
-                       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 HAVE_OPENSSL_EXT
-                       if (sock->ssl_active)
-                               return FAILURE;
-#endif
-                       if (ret)
-                               *ret = (void*)sock->socket;
-                       return SUCCESS;
-               default:
-                       return FAILURE;
-       }
-}
-
-php_stream_ops php_stream_socket_ops = {
-       php_sockop_write, php_sockop_read,
-       php_sockop_close, php_sockop_flush,
-       "socket",
-       NULL, /* seek */
-       php_sockop_cast,
-       php_sockop_stat,
-       php_sockop_set_option,
-};
-
-
-
 
 /*
  * Local variables:
index fc322d93e6b038eb9356b60fc0f54f3a6ae78a04..57dc41f96c884e270276b48b713a9b628b5ef658 100644 (file)
@@ -40,6 +40,7 @@
 
 #ifdef PHP_WIN32
 #define EWOULDBLOCK WSAEWOULDBLOCK
+#define EINPROGRESS    WSAEWOULDBLOCK
 #      define fsync _commit
 #      define ftruncate(a, b) chsize(a, b)
 #endif /* defined(PHP_WIN32) */
@@ -109,6 +110,18 @@ typedef struct {
 } php_sockaddr_storage;
 #endif
 
+PHPAPI int php_network_connect_socket_to_host(const char *host, unsigned short port,
+               int socktype, int asynchronous, struct timeval *timeout, char **error_string,
+               int *error_code
+               TSRMLS_DC);
+
+PHPAPI int php_network_connect_socket(int sockfd,
+               const struct sockaddr *addr,
+               socklen_t addrlen,
+               int asynchronous,
+               struct timeval *timeout,
+               char **error_string,
+               int *error_code);
 
 int php_hostconnect(const char *host, unsigned short port, int socktype, struct timeval *timeout TSRMLS_DC);
 PHPAPI int php_connect_nonb(int sockfd, const struct sockaddr *addr, socklen_t addrlen, struct timeval *timeout);
@@ -125,29 +138,19 @@ struct _php_netstream_data_t      {
        char is_blocked;
        struct timeval timeout;
        char timeout_event;
-#if HAVE_OPENSSL_EXT
-       /* openssl specific bits here */
-       SSL *ssl_handle;
-       int ssl_active;
-#endif
 };
 typedef struct _php_netstream_data_t php_netstream_data_t;
-
-#define PHP_NETSTREAM_DATA_FROM_STREAM(stream)         (php_netstream_data_t*)(stream)->abstract
-
 extern php_stream_ops php_stream_socket_ops;
+extern php_stream_ops php_stream_generic_socket_ops;
 #define PHP_STREAM_IS_SOCKET   (&php_stream_socket_ops)
 
 PHPAPI php_stream *_php_stream_sock_open_from_socket(int socket, const char *persistent_id STREAMS_DC TSRMLS_DC );
 /* open a connection to a host using php_hostconnect and return a stream */
 PHPAPI php_stream *_php_stream_sock_open_host(const char *host, unsigned short port,
                int socktype, struct timeval *timeout, const char *persistent_id STREAMS_DC TSRMLS_DC);
-PHPAPI php_stream *_php_stream_sock_open_unix(const char *path, int pathlen, const char *persistent_id,
-               struct timeval *timeout STREAMS_DC TSRMLS_DC);
 
 #define php_stream_sock_open_from_socket(socket, persistent)   _php_stream_sock_open_from_socket((socket), (persistent) STREAMS_CC TSRMLS_CC)
 #define php_stream_sock_open_host(host, port, socktype, timeout, persistent)   _php_stream_sock_open_host((host), (port), (socktype), (timeout), (persistent) STREAMS_CC TSRMLS_CC)
-#define php_stream_sock_open_unix(path, pathlen, persistent, timeval)  _php_stream_sock_open_unix((path), (pathlen), (persistent), (timeval) STREAMS_CC TSRMLS_CC)
 
 /* {{{ memory debug */
 #define php_stream_sock_open_from_socket_rel(socket, persistent)       _php_stream_sock_open_from_socket((socket), (persistent) STREAMS_REL_CC TSRMLS_CC)
@@ -156,12 +159,6 @@ PHPAPI php_stream *_php_stream_sock_open_unix(const char *path, int pathlen, con
 
 /* }}} */
 
-#if HAVE_OPENSSL_EXT
-PHPAPI int php_stream_sock_ssl_activate_with_method(php_stream *stream, int activate, SSL_METHOD *method, php_stream *session_stream TSRMLS_DC);
-#define php_stream_sock_ssl_activate(stream, activate) php_stream_sock_ssl_activate_with_method((stream), (activate), SSLv23_client_method(), NULL TSRMLS_CC)
-
-#endif
-
 #endif /* _PHP_NETWORK_H */
 
 /*
index bcb6c33f474268da2a1ece3f7faae4da011528d9..008bcdb1a4af94e69f99589cda7610245592d727 100755 (executable)
@@ -332,8 +332,22 @@ PHPAPI int _php_stream_set_option(php_stream *stream, int option, int value, voi
 /* whether or not locking is supported */
 #define PHP_STREAM_LOCK_SUPPORTED              1       
 
-#define php_stream_supports_lock(stream)       php_stream_set_option((stream), PHP_STREAM_OPTION_LOCKING, 0, (void *) PHP_STREAM_LOCK_SUPPORTED TSRMLS_CC) == 0 ? 1 : 0
-#define php_stream_lock(stream, mode)          php_stream_set_option((stream), PHP_STREAM_OPTION_LOCKING, (mode), (void *) NULL TSRMLS_CC)
+#define php_stream_supports_lock(stream)       _php_stream_set_option((stream), PHP_STREAM_OPTION_LOCKING, 0, (void *) PHP_STREAM_LOCK_SUPPORTED TSRMLS_CC) == 0 ? 1 : 0
+#define php_stream_lock(stream, mode)          _php_stream_set_option((stream), PHP_STREAM_OPTION_LOCKING, (mode), (void *) NULL TSRMLS_CC)
+
+/* option code used by the php_stream_xport_XXX api */
+#define PHP_STREAM_OPTION_XPORT_API                    7 /* see php_stream_transport.h */
+#define PHP_STREAM_OPTION_CRYPTO_API           8 /* see php_stream_transport.h */
+#define PHP_STREAM_OPTION_MMAP_API                     9 /* see php_stream_mmap.h */
+#define PHP_STREAM_OPTION_TRUNCATE_API         10
+
+#define PHP_STREAM_TRUNCATE_SUPPORTED  0
+#define PHP_STREAM_TRUNCATE_SET_SIZE   1       /* ptrparam is a pointer to a size_t */
+
+#define php_stream_truncate_supported(stream)  (_php_stream_set_option((stream), PHP_STREAM_OPTION_TRUNCATE_API, PHP_STREAM_TRUNCATE_SUPPORTED, NULL TSRMLS_CC) == PHP_STREAM_OPTION_RETURN_OK ? 1 : 0)
+
+PHPAPI int _php_stream_truncate_set_size(php_stream *stream, size_t newsize TSRMLS_DC);
+#define php_stream_truncate_set_size(stream, size)     _php_stream_truncate_set_size((stream), (size) TSRMLS_CC)
 
 #define PHP_STREAM_OPTION_RETURN_OK                     0 /* option set OK */
 #define PHP_STREAM_OPTION_RETURN_ERR           -1 /* problem setting option */
@@ -356,8 +370,10 @@ PHPAPI size_t _php_stream_copy_to_mem(php_stream *src, char **buf, size_t maxlen
 PHPAPI size_t _php_stream_passthru(php_stream * src STREAMS_DC TSRMLS_DC);
 #define php_stream_passthru(stream)    _php_stream_passthru((stream) STREAMS_CC TSRMLS_CC)
 
+#include "streams/php_stream_transport.h"
 #include "streams/php_stream_plain_wrapper.h"
 #include "streams/php_stream_userspace.h"
+#include "streams/php_stream_mmap.h"
 
 /* coerce the stream into some other form */
 /* cast as a stdio FILE * */
index aa6e6d12b254b12478d48ab5501230c9c5db07e8..1ebc6af8773f6101a6f644158869d917f993aa7c 100644 (file)
@@ -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 (file)
index 0000000..791b964
--- /dev/null
@@ -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 (file)
index 0000000..ccfc767
--- /dev/null
@@ -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 (file)
index 0000000..89642b5
--- /dev/null
@@ -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
+ */
index 05ad1a0dc81856c45c197b70f670fcd768e70a80..ddf569399d6a5e6ad3676d9c35b2520ce7aa564f 100644 (file)
@@ -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);
 
index f73c2642aaf091dfc163a995acd66a097c776221..f121b8ce15599a111f6ba6ae0c534e009f4f37b5 100644 (file)
@@ -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,
index e798684d182d0b04dbe65be82c60a4f7bac77ce7..b46dcc97aa1302419b598c94d22390f252db5c30 100755 (executable)
@@ -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 (file)
index 0000000..dc8c5a9
--- /dev/null
@@ -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 (file)
index 0000000..0186170
--- /dev/null
@@ -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
+ */