From 1b53a2d12e520adec5cbbc60bf8f2b6d8e54eece Mon Sep 17 00:00:00 2001 From: Wez Furlong Date: Fri, 28 Feb 2003 19:53:21 +0000 Subject: [PATCH] New user-space functions: . 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. --- NEWS | 10 +- configure.in | 2 + ext/ftp/ftp.c | 4 +- ext/openssl/xp_ssl.c | 59 +++- ext/standard/basic_functions.c | 6 +- ext/standard/file.c | 8 +- ext/standard/file.h | 15 - ext/standard/fsock.c | 6 +- ext/standard/streamsfuncs.c | 227 +++++++++++++ main/network.c | 504 ++++++++++++++-------------- main/php_network.h | 32 +- main/streams/php_stream_transport.h | 14 +- main/streams/transports.c | 35 +- main/streams/xp_socket.c | 171 ++++++++-- 14 files changed, 778 insertions(+), 315 deletions(-) diff --git a/NEWS b/NEWS index 8ad72866858..86939d60cf4 100644 --- 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 diff --git a/configure.in b/configure.in index 7232d34eabe..eabade523b1 100644 --- a/configure.in +++ b/configure.in @@ -461,6 +461,8 @@ getservbyport \ getrusage \ gettimeofday \ gmtime_r \ +inet_ntoa \ +inet_ntop \ isascii \ link \ localtime_r \ diff --git a/ext/ftp/ftp.c b/ext/ftp/ftp.c index 8ceb938c7d5..4601a6e321d 100644 --- a/ext/ftp/ftp.c +++ b/ext/ftp/ftp.c @@ -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; } diff --git a/ext/openssl/xp_ssl.c b/ext/openssl/xp_ssl.c index 4e9a0ac11a9..69f11a287e0 100644 --- a/ext/openssl/xp_ssl.c +++ b/ext/openssl/xp_ssl.c @@ -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; } diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index 56d25da5e27..d9ef2ebb6c2 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -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) diff --git a/ext/standard/file.c b/ext/standard/file.c index 280b49b7893..54472d4bf5d 100644 --- a/ext/standard/file.c +++ b/ext/standard/file.c @@ -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); diff --git a/ext/standard/file.h b/ext/standard/file.h index e32eac24fda..faa389fc674 100644 --- a/ext/standard/file.h +++ b/ext/standard/file.h @@ -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); diff --git a/ext/standard/fsock.c b/ext/standard/fsock.c index 8f41cf4d3f1..ddc0812c489 100644 --- a/ext/standard/fsock.c +++ b/ext/standard/fsock.c @@ -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); } diff --git a/ext/standard/streamsfuncs.c b/ext/standard/streamsfuncs.c index 1a054fe00e9..110058ac263 100644 --- a/ext/standard/streamsfuncs.c +++ b/ext/standard/streamsfuncs.c @@ -27,12 +27,239 @@ #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) diff --git a/main/network.c b/main/network.c index cc499bb3c18..f04447745aa 100644 --- a/main/network.c +++ b/main/network.c @@ -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,148 +373,6 @@ ok: } /* }}} */ -/* {{{ php_connect_nonb */ -PHPAPI int php_connect_nonb(int sockfd, - const struct sockaddr *addr, - socklen_t addrlen, - struct timeval *timeout) -{ -#if (!defined(__BEOS__) && !defined(PHP_WIN32)) && (defined(O_NONBLOCK) || defined(O_NDELAY)) - - - int flags; - int n; - int error = 0; - socklen_t len; - int ret = 0; - fd_set rset; - fd_set wset; - fd_set eset; - - if (timeout == NULL) { - /* blocking mode */ - return connect(sockfd, addr, addrlen); - } - - 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; - } - } - - 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: - fcntl(sockfd, F_SETFL, flags); - - if(error) { - errno = error; - ret = -1; - } - 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 -} -/* }}} */ - -#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) -{ - 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); - } - - /* 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; - } - } - - FD_ZERO(&rset); - FD_SET(sockfd, &rset); - - FD_ZERO(&wset); - FD_SET(sockfd, &wset); - - if ((ret = select(sockfd + 1, &rset, &wset, NULL, timeout)) == 0) { - WSASetLastError(WSAETIMEDOUT); - return SOCKET_ERROR; - } - - if (ret == SOCKET_ERROR) { - return SOCKET_ERROR; - } - - 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); - - if (error) { - WSASetLastError(error); - return SOCKET_ERROR; - } - - return 0; -} -/* }}} */ -#endif - /* {{{ sub_times */ static inline void sub_times(struct timeval a, struct timeval b, struct timeval *result) { @@ -534,6 +389,261 @@ static inline void sub_times(struct timeval a, struct timeval b, struct timeval } /* }}} */ +/* 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; + + num_addrs = php_network_getaddresses(host, &psal, error_string TSRMLS_CC); + + if (num_addrs == 0) { + /* could not resolve address(es) */ + return -1; + } + + for (sal = psal; *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) { + /* 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 (n != SOCK_CONN_ERR) { + goto bound; + } + + err = php_socket_errno(); + } + + close(sock); + } + sock = -1; + + if (error_code) { + *error_code = err; + } + if (error_string) { + *error_string = php_socket_strerror(err, NULL, 0); + } + +bound: + + php_network_freeaddresses(psal); + + return sock; + +} +/* }}} */ + +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) +{ + if (addr) { + *addr = emalloc(sl); + memcpy(*addr, sa, sl); + *addrlen = sl; + } + + if (textaddr) { +#if HAVE_IPV6 && HAVE_INET_NTOP + char abuf[256]; +#endif + char *buf = NULL; + + 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); + } + + break; + +#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); + } + + 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 + + } + + } +} + +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; +} + +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; + +} + + +/* 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) +{ + 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(); + } + } + + 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 * enable non-blocking mode on the socket. @@ -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 */ diff --git a/main/php_network.h b/main/php_network.h index f3870d2aa15..34399643532 100644 --- a/main/php_network.h +++ b/main/php_network.h @@ -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; diff --git a/main/streams/php_stream_transport.h b/main/streams/php_stream_transport.h index 89642b5652c..c1cd622f2b7 100644 --- a/main/streams/php_stream_transport.h +++ b/main/streams/php_stream_transport.h @@ -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; diff --git a/main/streams/transports.c b/main/streams/transports.c index 4f12e193ed3..0bfddd39c01 100644 --- a/main/streams/transports.c +++ b/main/streams/transports.c @@ -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(¶m, 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(¶m, 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, ¶m); + + 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; diff --git a/main/streams/xp_socket.c b/main/streams/xp_socket.c index 34d617b8ce4..c2547bd7d33 100644 --- a/main/streams/xp_socket.c +++ b/main/streams/xp_socket.c @@ -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; - return -1; +#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 + + 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 */