]> granicus.if.org Git - php/commitdiff
New user-space functions:
authorWez Furlong <wez@php.net>
Fri, 28 Feb 2003 19:53:21 +0000 (19:53 +0000)
committerWez Furlong <wez@php.net>
Fri, 28 Feb 2003 19:53:21 +0000 (19:53 +0000)
  . stream_socket_client() - similar to fsockopen(), but more powerful.
  . stream_socket_server() - Creates a server socket.
  . stream_socket_accept() - Accept a client connection.
  . stream_socket_get_name() - Get local or remote name of socket.

Tidy up some leaks and debug printfs.
Move more streams functions into streamsfuncs.c and streamsfuncs.h.

14 files changed:
NEWS
configure.in
ext/ftp/ftp.c
ext/openssl/xp_ssl.c
ext/standard/basic_functions.c
ext/standard/file.c
ext/standard/file.h
ext/standard/fsock.c
ext/standard/streamsfuncs.c
main/network.c
main/php_network.h
main/streams/php_stream_transport.h
main/streams/transports.c
main/streams/xp_socket.c

diff --git a/NEWS b/NEWS
index 8ad728668587230dda6651019efd76b0df4d1e72..86939d60cf40c61dbc39e72ff41fe78cfdc6383d 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -21,8 +21,16 @@ PHP 4                                                                      NEWS
   of the specified parameters to be passed as extra parameters to the sendmail 
   binary. These parameters will always replace the value of the 5th parameter
   to mail(), even in safe mode. (Derick)
+- Added new "transport" layer for sockets and associated functions. (Wez)
+  . stream_socket_client() - similar to fsockopen(), but more powerful.
+  . stream_socket_server() - Creates a server socket.
+  . stream_socket_accept() - Accept a client connection.
+  . stream_socket_get_name() - Get local or remote name of socket.
+  . generic crypto interface for streams (supports dynamic loading of OpenSSL)
+- Added memory mapping support under win32 to improve performance of
+  readfile(), fpassthru() and some internal streams operations. (Wez)
 - Added DBA handler 'inifile' to support ini files. (Marcus)
-- Added filter support. See README.input_filter. (Rasmus)
+- Added input filter support. See README.input_filter. (Rasmus)
 - Added "session.hash_function" and "session.hash_bits_per_character". (Sascha)
 - Added lightweight streaming input abstraction to the Zend Engine scanners
   that provides uniform support for include()'ing data from PHP streams across
index 7232d34eabecc6fc4ecd2ec6c73d2e94a9e0616b..eabade523b11c6761d236f20c75649594156a69b 100644 (file)
@@ -461,6 +461,8 @@ getservbyport \
 getrusage \
 gettimeofday \
 gmtime_r \
+inet_ntoa \
+inet_ntop \
 isascii \
 link \
 localtime_r \
index 8ceb938c7d561a9bd0c6542c9b075b9069fe4242..4601a6e321db9ec26d65ffe58be0177018c8200f 100644 (file)
@@ -135,7 +135,9 @@ ftp_open(const char *host, short port, long timeout_sec TSRMLS_DC)
        tv.tv_sec = timeout_sec;
        tv.tv_usec = 0;
 
-       ftp->fd = php_hostconnect(host, (unsigned short) (port ? port : 21), SOCK_STREAM, &tv TSRMLS_CC);
+       ftp->fd = php_network_connect_socket_to_host(host,
+                       (unsigned short) (port ? port : 21), SOCK_STREAM,
+                       0, &tv, NULL, NULL TSRMLS_CC);
        if (ftp->fd == -1) {
                goto bail;
        }
index 4e9a0ac11a9e60297c166beb91ac619bf490606c..69f11a287e06141faa8581e27467744e6896f23f 100644 (file)
@@ -288,7 +288,6 @@ static inline int php_openssl_setup_crypto(php_stream *stream,
                        method = TLSv1_server_method();
                        break;
                default:
-                       printf("unknown method\n");
                        return -1;
 
        }
@@ -307,19 +306,16 @@ static inline int php_openssl_setup_crypto(php_stream *stream,
        }
 
        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;
 }
 
@@ -337,13 +333,11 @@ static inline int php_openssl_enable_crypto(php_stream *stream,
 
                                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;
                        }
@@ -359,6 +353,47 @@ static inline int php_openssl_enable_crypto(php_stream *stream,
        return -1;
 }
 
+static inline int php_openssl_tcp_sockop_accept(php_stream *stream, php_openssl_netstream_data_t *sock,
+               php_stream_xport_param *xparam STREAMS_DC TSRMLS_DC)
+{
+       int clisock;
+
+       xparam->outputs.client = NULL;
+
+       clisock = php_network_accept_incoming(sock->s.socket,
+                       xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
+                       xparam->want_textaddr ? &xparam->outputs.textaddrlen : NULL,
+                       xparam->want_addr ? &xparam->outputs.addr : NULL,
+                       xparam->want_addr ? &xparam->outputs.addrlen : NULL,
+                       xparam->inputs.timeout,
+                       xparam->want_errortext ? &xparam->outputs.error_text : NULL,
+                       &xparam->outputs.error_code
+                       TSRMLS_CC);
+
+       if (clisock >= 0) {
+               php_openssl_netstream_data_t *clisockdata;
+
+               clisockdata = pemalloc(sizeof(*clisockdata), stream->is_persistent);
+
+               if (clisockdata == NULL) {
+                       close(clisock);
+                       /* technically a fatal error */
+               } else {
+                       /* copy underlying tcp fields */
+                       memset(clisockdata, 0, sizeof(*clisockdata));
+                       memcpy(clisockdata, sock, sizeof(clisockdata->s));
+
+                       clisockdata->s.socket = clisock;
+                       
+                       xparam->outputs.client = php_stream_alloc_rel(stream->ops, clisockdata, NULL, "r+");
+                       if (xparam->outputs.client) {
+                               xparam->outputs.client->context = stream->context;
+                       }
+               }
+       }
+       
+       return xparam->outputs.client == NULL ? -1 : 0;
+}
 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;
@@ -403,6 +438,14 @@ static int php_openssl_sockop_set_option(php_stream *stream, int option, int val
                                        }
                                        return PHP_STREAM_OPTION_RETURN_OK;
                                        break;
+
+                               case STREAM_XPORT_OP_ACCEPT:
+                                       /* we need to copy the additional fields that the underlying tcp transport
+                                        * doesn't know about */
+                                       xparam->outputs.returncode = php_openssl_tcp_sockop_accept(stream, sslsock, xparam STREAMS_CC TSRMLS_CC);
+                                       return PHP_STREAM_OPTION_RETURN_OK;
+                                       break;
+
                                default:
                                        /* fall through */
                                        break;
@@ -464,7 +507,7 @@ PHPAPI php_stream *php_openssl_ssl_socket_factory(const char *proto, long protol
        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));
+       memset(sslsock, 0, sizeof(*sslsock));
 
        sslsock->s.is_blocked = 1;
        sslsock->s.timeout.tv_sec = FG(default_socket_timeout);
@@ -488,6 +531,8 @@ PHPAPI php_stream *php_openssl_ssl_socket_factory(const char *proto, long protol
                sslsock->enable_on_connect = 1;
                sslsock->method = STREAM_CRYPTO_METHOD_TLS_CLIENT;
        }
+
+printf("enable_on_connect = %d  --> proto %s\n", sslsock->enable_on_connect, proto);
        
        return stream;
 }
index 56d25da5e27c0d71c6b999d3bea5eef2c87f112a..d9ef2ebb6c2b30a5e33b409d67c6e8b06c14bad5 100644 (file)
@@ -96,8 +96,8 @@ int basic_globals_id;
 php_basic_globals basic_globals;
 #endif
 
-
 #include "php_fopen_wrappers.h"
+#include "streamsfuncs.h"
 
 static unsigned char first_and_second__args_force_ref[] = { 2, BYREF_FORCE, BYREF_FORCE };
 static unsigned char second_and_third_args_force_ref[] = { 3, BYREF_NONE, BYREF_FORCE, BYREF_FORCE };
@@ -679,6 +679,10 @@ function_entry basic_functions[] = {
        PHP_FE(stream_context_get_options,                                                                              NULL)
        PHP_FE(stream_filter_prepend,                                                                                   NULL)
        PHP_FE(stream_filter_append,                                                                                    NULL)
+       PHP_FE(stream_socket_client,                             second_and_third_args_force_ref)
+       PHP_FE(stream_socket_server,                             second_and_third_args_force_ref)
+       PHP_FE(stream_socket_accept,                                               third_arg_force_ref)
+       PHP_FE(stream_socket_get_name,                                                                                  NULL)
        PHP_FE(fgetcsv,                                                                                                                 NULL)
        PHP_FE(flock,                                                                                                                   NULL)
        PHP_FE(get_meta_tags,                                                                                                   NULL)
index 280b49b7893f67d519168f67084f80b9bf23e94b..54472d4bf5d308a5111e3da0754265641579ae4d 100644 (file)
@@ -82,7 +82,7 @@
 #endif
 #include "fsock.h"
 #include "fopen_wrappers.h"
-#include "php_streams.h"
+#include "streamsfuncs.h"
 #include "php_globals.h"
 
 #ifdef HAVE_SYS_FILE_H
@@ -187,6 +187,12 @@ PHP_MINIT_FUNCTION(file)
        REGISTER_LONG_CONSTANT("STREAM_FILTER_WRITE",                   PHP_STREAM_FILTER_WRITE,                        CONST_CS | CONST_PERSISTENT);
        REGISTER_LONG_CONSTANT("STREAM_FILTER_ALL",                             PHP_STREAM_FILTER_ALL,                          CONST_CS | CONST_PERSISTENT);
 
+       REGISTER_LONG_CONSTANT("STREAM_CLIENT_PERSISTENT",              PHP_STREAM_CLIENT_PERSISTENT,           CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("STREAM_CLIENT_ASYNC_CONNECT",   PHP_STREAM_CLIENT_ASYNC_CONNECT,        CONST_CS | CONST_PERSISTENT);
+
+       REGISTER_LONG_CONSTANT("STREAM_SERVER_BIND",                    STREAM_XPORT_BIND,                                      CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("STREAM_SERVER_LISTEN",                  STREAM_XPORT_LISTEN,                            CONST_CS | CONST_PERSISTENT);
+       
        REGISTER_LONG_CONSTANT("FILE_USE_INCLUDE_PATH", PHP_FILE_USE_INCLUDE_PATH, CONST_CS | CONST_PERSISTENT);
        REGISTER_LONG_CONSTANT("FILE_IGNORE_NEW_LINES", PHP_FILE_IGNORE_NEW_LINES, CONST_CS | CONST_PERSISTENT);
        REGISTER_LONG_CONSTANT("FILE_SKIP_EMPTY_LINES", PHP_FILE_SKIP_EMPTY_LINES, CONST_CS | CONST_PERSISTENT);
index e32eac24fdad0f3e6b22687ea118ab8efbc18b77..faa389fc674072d1b9420cae70165369d9d0fc67 100644 (file)
@@ -54,13 +54,6 @@ PHP_FUNCTION(unlink);
 PHP_FUNCTION(copy);
 PHP_FUNCTION(file);
 PHP_FUNCTION(file_get_contents);
-PHP_FUNCTION(set_socket_blocking); /* deprecated */
-PHP_FUNCTION(stream_set_blocking);
-PHP_FUNCTION(stream_select);
-PHP_FUNCTION(stream_set_timeout);
-PHP_FUNCTION(stream_set_write_buffer);
-PHP_FUNCTION(stream_get_wrappers);
-PHP_FUNCTION(stream_get_line);
 PHP_FUNCTION(get_meta_tags);
 PHP_FUNCTION(flock);
 PHP_FUNCTION(fd_set);
@@ -72,14 +65,6 @@ PHP_FUNCTION(fnmatch);
 PHP_NAMED_FUNCTION(php_if_ftruncate);
 PHP_NAMED_FUNCTION(php_if_fstat);
 
-PHP_FUNCTION(stream_get_meta_data);
-PHP_FUNCTION(stream_register_wrapper);
-PHP_FUNCTION(stream_context_create);
-PHP_FUNCTION(stream_context_set_params);
-PHP_FUNCTION(stream_context_set_option);
-PHP_FUNCTION(stream_context_get_options);
-PHP_FUNCTION(stream_filter_prepend);
-PHP_FUNCTION(stream_filter_append);
 PHP_MINIT_FUNCTION(user_streams);
 
 PHPAPI int php_le_stream_context(void);
index 8f41cf4d3f1606c50ed1f31e03159a2f8a2de9ae..ddc0812c4892f1e652f602a6e4b6e4f79cd38bc2 100644 (file)
@@ -83,7 +83,7 @@ static void php_fsockopen_stream(INTERNAL_FUNCTION_PARAMETERS, int persistent)
                efree(hostname);
        }
        if (stream == NULL) {
-               php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to connect to %s:%d", host, port);
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to connect to %s:%d (%s)", host, port, errstr == NULL ? "Unknown error" : errstr);
        }
 
        if (hashkey) {
@@ -102,6 +102,10 @@ static void php_fsockopen_stream(INTERNAL_FUNCTION_PARAMETERS, int persistent)
                }
                RETURN_FALSE;
        }
+
+       if (errstr) {
+               efree(errstr);
+       }
                
        php_stream_to_zval(stream, return_value);
 }
index 1a054fe00e92038be366cad1ca05d1a7e7d13333..110058ac263112ffabe45ccb83b1fcfd7fe6b753 100644 (file)
 #include "php_open_temporary_file.h"
 #include "ext/standard/basic_functions.h"
 #include "php_ini.h"
+#include "streamsfuncs.h"
 
 
 #ifndef PHP_WIN32
 #define php_select(m, r, w, e, t)      select(m, r, w, e, t)
 #endif
 
+static php_stream_context *decode_context_param(zval *contextresource TSRMLS_DC);
+
+/* Streams based network functions */
+
+/* {{{ proto resource stream_socket_client(string remoteaddress [, long &errcode, string &errstring, double timeout, long flags, resource context])
+   Open a client connection to a remote address */
+PHP_FUNCTION(stream_socket_client)
+{
+       char *host;
+       long host_len;
+       zval *zerrno = NULL, *zerrstr = NULL, *zcontext = NULL;
+       double timeout = FG(default_socket_timeout);
+       unsigned long conv;
+       struct timeval tv;
+       char *hashkey = NULL;
+       php_stream *stream = NULL;
+       int err;
+       long flags = 0;
+       char *errstr = NULL;
+       php_stream_context *context = NULL;
+
+       RETVAL_FALSE;
+       
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|zzd!lr", &host, &host_len, &zerrno, &zerrstr, &timeout, &flags, &zcontext) == FAILURE) {
+               RETURN_FALSE;
+       }
+       
+       if (zcontext) {
+               context = decode_context_param(zcontext TSRMLS_CC);
+       }
+
+       if (flags & PHP_STREAM_CLIENT_PERSISTENT) {
+               spprintf(&hashkey, 0, "stream_socket_client__%s", host);
+       }
+       
+       /* prepare the timeout value for use */
+       conv = (unsigned long) (timeout * 1000000.0);
+       tv.tv_sec = conv / 1000000;
+       tv.tv_usec = conv % 1000000;
+
+       if (zerrno)     {
+               zval_dtor(zerrno);
+               ZVAL_LONG(zerrno, 0);
+       }
+       if (zerrstr) {
+               zval_dtor(zerrstr);
+               ZVAL_STRING(zerrstr, "", 1);
+       }
+
+       stream = php_stream_xport_create(host, host_len, ENFORCE_SAFE_MODE | REPORT_ERRORS,
+                       STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT |
+                       (flags & PHP_STREAM_CLIENT_ASYNC_CONNECT ? STREAM_XPORT_CONNECT_ASYNC : 0),
+                       hashkey, &tv, context, &errstr, &err);
+
+       if (stream == NULL) {
+               /* host might contain binary characters */
+               char *quoted_host = php_addslashes(host, host_len, NULL, 0 TSRMLS_CC);
+               
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to connect to %s (%s)", quoted_host, errstr == NULL ? "Unknown error" : errstr);
+               efree(quoted_host);
+       }
+
+       if (hashkey) {
+               efree(hashkey);
+       }
+       
+       if (stream == NULL)     {
+               if (zerrno) {
+                       zval_dtor(zerrno);
+                       ZVAL_LONG(zerrno, err);
+               }
+               if (zerrstr && errstr) {
+                       /* no need to dup; we need to efree buf anyway */
+                       zval_dtor(zerrstr);
+                       ZVAL_STRING(zerrstr, errstr, 0);
+               } else if (errstr) {
+                       efree(errstr);
+               }
+               RETURN_FALSE;
+       }
+       
+       if (errstr) {
+               efree(errstr);
+       }
+       
+       php_stream_to_zval(stream, return_value);
+}
+/* }}} */
+
+/* {{{ proto resource stream_socket_server(string localaddress [, long &errcode, string &errstring, long flags, resource context])
+   Create a server socket bound to localaddress */
+PHP_FUNCTION(stream_socket_server)
+{
+       char *host;
+       long host_len;
+       zval *zerrno = NULL, *zerrstr = NULL, *zcontext = NULL;
+       php_stream *stream = NULL;
+       int err;
+       long flags = STREAM_XPORT_BIND | STREAM_XPORT_LISTEN;
+       char *errstr = NULL;
+       php_stream_context *context = NULL;
+
+       RETVAL_FALSE;
+       
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|zzlr", &host, &host_len, &zerrno, &zerrstr, &flags, &zcontext) == FAILURE) {
+               RETURN_FALSE;
+       }
+       
+       if (zcontext) {
+               context = decode_context_param(zcontext TSRMLS_CC);
+       }
+
+       if (zerrno)     {
+               zval_dtor(zerrno);
+               ZVAL_LONG(zerrno, 0);
+       }
+       if (zerrstr) {
+               zval_dtor(zerrstr);
+               ZVAL_STRING(zerrstr, "", 1);
+       }
+
+       stream = php_stream_xport_create(host, host_len, ENFORCE_SAFE_MODE | REPORT_ERRORS,
+                       STREAM_XPORT_SERVER | flags,
+                       NULL, NULL, context, &errstr, &err);
+
+       if (stream == NULL) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to connect to %s (%s)", host, errstr == NULL ? "Unknown error" : errstr);
+       }
+       
+       if (stream == NULL)     {
+               if (zerrno) {
+                       zval_dtor(zerrno);
+                       ZVAL_LONG(zerrno, err);
+               }
+               if (zerrstr && errstr) {
+                       /* no need to dup; we need to efree buf anyway */
+                       zval_dtor(zerrstr);
+                       ZVAL_STRING(zerrstr, errstr, 0);
+               }
+               RETURN_FALSE;
+       }
+       
+       if (errstr) {
+               efree(errstr);
+       }
+       
+       php_stream_to_zval(stream, return_value);
+
+}
+/* }}} */
+
+/* {{{ proto resource stream_socket_accept(resource serverstream, [ double timeout, string &peername ])
+   Accept a client connection from a server socket */
+PHP_FUNCTION(stream_socket_accept)
+{
+       double timeout = FG(default_socket_timeout);
+       zval *peername = NULL;
+       unsigned long conv;
+       struct timeval tv;
+       php_stream *stream = NULL, *clistream = NULL;
+       zval *zstream;
+
+       char *errstr = NULL;
+       
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|dz!", &zstream, &timeout, &peername) == FAILURE) {
+               RETURN_FALSE;
+       }
+       
+       php_stream_from_zval(stream, &zstream);
+       
+       /* prepare the timeout value for use */
+       conv = (unsigned long) (timeout * 1000000.0);
+       tv.tv_sec = conv / 1000000;
+       tv.tv_usec = conv % 1000000;
+
+       if (peername) {
+               zval_dtor(peername);
+               ZVAL_STRING(peername, "", 1);
+       }
+
+       if (0 == php_stream_xport_accept(stream, &clistream,
+                               peername ? &Z_STRVAL_P(peername) : NULL,
+                               peername ? &Z_STRLEN_P(peername) : NULL,
+                               NULL, NULL,
+                               &tv, &errstr
+                               TSRMLS_CC) && clistream) {
+
+               php_stream_to_zval(clistream, return_value);
+       } else {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "accept failed: %s", errstr ? errstr : "Unknown error");
+
+               RETVAL_FALSE;
+       }
+
+       if (errstr) {
+               efree(errstr);
+       }
+}
+/* }}} */
+
+/* {{{ proto string stream_socket_get_name(resource stream, bool want_peer)
+   Returns either the locally bound or remote name for a socket stream */
+PHP_FUNCTION(stream_socket_get_name)
+{
+       php_stream *stream;
+       zval *zstream;
+       zend_bool want_peer;
+       
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rb", &zstream, &want_peer) == FAILURE) {
+               RETURN_FALSE;
+       }
+       
+       php_stream_from_zval(stream, &zstream);
+
+       Z_TYPE_P(return_value) = IS_STRING;
+       
+       if (0 != php_stream_xport_get_name(stream, want_peer,
+                               &Z_STRVAL_P(return_value),
+                               &Z_STRLEN_P(return_value),
+                               NULL, NULL
+                               TSRMLS_CC)) {
+               RETURN_FALSE;
+       }
+}
+/* }}} */
+
 /* {{{ proto resource stream_get_meta_data(resource fp)
     Retrieves header/meta data from streams/file pointers */
 PHP_FUNCTION(stream_get_meta_data)
index cc499bb3c18a33b6fdb15c48dd08914d4bf314f8..f04447745aaea3a4aaf2fb0df2d727ee7f265cdc 100644 (file)
@@ -275,9 +275,6 @@ typedef int php_non_blocking_flags_t;
 # 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.
@@ -376,163 +373,276 @@ ok:
 }
 /* }}} */
 
-/* {{{ php_connect_nonb */
-PHPAPI int php_connect_nonb(int sockfd,
-                                               const struct sockaddr *addr,
-                                               socklen_t addrlen,
-                                               struct timeval *timeout)
+/* {{{ sub_times */
+static inline void sub_times(struct timeval a, struct timeval b, struct timeval *result)
 {
-#if (!defined(__BEOS__) && !defined(PHP_WIN32)) && (defined(O_NONBLOCK) || defined(O_NDELAY))
+       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;
+       }
+}
+/* }}} */
 
+/* Bind to a local IP address.
+ * Returns the bound socket, or -1 on failure.
+ * */
+/* {{{ php_network_bind_socket_to_local_addr */
+int php_network_bind_socket_to_local_addr(const char *host, unsigned port,
+               int socktype, char **error_string, int *error_code
+               TSRMLS_DC)
+{
+       int num_addrs, sock, n, err = 0;
+       struct sockaddr **sal, **psal, *sa;
+       socklen_t socklen;
 
-       int flags;
-       int n;
-       int error = 0;
-       socklen_t len;
-       int ret = 0;
-       fd_set rset;
-       fd_set wset;
-       fd_set eset;
+       num_addrs = php_network_getaddresses(host, &psal, error_string TSRMLS_CC);
 
-       if (timeout == NULL)    {
-               /* blocking mode */
-               return connect(sockfd, addr, addrlen);
+       if (num_addrs == 0) {
+               /* could not resolve address(es) */
+               return -1;
        }
-       
-       flags = fcntl(sockfd, F_GETFL, 0);
-       fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
 
-       if ((n = connect(sockfd, addr, addrlen)) < 0) {
-               if (errno != EINPROGRESS) {
-                       return -1;
-               }
-       }
+       for (sal = psal; *sal != NULL; sal++) {
+               sa = *sal;
 
-       if (n == 0) {
-               goto ok;
-       }
+               /* create a socket for this address */
+               sock = socket(sa->sa_family, socktype, 0);
 
-       FD_ZERO(&rset);
-       FD_ZERO(&eset);
-       FD_SET(sockfd, &rset);
-       FD_SET(sockfd, &eset);
+               if (sock == SOCK_ERR) {
+                       continue;
+               }
 
-       wset = rset;
+               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 ((n = select(sockfd + 1, &rset, &wset, &eset, timeout)) == 0) {
-               error = ETIMEDOUT;
-       }
+               if (sa) {
+                       /* attempt to bind */
+               
+#ifdef SO_REUSEADDR
+                       {
+                               int val = 1;
+                               setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
+                       }
+#endif
+                       
+                       n = bind(sock, sa, socklen);
 
-       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;
+                       if (n != SOCK_CONN_ERR) {
+                               goto bound;
+                       }
+
+                       err = php_socket_errno();
                }
-       } else {
-               /* whoops: sockfd has disappeared */
-               ret = -1;
-       }
 
-ok:
-       fcntl(sockfd, F_SETFL, flags);
+               close(sock);
+       }
+       sock = -1;
 
-       if(error) {
-               errno = error;
-               ret = -1;
+       if (error_code) {
+               *error_code = err;
        }
-       return ret;
-#else /* !defined(PHP_WIN32) && ... */
-#ifdef PHP_WIN32
-       return php_connect_nonb_win32((SOCKET) sockfd, addr, addrlen, timeout);
-#endif
-       return connect(sockfd, addr, addrlen);
-#endif
+       if (error_string) {
+               *error_string = php_socket_strerror(err, NULL, 0);
+       }
+       
+bound:
+
+       php_network_freeaddresses(psal);
+       
+       return sock;
+       
 }
 /* }}} */
 
-#ifdef PHP_WIN32
-/* {{{ php_connect_nonb_win32 */
-PHPAPI int php_connect_nonb_win32(SOCKET sockfd,
-                                               const struct sockaddr *addr,
-                                               socklen_t addrlen,
-                                               struct timeval *timeout)
+static void populate_name(
+               /* input address */
+               struct sockaddr *sa, socklen_t sl,
+               /* output readable address */
+               char **textaddr, long *textaddrlen,
+               /* output address */
+               struct sockaddr **addr,
+               socklen_t *addrlen
+               TSRMLS_DC)
 {
-       int error = 0, error_len, ret;
-       u_long non_block = TRUE, block = FALSE;
-
-       fd_set rset, wset;
-
-       if (timeout == NULL)    {
-               /* blocking mode */
-               return connect(sockfd, addr, addrlen);
+       if (addr) {
+               *addr = emalloc(sl);
+               memcpy(*addr, sa, sl);
+               *addrlen = sl;
        }
-       
-       /* Set the socket to be non-blocking */
-       ioctlsocket(sockfd, FIONBIO, &non_block);
 
-       if (connect(sockfd, addr, addrlen) == SOCKET_ERROR) {
-               if (WSAGetLastError() != WSAEWOULDBLOCK) {
-                       return SOCKET_ERROR;
-               }
-       }
+       if (textaddr) {
+#if HAVE_IPV6 && HAVE_INET_NTOP
+               char abuf[256];
+#endif
+               char *buf = NULL;
 
-       FD_ZERO(&rset);
-       FD_SET(sockfd, &rset);
+               switch (sa->sa_family) {
+                       case AF_INET:
+                               /* generally not thread safe, but it *is* thread safe under win32 */
+                               buf = inet_ntoa(((struct sockaddr_in*)sa)->sin_addr);
+                               if (buf) {
+                                       *textaddrlen = strlen(buf);
+                                       *textaddr = estrndup(buf, *textaddrlen);
+                               }
 
-       FD_ZERO(&wset);
-       FD_SET(sockfd, &wset);
+                               break;
 
-       if ((ret = select(sockfd + 1, &rset, &wset, NULL, timeout)) == 0) {
-               WSASetLastError(WSAETIMEDOUT);
-               return SOCKET_ERROR;
-       }
+#if HAVE_IPV6
+                       case AF_INET6:
+                               buf = (char*)inet_ntop(sa->sa_family, &((struct sockaddr_in6*)sa)->sin6_addr, (char *)&abuf, sizeof(abuf));
+                               if (buf) {
+                                       *textaddrlen = strlen(buf);
+                                       *textaddr = estrndup(buf, *textaddrlen);
+                               }
 
-       if (ret == SOCKET_ERROR) {
-               return SOCKET_ERROR;
-       }
+                               break;
+#endif
+#ifdef AF_UNIX
+                       case AF_UNIX:
+                               {
+                                       struct sockaddr_un *ua = (struct sockaddr_un*)sa;
+
+                                       if (ua->sun_path[0] == '\0') {
+                                               /* abstract name */
+                                               int len = strlen(ua->sun_path + 1) + 1;
+                                               *textaddrlen = len;
+                                               *textaddr = emalloc(len + 1);
+                                               memcpy(*textaddr, ua->sun_path, len);
+                                               (*textaddr)[len] = '\0';
+                                       } else {
+                                               *textaddrlen = strlen(ua->sun_path);
+                                               *textaddr = estrndup(ua->sun_path, *textaddrlen);
+                                       }
+                               }
+                               break;
+#endif
 
-       if(FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) {
-               error_len = sizeof(error);
-               if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (char *) &error, &error_len) == SOCKET_ERROR) {
-                       return SOCKET_ERROR;
                }
-       } else {
-               /* whoops: sockfd has disappeared */
-               return SOCKET_ERROR;
+
        }
+}
 
-       /* Set the socket back to blocking */
-       ioctlsocket(sockfd, FIONBIO, &block);
+PHPAPI int php_network_get_peer_name(int sock, 
+               char **textaddr, long *textaddrlen,
+               struct sockaddr **addr,
+               socklen_t *addrlen
+               TSRMLS_DC)
+{
+       php_sockaddr_storage sa;
+       socklen_t sl = sizeof(sa);
+       
+       if (getpeername(sock, (struct sockaddr*)&sa, &sl) == 0) {
+               populate_name((struct sockaddr*)&sa, sl,
+                               textaddr, textaddrlen,
+                               addr, addrlen
+                               TSRMLS_CC);
+               return 0;
+       }
+       return -1;
+}
 
-       if (error) { 
-               WSASetLastError(error);
-               return SOCKET_ERROR;
+PHPAPI int php_network_get_sock_name(int sock, 
+               char **textaddr, long *textaddrlen,
+               struct sockaddr **addr,
+               socklen_t *addrlen
+               TSRMLS_DC)
+{
+       php_sockaddr_storage sa;
+       socklen_t sl = sizeof(sa);
+       
+       if (getsockname(sock, (struct sockaddr*)&sa, &sl) == 0) {
+               populate_name((struct sockaddr*)&sa, sl,
+                               textaddr, textaddrlen,
+                               addr, addrlen
+                               TSRMLS_CC);
+               return 0;
        }
+       return -1;
 
-       return 0;
 }
-/* }}} */
-#endif
 
-/* {{{ sub_times */
-static inline void sub_times(struct timeval a, struct timeval b, struct timeval *result)
+
+/* Accept a client connection from a server socket,
+ * using an optional timeout.
+ * Returns the peer address in addr/addrlen (it will emalloc
+ * these, so be sure to efree the result).
+ * If you specify textaddr/textaddrlen, a text-printable
+ * version of the address will be emalloc'd and returned.
+ * */
+
+/* {{{ php_network_accept_incoming */
+PHPAPI int php_network_accept_incoming(int srvsock,
+               char **textaddr, long *textaddrlen,
+               struct sockaddr **addr,
+               socklen_t *addrlen,
+               struct timeval *timeout,
+               char **error_string,
+               int *error_code
+               TSRMLS_DC)
 {
-       result->tv_usec = a.tv_usec - b.tv_usec;
-       if (result->tv_usec < 0L) {
-               a.tv_sec--;
-               result->tv_usec += 1000000L;
+       int clisock = -1;
+       fd_set rset;
+       int error, n;
+       php_sockaddr_storage sa;
+       socklen_t sl;
+
+       FD_ZERO(&rset);
+       FD_SET(srvsock, &rset);
+               
+       n = select(srvsock + 1, &rset, NULL, NULL, timeout);
+
+       if (n == 0) {
+               error = PHP_TIMEOUT_ERROR_VALUE;
+       } else if (n == -1) {
+               error = php_socket_errno();
+       } else if (FD_ISSET(srvsock, &rset)) {
+               sl = sizeof(sa);
+
+               clisock = accept(srvsock, (struct sockaddr*)&sa, &sl);
+
+               if (clisock >= 0) {
+                       populate_name((struct sockaddr*)&sa, sl,
+                                       textaddr, textaddrlen,
+                                       addr, addrlen
+                                       TSRMLS_CC);
+               } else {
+                       error = php_socket_errno();
+               }
        }
-       result->tv_sec = a.tv_sec - b.tv_sec;
-       if (result->tv_sec < 0L) {
-               result->tv_sec++;
-               result->tv_usec -= 1000000L;
+       
+       if (error_code) {
+               *error_code = error;
        }
+       if (error_string) {
+               *error_string = php_socket_strerror(error, NULL, 0);
+       }
+       
+       return clisock;
 }
 /* }}} */
+       
+
 
 /* Connect to a remote host using an interruptible connect with optional timeout.
  * Optionally, the connect can be made asynchronously, which will implicitly
@@ -654,110 +764,6 @@ connected:
 }
 /* }}} */
 
-
-
-/* {{{ 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.
- * timeout gives timeout in seconds, 0 means blocking mode.
- */
-int php_hostconnect(const char *host, unsigned short port, int socktype, struct timeval *timeout TSRMLS_DC)
-{      
-       int n, repeatto, s;
-       struct sockaddr **sal, **psal;
-       struct timeval individual_timeout;
-       int set_timeout = 0;
-       int err;
-       
-       n = php_network_getaddresses(host, &sal, NULL TSRMLS_CC);
-
-       if (n == 0)
-               return -1;
-       
-       if (timeout != NULL) {
-               /* is this a good idea? 5s? */
-               repeatto = timeout->tv_sec / n > 5;
-               if (repeatto) {
-                       individual_timeout.tv_sec = timeout->tv_sec / n;
-               } else {
-                       individual_timeout.tv_sec = timeout->tv_sec;
-               }
-
-               individual_timeout.tv_usec = timeout->tv_usec;
-       } else {
-               individual_timeout.tv_sec = 0;
-               individual_timeout.tv_usec = 0;
-       }
-       
-       /* Boolean indicating whether to pass a timeout */
-       set_timeout = individual_timeout.tv_sec + individual_timeout.tv_usec;
-       
-       psal = sal;
-       while (*sal != NULL) {
-               s = socket((*sal)->sa_family, socktype, 0);
-               if (s != SOCK_ERR) {
-                       switch ((*sal)->sa_family) {
-#if defined( HAVE_GETADDRINFO ) && defined( HAVE_IPV6 )
-                               case AF_INET6:
-                                       {
-                                               struct sockaddr_in6 *sa =
-                                                       (struct sockaddr_in6 *)*sal;
-
-                                               sa->sin6_family = (*sal)->sa_family;
-                                               sa->sin6_port = htons(port);
-                                               if (php_connect_nonb(s, (struct sockaddr *) sa,
-                                                                       sizeof(*sa), (set_timeout) ? &individual_timeout : NULL) != SOCK_CONN_ERR)
-                                                       goto ok;
-                                       } 
-                                       break;
-#endif
-                               case AF_INET:
-                                       {
-                                               struct sockaddr_in *sa =
-                                                       (struct sockaddr_in *)*sal;
-
-                                               sa->sin_family = (*sal)->sa_family;
-                                               sa->sin_port = htons(port);
-                                               if (php_connect_nonb(s, (struct sockaddr *) sa,
-                                                                       sizeof(*sa), (set_timeout) ? &individual_timeout : NULL) != SOCK_CONN_ERR)
-                                                       goto ok;
-
-                                       } 
-                                       break;
-                       }
-#ifdef PHP_WIN32
-                       /* Preserve the last error */
-                       err = WSAGetLastError();
-#else
-                       err = errno;
-#endif
-                       close (s);
-               }
-               sal++;
-
-               if (err == PHP_TIMEOUT_ERROR_VALUE) {
-                       /* if the first attempt timed out, it's highly likely
-                        * that any subsequent attempts will do so also */
-                       break;
-               }
-               
-       }
-       php_network_freeaddresses(psal);
-       php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_hostconnect: connect failed");
-
-#ifdef PHP_WIN32
-       /* Restore the last error */
-       WSASetLastError(err);
-#endif 
-
-       return -1;
-
- ok:
-       php_network_freeaddresses(psal);
-       return s;
-}
-/* }}} */
-
 /* {{{ php_any_addr
  * Fills the any (wildcard) address into php_sockaddr_storage
  */
index f3870d2aa1538b132ce947e80007958e7e99bf99..343996435325ec08b213eaa178916ed7a7131423 100644 (file)
@@ -119,12 +119,33 @@ PHPAPI int php_network_connect_socket(int sockfd,
                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);
+#define php_connect_nonb(sock, addr, addrlen, timeout) \
+       php_network_connect_socket((sock), (addr), (addrlen), 0, (timeout), NULL, NULL)
 
-#ifdef PHP_WIN32
-PHPAPI int php_connect_nonb_win32(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen, struct timeval *timeout);
-#endif
+PHPAPI int php_network_bind_socket_to_local_addr(const char *host, unsigned port,
+               int socktype, char **error_string, int *error_code
+               TSRMLS_DC);
+
+PHPAPI int php_network_accept_incoming(int srvsock,
+               char **textaddr, long *textaddrlen,
+               struct sockaddr **addr,
+               socklen_t *addrlen,
+               struct timeval *timeout,
+               char **error_string,
+               int *error_code
+               TSRMLS_DC);
+
+PHPAPI int php_network_get_sock_name(int sock, 
+               char **textaddr, long *textaddrlen,
+               struct sockaddr **addr,
+               socklen_t *addrlen
+               TSRMLS_DC);
+       
+PHPAPI int php_network_get_peer_name(int sock, 
+               char **textaddr, long *textaddrlen,
+               struct sockaddr **addr,
+               socklen_t *addrlen
+               TSRMLS_DC);
 
 void php_any_addr(int family, php_sockaddr_storage *addr, unsigned short port);
 int php_sockaddr_size(php_sockaddr_storage *addr);
@@ -134,6 +155,7 @@ struct _php_netstream_data_t        {
        char is_blocked;
        struct timeval timeout;
        char timeout_event;
+       size_t ownsize;
 };
 typedef struct _php_netstream_data_t php_netstream_data_t;
 extern php_stream_ops php_stream_socket_ops;
index 89642b5652ce97ba9fbcc4513732016b8d1e8602..c1cd622f2b7f174d57e88d1535d63b125a89c6d8 100644 (file)
@@ -72,19 +72,27 @@ PHPAPI int php_stream_xport_listen(php_stream *stream,
 /* 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,
+               char **textaddr, int *textaddrlen,
                void **addr, size_t *addrlen,
                struct timeval *timeout,
                char **error_text
                TSRMLS_DC);
 
+/* Get the name of either the socket or it's peer */
+PHPAPI int php_stream_xport_get_name(php_stream *stream, int want_peer,
+               char **textaddr, int *textaddrlen,
+               void **addr, size_t *addrlen
+               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
+               STREAM_XPORT_OP_CONNECT_ASYNC,
+               STREAM_XPORT_OP_GET_NAME,
+               STREAM_XPORT_OP_GET_PEER_NAME
        } op;
        int want_addr:1;
        int want_textaddr:1;
@@ -99,7 +107,7 @@ typedef struct _php_stream_xport_param {
        struct {
                php_stream *client;
                int returncode;
-               void *addr;
+               struct sockaddr *addr;
                size_t addrlen;
                char *textaddr;
                long textaddrlen;
index 4f12e193ed3a4424330e38db9754f9177a28e082..0bfddd39c01e561f8b0979f2dd3ae06dcb2b1dec 100644 (file)
@@ -252,7 +252,7 @@ PHPAPI int php_stream_xport_listen(php_stream *stream, int backlog, char **error
 
 /* 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,
+               char **textaddr, int *textaddrlen,
                void **addr, size_t *addrlen,
                struct timeval *timeout,
                char **error_text
@@ -263,7 +263,7 @@ PHPAPI int php_stream_xport_accept(php_stream *stream, php_stream **client,
 
        memset(&param, 0, sizeof(param));
 
-       param.op = STREAM_XPORT_OP_BIND;
+       param.op = STREAM_XPORT_OP_ACCEPT;
        param.inputs.timeout = timeout;
        param.want_addr = addr ? 1 : 0;
        param.want_textaddr = textaddr ? 1 : 0;
@@ -290,6 +290,37 @@ PHPAPI int php_stream_xport_accept(php_stream *stream, php_stream **client,
        return ret;
 }
 
+PHPAPI int php_stream_xport_get_name(php_stream *stream, int want_peer,
+               char **textaddr, int *textaddrlen,
+               void **addr, size_t *addrlen
+               TSRMLS_DC)
+{
+       php_stream_xport_param param;
+       int ret;
+
+       memset(&param, 0, sizeof(param));
+
+       param.op = want_peer ? STREAM_XPORT_OP_GET_PEER_NAME : STREAM_XPORT_OP_GET_NAME;
+       param.want_addr = addr ? 1 : 0;
+       param.want_textaddr = textaddr ? 1 : 0;
+       
+       ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, &param);
+
+       if (ret == PHP_STREAM_OPTION_RETURN_OK) {
+               if (addr) {
+                       *addr = param.outputs.addr;
+                       *addrlen = param.outputs.addrlen;
+               }
+               if (textaddr) {
+                       *textaddr = param.outputs.textaddr;
+                       *textaddrlen = param.outputs.textaddrlen;
+               }
+
+               return param.outputs.returncode;
+       }
+       return ret;
+}
+
 PHPAPI int php_stream_xport_crypto_setup(php_stream *stream, php_stream_xport_crypt_method_t crypto_method, php_stream *session_stream TSRMLS_DC)
 {
        php_stream_xport_crypto_param param;
index 34d617b8ce407a3403610ad2e78fba90354ac2fe..c2547bd7d33affcb849e57f087d2d9e72c3632fc 100644 (file)
@@ -217,6 +217,25 @@ static int php_sockop_set_option(php_stream *stream, int option, int value, void
                                        xparam->outputs.returncode = listen(sock->socket, 5);
                                        return PHP_STREAM_OPTION_RETURN_OK;
 
+                               case STREAM_XPORT_OP_GET_NAME:
+                                       xparam->outputs.returncode = php_network_get_sock_name(sock->socket,
+                                                       xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
+                                                       xparam->want_textaddr ? &xparam->outputs.textaddrlen : NULL,
+                                                       xparam->want_addr ? &xparam->outputs.addr : NULL,
+                                                       xparam->want_addr ? &xparam->outputs.addrlen : NULL
+                                                       TSRMLS_CC);
+                                       return PHP_STREAM_OPTION_RETURN_OK;
+
+                               case STREAM_XPORT_OP_GET_PEER_NAME:
+                                       xparam->outputs.returncode = php_network_get_peer_name(sock->socket,
+                                                       xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
+                                                       xparam->want_textaddr ? &xparam->outputs.textaddrlen : NULL,
+                                                       xparam->want_addr ? &xparam->outputs.addr : NULL,
+                                                       xparam->want_addr ? &xparam->outputs.addrlen : NULL
+                                                       TSRMLS_CC);
+                                       return PHP_STREAM_OPTION_RETURN_OK;
+
+
                                default:
                                        return PHP_STREAM_OPTION_RETURN_NOTIMPL;
                        }
@@ -311,17 +330,97 @@ php_stream_ops php_stream_unixdg_socket_ops = {
 
 /* network socket operations */
 
+#ifdef AF_UNIX
+static inline int parse_unix_address(php_stream_xport_param *xparam, struct sockaddr_un *unix_addr TSRMLS_DC)
+{
+       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);
+
+       return 1;
+}
+#endif
+
+static inline char *parse_ip_address(php_stream_xport_param *xparam, int *portno TSRMLS_DC)
+{
+       char *colon;
+       char *host = NULL;
+
+       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 NULL;
+       }
+
+       return host;
+}
+
 static inline int php_tcp_sockop_bind(php_stream *stream, php_netstream_data_t *sock,
                php_stream_xport_param *xparam TSRMLS_DC)
 {
+       char *host = NULL;
+       int portno, err;
+
+#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%s socket %s",
+                                               stream->ops == &php_stream_unix_socket_ops ? "" : "datagram",
+                                               strerror(errno));
+                       }
+                       return -1;
+               }
+
+               parse_unix_address(xparam, &unix_addr TSRMLS_CC);
+
+               return bind(sock->socket, &unix_addr, sizeof(unix_addr));
+       }
+#endif
 
-       return -1;
+       host = parse_ip_address(xparam, &portno TSRMLS_CC);
+
+       if (host == NULL) {
+               return -1;
+       }
+
+       sock->socket = php_network_bind_socket_to_local_addr(host, portno,
+                       stream->ops == &php_stream_udp_socket_ops ? SOCK_DGRAM : SOCK_STREAM,
+                       xparam->want_errortext ? &xparam->outputs.error_text : NULL,
+                       &err
+                       TSRMLS_CC);
+       
+       if (host) {
+               efree(host);
+       }
+
+       return sock->socket == -1 ? -1 : 0;
 }
 
 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;
@@ -339,21 +438,7 @@ static inline int php_tcp_sockop_connect(php_stream *stream, php_netstream_data_
                        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);
+               parse_unix_address(xparam, &unix_addr TSRMLS_CC);
 
                ret = php_network_connect_socket(sock->socket,
                                (const struct sockaddr *)&unix_addr, sizeof(unix_addr),
@@ -366,15 +451,10 @@ static inline int php_tcp_sockop_connect(php_stream *stream, php_netstream_data_
                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);
-               }
+
+       host = parse_ip_address(xparam, &portno TSRMLS_CC);
+
+       if (host == NULL) {
                return -1;
        }
 
@@ -409,9 +489,42 @@ out:
 }
 
 static inline int php_tcp_sockop_accept(php_stream *stream, php_netstream_data_t *sock,
-               php_stream_xport_param *xparam TSRMLS_DC)
+               php_stream_xport_param *xparam STREAMS_DC TSRMLS_DC)
 {
-       return -1;
+       int clisock;
+
+       xparam->outputs.client = NULL;
+
+       clisock = php_network_accept_incoming(sock->socket,
+                       xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
+                       xparam->want_textaddr ? &xparam->outputs.textaddrlen : NULL,
+                       xparam->want_addr ? &xparam->outputs.addr : NULL,
+                       xparam->want_addr ? &xparam->outputs.addrlen : NULL,
+                       xparam->inputs.timeout,
+                       xparam->want_errortext ? &xparam->outputs.error_text : NULL,
+                       &xparam->outputs.error_code
+                       TSRMLS_CC);
+
+       if (clisock >= 0) {
+               php_netstream_data_t *clisockdata;
+
+               clisockdata = pemalloc(sizeof(*clisockdata), stream->is_persistent);
+
+               if (clisockdata == NULL) {
+                       close(clisock);
+                       /* technically a fatal error */
+               } else {
+                       memcpy(clisockdata, sock, sizeof(*clisockdata));
+                       clisockdata->socket = clisock;
+
+                       xparam->outputs.client = php_stream_alloc_rel(stream->ops, clisockdata, NULL, "r+");
+                       if (xparam->outputs.client) {
+                               xparam->outputs.client->context = stream->context;
+                       }
+               }
+       }
+       
+       return xparam->outputs.client == NULL ? -1 : 0;
 }
 
 static int php_tcp_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC)
@@ -435,7 +548,7 @@ static int php_tcp_sockop_set_option(php_stream *stream, int option, int value,
 
 
                                case STREAM_XPORT_OP_ACCEPT:
-                                       xparam->outputs.returncode = php_tcp_sockop_accept(stream, sock, xparam TSRMLS_CC);
+                                       xparam->outputs.returncode = php_tcp_sockop_accept(stream, sock, xparam STREAMS_CC TSRMLS_CC);
                                        return PHP_STREAM_OPTION_RETURN_OK;
                                default:
                                        /* fall through */