Another big commit (tm).

Main Changes:
- Implement a socket transport layer for use by all code that needs to open
  some kind of "special" socket for network or IPC.
- Extensions can register (and override) transports.
- Implement ftruncate() on streams via the ioctl-alike option interface.
- Implement mmap() on streams via the ioctl-alike option interface.
- Implement generic crypto API via the ioctl-alike option interface.
  (currently only supports OpenSSL, but could support other SSL toolkits,
  and other crypto transport protocols).

Impact:
- tcp sockets can be overloaded by the openssl capable sockets at runtime,
  removing the link-time requirement for ssl:// and https:// sockets and
  streams.
- checking stream types using PHP_STREAM_IS_SOCKET is deprecated, since
  there are now a range of possible socket-type streams.

Working towards:
- socket servers using the new transport layer
- mmap support under win32
- Cleaner code.

# I will be updating the win32 build to add the new files shortly
# after this commit.
This commit is contained in:
Wez Furlong 2003-02-27 17:43:38 +00:00
parent 560e33968d
commit fd61f69077
24 changed files with 2232 additions and 927 deletions

View file

@ -1113,7 +1113,7 @@ PHP_ADD_SOURCES(main, main.c snprintf.c spprintf.c php_sprintf.c \
output.c )
PHP_ADD_SOURCES(main/streams, streams.c cast.c memory.c filter.c \
plain_wrapper.c userspace.c)
plain_wrapper.c userspace.c transports.c xp_socket.c mmap.c)
PHP_ADD_SOURCES(/main, internal_functions.c,, sapi)
PHP_ADD_SOURCES(/main, internal_functions_cli.c,, cli)

View file

@ -87,8 +87,8 @@ inifile * inifile_alloc(php_stream *fp, int readonly, int persistent TSRMLS_DC)
int fd = 0;
if (!readonly) {
if (php_stream_is(fp, PHP_STREAM_IS_SOCKET)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can't truncate sockets");
if (!php_stream_truncate_supported(fp)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can't truncate this stream");
return NULL;
}
if (SUCCESS != php_stream_cast(fp, PHP_STREAM_AS_FD, (void*)&fd, 1)) {
@ -320,7 +320,7 @@ static int inifile_truncate(inifile *dba, size_t size TSRMLS_DC)
{
int res;
if ((res=ftruncate(dba->fd, size)) != 0) {
if ((res=php_stream_truncate_set_size(dba->fp, size)) != 0) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error in ftruncate: %d", res);
return FAILURE;
}

View file

@ -3,7 +3,7 @@ dnl $Id$
dnl
if test "$PHP_OPENSSL" != "no"; then
PHP_NEW_EXTENSION(openssl, openssl.c, $ext_openssl_shared)
PHP_NEW_EXTENSION(openssl, openssl.c xp_ssl.c, $ext_openssl_shared)
OPENSSL_SHARED_LIBADD="-lcrypto -lssl"
PHP_SUBST(OPENSSL_SHARED_LIBADD)
AC_DEFINE(HAVE_OPENSSL_EXT,1,[ ])

View file

@ -608,6 +608,13 @@ PHP_MINIT_FUNCTION(openssl)
} else {
strlcpy(default_ssl_conf_filename, config_filename, sizeof(default_ssl_conf_filename));
}
php_stream_xport_register("ssl", php_openssl_ssl_socket_factory TSRMLS_CC);
php_stream_xport_register("tls", php_openssl_ssl_socket_factory TSRMLS_CC);
/* override the default tcp socket provider */
php_stream_xport_register("tcp", php_openssl_ssl_socket_factory TSRMLS_CC);
return SUCCESS;
}
/* }}} */
@ -628,6 +635,13 @@ PHP_MINFO_FUNCTION(openssl)
PHP_MSHUTDOWN_FUNCTION(openssl)
{
EVP_cleanup();
php_stream_xport_unregister("ssl" TSRMLS_CC);
php_stream_xport_unregister("tls" TSRMLS_CC);
/* reinstate the default tcp handler */
php_stream_xport_register("tcp", php_stream_generic_socket_factory TSRMLS_CC);
return SUCCESS;
}
/* }}} */

View file

@ -26,6 +26,8 @@
extern zend_module_entry openssl_module_entry;
#define phpext_openssl_ptr &openssl_module_entry
php_stream_transport_factory_func php_openssl_ssl_socket_factory;
PHP_MINIT_FUNCTION(openssl);
PHP_MSHUTDOWN_FUNCTION(openssl);
PHP_MINFO_FUNCTION(openssl);

498
ext/openssl/xp_ssl.c Normal file
View file

@ -0,0 +1,498 @@
/*
+----------------------------------------------------------------------+
| PHP Version 4 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2003 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 2.02 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available at through the world-wide-web at |
| http://www.php.net/license/2_02.txt. |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Author: Wez Furlong <wez@thebrainroom.com> |
+----------------------------------------------------------------------+
*/
/* $Id$ */
#include "php.h"
#include "ext/standard/file.h"
#include "streams/php_streams_int.h"
#include "php_network.h"
#include "php_openssl.h"
#include <openssl/err.h>
/* This implementation is very closely tied to the that of the native
* sockets implemented in the core.
* Don't try this technique in other extensions!
* */
typedef struct _php_openssl_netstream_data_t {
php_netstream_data_t s;
SSL *ssl_handle;
int enable_on_connect;
int is_client;
int ssl_active;
php_stream_xport_crypt_method_t method;
} php_openssl_netstream_data_t;
php_stream_ops php_openssl_socket_ops;
static int handle_ssl_error(php_stream *stream, int nr_bytes TSRMLS_DC)
{
php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
int err = SSL_get_error(sslsock->ssl_handle, nr_bytes);
char esbuf[512];
char *ebuf = NULL, *wptr = NULL;
size_t ebuf_size = 0;
unsigned long code;
int retry = 1;
switch(err) {
case SSL_ERROR_ZERO_RETURN:
/* SSL terminated (but socket may still be active) */
retry = 0;
break;
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
/* re-negotiation, or perhaps the SSL layer needs more
* packets: retry in next iteration */
break;
case SSL_ERROR_SYSCALL:
if (ERR_peek_error() == 0) {
if (nr_bytes == 0) {
php_error_docref(NULL TSRMLS_CC, E_WARNING,
"SSL: fatal protocol error");
stream->eof = 1;
retry = 0;
} else {
char *estr = php_socket_strerror(php_socket_errno(), NULL, 0);
php_error_docref(NULL TSRMLS_CC, E_WARNING,
"SSL: %s", estr);
efree(estr);
retry = 0;
}
break;
}
/* fall through */
default:
/* some other error */
while ((code = ERR_get_error()) != 0) {
/* allow room for a NUL and an optional \n */
if (ebuf) {
esbuf[0] = '\n';
esbuf[1] = '\0';
ERR_error_string_n(code, esbuf + 1, sizeof(esbuf) - 2);
} else {
esbuf[0] = '\0';
ERR_error_string_n(code, esbuf, sizeof(esbuf) - 1);
}
code = strlen(esbuf);
esbuf[code] = '\0';
ebuf = erealloc(ebuf, ebuf_size + code + 1);
if (wptr == NULL) {
wptr = ebuf;
}
/* also copies the NUL */
memcpy(wptr, esbuf, code + 1);
wptr += code;
}
php_error_docref(NULL TSRMLS_CC, E_WARNING,
"SSL operation failed with code %d.%s%s",
err,
ebuf ? "OpenSSL Error messages:\n" : "",
ebuf ? ebuf : "");
retry = 0;
}
return retry;
}
static size_t php_openssl_sockop_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
{
php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
size_t didwrite;
if (sslsock->ssl_active) {
int retry = 1;
do {
didwrite = SSL_write(sslsock->ssl_handle, buf, count);
if (didwrite <= 0) {
retry = handle_ssl_error(stream, didwrite TSRMLS_CC);
} else {
break;
}
} while(retry);
} else {
didwrite = php_stream_socket_ops.write(stream, buf, count TSRMLS_CC);
}
if (didwrite > 0)
php_stream_notify_progress_increment(stream->context, didwrite, 0);
return didwrite;
}
static size_t php_openssl_sockop_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
{
php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
int nr_bytes = 0;
if (sslsock->ssl_active) {
int retry = 1;
do {
nr_bytes = SSL_read(sslsock->ssl_handle, buf, count);
if (nr_bytes <= 0) {
retry = handle_ssl_error(stream, nr_bytes TSRMLS_CC);
if (retry == 0 && !SSL_pending(sslsock->ssl_handle)) {
stream->eof = 1;
}
} else {
/* we got the data */
break;
}
} while (retry);
}
else
{
nr_bytes = php_stream_socket_ops.read(stream, buf, count TSRMLS_CC);
}
if (nr_bytes > 0)
php_stream_notify_progress_increment(stream->context, nr_bytes, 0);
return nr_bytes;
}
static int php_openssl_sockop_close(php_stream *stream, int close_handle TSRMLS_DC)
{
php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
fd_set wrfds, efds;
int n;
struct timeval timeout;
if (close_handle) {
if (sslsock->ssl_active) {
SSL_shutdown(sslsock->ssl_handle);
sslsock->ssl_active = 0;
}
if (sslsock->ssl_handle) {
SSL_free(sslsock->ssl_handle);
sslsock->ssl_handle = NULL;
}
if (sslsock->s.socket != -1) {
/* prevent more data from coming in */
shutdown(sslsock->s.socket, SHUT_RD);
/* try to make sure that the OS sends all data before we close the connection.
* Essentially, we are waiting for the socket to become writeable, which means
* that all pending data has been sent.
* We use a small timeout which should encourage the OS to send the data,
* but at the same time avoid hanging indefintely.
* */
do {
FD_ZERO(&wrfds);
FD_SET(sslsock->s.socket, &wrfds);
efds = wrfds;
timeout.tv_sec = 0;
timeout.tv_usec = 5000; /* arbitrary */
n = select(sslsock->s.socket + 1, NULL, &wrfds, &efds, &timeout);
} while (n == -1 && php_socket_errno() == EINTR);
closesocket(sslsock->s.socket);
sslsock->s.socket = -1;
}
}
pefree(sslsock, php_stream_is_persistent(stream));
return 0;
}
static int php_openssl_sockop_flush(php_stream *stream TSRMLS_DC)
{
return php_stream_socket_ops.flush(stream TSRMLS_CC);
}
static int php_openssl_sockop_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC)
{
return php_stream_socket_ops.stat(stream, ssb TSRMLS_CC);
}
static inline int php_openssl_setup_crypto(php_stream *stream,
php_openssl_netstream_data_t *sslsock,
php_stream_xport_crypto_param *cparam
TSRMLS_DC)
{
SSL_CTX *ctx;
SSL_METHOD *method;
if (sslsock->ssl_handle) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSL/TLS already set-up for this stream");
return -1;
}
/* need to do slightly different things, based on client/server method,
* so lets remember which method was selected */
switch (cparam->inputs.method) {
case STREAM_CRYPTO_METHOD_SSLv23_CLIENT:
sslsock->is_client = 1;
method = SSLv23_client_method();
break;
case STREAM_CRYPTO_METHOD_SSLv2_CLIENT:
sslsock->is_client = 1;
method = SSLv2_client_method();
break;
case STREAM_CRYPTO_METHOD_SSLv3_CLIENT:
sslsock->is_client = 1;
method = SSLv3_client_method();
break;
case STREAM_CRYPTO_METHOD_TLS_CLIENT:
sslsock->is_client = 1;
method = TLSv1_client_method();
break;
case STREAM_CRYPTO_METHOD_SSLv23_SERVER:
sslsock->is_client = 0;
method = SSLv23_server_method();
break;
case STREAM_CRYPTO_METHOD_SSLv3_SERVER:
sslsock->is_client = 0;
method = SSLv3_server_method();
break;
case STREAM_CRYPTO_METHOD_SSLv2_SERVER:
sslsock->is_client = 0;
method = SSLv2_server_method();
break;
case STREAM_CRYPTO_METHOD_TLS_SERVER:
sslsock->is_client = 0;
method = TLSv1_server_method();
break;
default:
printf("unknown method\n");
return -1;
}
ctx = SSL_CTX_new(method);
if (ctx == NULL) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create an SSL context");
return -1;
}
sslsock->ssl_handle = SSL_new(ctx);
if (sslsock->ssl_handle == NULL) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create an SSL handle");
SSL_CTX_free(ctx);
return -1;
}
if (!SSL_set_fd(sslsock->ssl_handle, sslsock->s.socket)) {
printf("failed to set fd %d\n", sslsock->s.socket);
handle_ssl_error(stream, 0 TSRMLS_CC);
}
if (cparam->inputs.session) {
printf("sess=%p\n", cparam->inputs.session);
if (cparam->inputs.session->ops != &php_openssl_socket_ops) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied session stream must be an SSL enabled stream");
} else {
SSL_copy_session_id(sslsock->ssl_handle, ((php_openssl_netstream_data_t*)cparam->inputs.session->abstract)->ssl_handle);
}
}
printf("crypto prepared for fd=%d\n", sslsock->s.socket);
return 0;
}
static inline int php_openssl_enable_crypto(php_stream *stream,
php_openssl_netstream_data_t *sslsock,
php_stream_xport_crypto_param *cparam
TSRMLS_DC)
{
int n, retry = 1;
if (cparam->inputs.activate) {
if (sslsock->is_client) {
do {
n = SSL_connect(sslsock->ssl_handle);
if (n <= 0) {
retry = handle_ssl_error(stream, n TSRMLS_CC);
printf("error; retry = %d\n", retry);
} else {
break;
}
} while (retry);
printf("enabled_crypto: n=%d\n", n);
if (n == 1) {
sslsock->ssl_active = 1;
}
return n;
} else {
}
} else {
/* deactivate - common for server/client */
}
return -1;
}
static int php_openssl_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC)
{
php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
php_stream_xport_crypto_param *cparam = (php_stream_xport_crypto_param *)ptrparam;
php_stream_xport_param *xparam = (php_stream_xport_param *)ptrparam;
switch (option) {
case PHP_STREAM_OPTION_CRYPTO_API:
switch(cparam->op) {
case STREAM_XPORT_CRYPTO_OP_SETUP:
cparam->outputs.returncode = php_openssl_setup_crypto(stream, sslsock, cparam TSRMLS_CC);
return PHP_STREAM_OPTION_RETURN_OK;
case STREAM_XPORT_CRYPTO_OP_ENABLE:
cparam->outputs.returncode = php_openssl_enable_crypto(stream, sslsock, cparam TSRMLS_CC);
return PHP_STREAM_OPTION_RETURN_OK;
default:
/* fall through */
}
break;
case PHP_STREAM_OPTION_XPORT_API:
switch(xparam->op) {
case STREAM_XPORT_OP_CONNECT:
case STREAM_XPORT_OP_CONNECT_ASYNC:
/* TODO: Async connects need to check the enable_on_connect option when
* we notice that the connect has actually been established */
php_stream_socket_ops.set_option(stream, option, value, ptrparam TSRMLS_CC);
if (xparam->outputs.returncode == 0 && sslsock->enable_on_connect) {
if (php_stream_xport_crypto_setup(stream, sslsock->method, NULL TSRMLS_CC) < 0 ||
php_stream_xport_crypto_enable(stream, 1 TSRMLS_CC) < 0) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to enable crypto");
xparam->outputs.returncode = -1;
}
}
return PHP_STREAM_OPTION_RETURN_OK;
default:
/* fall through */
}
}
return php_stream_socket_ops.set_option(stream, option, value, ptrparam TSRMLS_CC);
}
static int php_openssl_sockop_cast(php_stream *stream, int castas, void **ret TSRMLS_DC)
{
php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
switch(castas) {
case PHP_STREAM_AS_STDIO:
if (sslsock->ssl_active) {
return FAILURE;
}
if (ret) {
*ret = fdopen(sslsock->s.socket, stream->mode);
if (*ret) {
return SUCCESS;
}
return FAILURE;
}
return SUCCESS;
case PHP_STREAM_AS_FD:
case PHP_STREAM_AS_SOCKETD:
if (sslsock->ssl_active) {
return FAILURE;
}
if (ret) {
*ret = (void*)sslsock->s.socket;
}
return SUCCESS;
default:
return FAILURE;
}
}
php_stream_ops php_openssl_socket_ops = {
php_openssl_sockop_write, php_openssl_sockop_read,
php_openssl_sockop_close, php_openssl_sockop_flush,
"tcp_socket/ssl",
NULL, /* seek */
php_openssl_sockop_cast,
php_openssl_sockop_stat,
php_openssl_sockop_set_option,
};
PHPAPI php_stream *php_openssl_ssl_socket_factory(const char *proto, long protolen,
char *resourcename, long resourcenamelen,
const char *persistent_id, int options, int flags,
struct timeval *timeout,
php_stream_context *context STREAMS_DC TSRMLS_DC)
{
php_stream *stream = NULL;
php_openssl_netstream_data_t *sslsock = NULL;
sslsock = pemalloc(sizeof(php_openssl_netstream_data_t), persistent_id ? 1 : 0);
memset(sslsock, 0, sizeof(php_openssl_netstream_data_t));
sslsock->s.is_blocked = 1;
sslsock->s.timeout.tv_sec = FG(default_socket_timeout);
sslsock->s.timeout.tv_usec = 0;
/* we don't know the socket until we have determined if we are binding or
* connecting */
sslsock->s.socket = -1;
stream = php_stream_alloc_rel(&php_openssl_socket_ops, sslsock, persistent_id, "r+");
if (stream == NULL) {
pefree(sslsock, persistent_id ? 1 : 0);
return NULL;
}
if (strncmp(proto, "ssl", protolen) == 0) {
sslsock->enable_on_connect = 1;
sslsock->method = STREAM_CRYPTO_METHOD_SSLv23_CLIENT;
} else if (strncmp(proto, "tls", protolen) == 0) {
sslsock->enable_on_connect = 1;
sslsock->method = STREAM_CRYPTO_METHOD_TLS_CLIENT;
}
return stream;
}
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
*/

View file

@ -1166,9 +1166,6 @@ PHP_RINIT_FUNCTION(basic)
BG(locale_string) = NULL;
BG(user_compare_func_name) = NULL;
BG(array_walk_func_name) = NULL;
#ifdef HAVE_MMAP
BG(mmap_file) = NULL;
#endif
BG(page_uid) = -1;
BG(page_gid) = -1;
BG(page_inode) = -1;
@ -1219,7 +1216,6 @@ PHP_RSHUTDOWN_FUNCTION(basic)
}
STR_FREE(BG(locale_string));
PHP_RSHUTDOWN(fsock)(SHUTDOWN_FUNC_ARGS_PASSTHRU);
PHP_RSHUTDOWN(filestat)(SHUTDOWN_FUNC_ARGS_PASSTHRU);
#ifdef HAVE_SYSLOG_H
PHP_RSHUTDOWN(syslog)(SHUTDOWN_FUNC_ARGS_PASSTHRU);
@ -1246,12 +1242,6 @@ PHP_RSHUTDOWN_FUNCTION(basic)
BG(user_filter_map) = NULL;
}
#ifdef HAVE_MMAP
if (BG(mmap_file)) {
munmap(BG(mmap_file), BG(mmap_len));
}
#endif
return SUCCESS;
}

View file

@ -647,6 +647,7 @@ PHP_FUNCTION(stream_get_meta_data)
add_assoc_long(return_value, "unread_bytes", stream->writepos - stream->readpos);
#if 0
if (php_stream_is(stream, PHP_STREAM_IS_SOCKET)) {
php_netstream_data_t *sock = PHP_NETSTREAM_DATA_FROM_STREAM(stream);
@ -654,10 +655,13 @@ PHP_FUNCTION(stream_get_meta_data)
add_assoc_bool(return_value, "blocked", sock->is_blocked);
add_assoc_bool(return_value, "eof", stream->eof);
} else {
#endif
add_assoc_bool(return_value, "timed_out", 0);
add_assoc_bool(return_value, "blocked", 1);
add_assoc_bool(return_value, "eof", php_stream_eof(stream));
#if 0
}
#endif
}
/* }}} */
@ -2237,7 +2241,7 @@ PHP_FUNCTION(unlink)
}
/* }}} */
/* {{{ proto int ftruncate(resource fp, int size)
/* {{{ proto bool ftruncate(resource fp, int size)
Truncate file to 'size' length */
PHP_NAMED_FUNCTION(php_if_ftruncate)
{
@ -2254,15 +2258,12 @@ PHP_NAMED_FUNCTION(php_if_ftruncate)
convert_to_long_ex(size);
if (php_stream_is(stream, PHP_STREAM_IS_SOCKET)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can't truncate sockets!");
if (!php_stream_truncate_supported(stream)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can't truncate this stream!");
RETURN_FALSE;
}
if (SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD, (void*)&fd, 1)) {
ret = ftruncate(fd, Z_LVAL_PP(size));
RETURN_LONG(ret + 1);
}
RETURN_FALSE;
RETURN_BOOL(0 == php_stream_truncate_set_size(stream, Z_LVAL_PP(size)));
}
/* }}} */

View file

@ -20,122 +20,19 @@
/* $Id$ */
/* converted to PHP Streams and moved much code to main/network.c [wez] */
/* Synced with php 3.0 revision 1.121 1999-06-18 [ssb] */
/* Synced with php 3.0 revision 1.133 1999-07-21 [sas] */
#include "php.h"
#include "php_globals.h"
#include <stdlib.h>
#include <stddef.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif
#include <sys/types.h>
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef PHP_WIN32
#include <winsock2.h>
#elif defined(NETWARE)
#ifdef NEW_LIBC
#ifdef USE_WINSOCK
#include <novsock2.h>
#else
#include <netinet/in.h>
#include <netdb.h>
/*#include <sys/socket.h>*/
#include <sys/select.h>
/*#else
#include <sys/socket.h>*/
#endif
#endif
#else
#include <netinet/in.h>
#include <netdb.h>
#if HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#endif
#if defined(PHP_WIN32) || defined(__riscos__) || defined(NETWARE)
#undef AF_UNIX
#endif
#if defined(AF_UNIX)
#include <sys/un.h>
#endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#ifndef PF_INET
#define PF_INET AF_INET
#endif
#ifndef PF_UNIX
#define PF_UNIX AF_UNIX
#endif
#include <string.h>
#include <errno.h>
#include "base64.h"
#include "file.h"
#include "url.h"
#include "fsock.h"
#include "php_network.h"
#ifdef ZTS
static int fsock_globals_id;
#endif
#ifdef PHP_WIN32
#define EWOULDBLOCK WSAEWOULDBLOCK
#elif defined(NETWARE)
#ifdef USE_WINSOCK
#define EWOULDBLOCK WSAEWOULDBLOCK
#endif
#endif
/* {{{ php_lookup_hostname */
/*
* Converts a host name to an IP address.
* TODO: This looks like unused code suitable for nuking.
*/
PHPAPI int php_lookup_hostname(const char *addr, struct in_addr *in)
{
struct hostent *host_info;
if (!inet_aton(addr, in)) {
/* XXX NOT THREAD SAFE */
host_info = gethostbyname(addr);
if (host_info == 0) {
/* Error: unknown host */
return -1;
}
*in = *((struct in_addr *) host_info->h_addr);
}
return 0;
}
/* }}} */
#include "file.h"
/* {{{ php_fsockopen() */
static void php_fsockopen_stream(INTERNAL_FUNCTION_PARAMETERS, int persistent)
{
char *host;
int host_len;
long host_len;
int port = -1;
zval *zerrno = NULL, *zerrstr = NULL;
double timeout = FG(default_socket_timeout);
@ -144,6 +41,9 @@ static void php_fsockopen_stream(INTERNAL_FUNCTION_PARAMETERS, int persistent)
char *hashkey = NULL;
php_stream *stream = NULL;
int err;
char *hostname = NULL;
long hostname_len;
char *errstr = NULL;
RETVAL_FALSE;
@ -151,22 +51,17 @@ static void php_fsockopen_stream(INTERNAL_FUNCTION_PARAMETERS, int persistent)
RETURN_FALSE;
}
if (persistent) {
spprintf(&hashkey, 0, "pfsockopen__%s:%d", host, port);
switch(php_stream_from_persistent_id(hashkey, &stream TSRMLS_CC)) {
case PHP_STREAM_PERSISTENT_SUCCESS:
/* TODO: could check if the socket is still alive here */
php_stream_to_zval(stream, return_value);
/* fall through */
case PHP_STREAM_PERSISTENT_FAILURE:
efree(hashkey);
return;
}
}
if (port > 0) {
hostname_len = spprintf(&hostname, 0, "%s:%d", host, port);
} else {
hostname_len = host_len;
hostname = host;
}
/* prepare the timeout value for use */
conv = (unsigned long) (timeout * 1000000.0);
tv.tv_sec = conv / 1000000;
@ -181,88 +76,29 @@ static void php_fsockopen_stream(INTERNAL_FUNCTION_PARAMETERS, int persistent)
ZVAL_STRING(zerrstr, "", 1);
}
if (port > 0) { /* connect to a host */
enum php_sslflags_t { php_ssl_none, php_ssl_v23, php_ssl_tls };
enum php_sslflags_t ssl_flags = php_ssl_none;
struct {
char *proto;
int protolen;
int socktype;
enum php_sslflags_t ssl_flags;
/* more flags to be added here */
} sockmodes[] = {
{ "udp://", 6, SOCK_DGRAM, php_ssl_none },
{ "tcp://", 6, SOCK_STREAM, php_ssl_none },
{ "ssl://", 6, SOCK_STREAM, php_ssl_v23 },
{ "tls://", 6, SOCK_STREAM, php_ssl_tls },
/* more modes to be added here */
{ NULL, 0, 0 }
};
int socktype = SOCK_STREAM;
int i;
stream = php_stream_xport_create(hostname, hostname_len, ENFORCE_SAFE_MODE | REPORT_ERRORS,
STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT, hashkey, &tv, NULL, &errstr, &err);
for (i = 0; sockmodes[i].proto != NULL; i++) {
if (strncmp(host, sockmodes[i].proto, sockmodes[i].protolen) == 0) {
ssl_flags = sockmodes[i].ssl_flags;
socktype = sockmodes[i].socktype;
host += sockmodes[i].protolen;
break;
}
}
#if !HAVE_OPENSSL_EXT
if (ssl_flags != php_ssl_none) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "no SSL support in this build");
}
else
#endif
stream = php_stream_sock_open_host(host, (unsigned short)port, socktype, &tv, hashkey);
/* Preserve error */
err = php_socket_errno();
if (stream == NULL) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to connect to %s:%d", host, port);
}
#if HAVE_OPENSSL_EXT
if (stream && ssl_flags != php_ssl_none) {
int ssl_ret = FAILURE;
switch(ssl_flags) {
case php_ssl_v23:
ssl_ret = php_stream_sock_ssl_activate_with_method(stream, 1, SSLv23_client_method(), NULL TSRMLS_CC);
break;
case php_ssl_tls:
ssl_ret = php_stream_sock_ssl_activate_with_method(stream, 1, TLSv1_client_method(), NULL TSRMLS_CC);
break;
default:
/* unknown ?? */
break;
}
if (ssl_ret == FAILURE)
php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to activate SSL mode %d", ssl_flags);
}
#endif
} else {
/* FIXME: Win32 - this probably does not return sensible errno and errstr */
stream = php_stream_sock_open_unix(host, host_len, hashkey, &tv);
err = php_socket_errno();
if (port > 0) {
efree(hostname);
}
if (stream == NULL) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to connect to %s:%d", host, port);
}
if (hashkey)
if (hashkey) {
efree(hashkey);
}
if (stream == NULL) {
if (zerrno) {
zval_dtor(zerrno);
ZVAL_LONG(zerrno, err);
}
if (zerrstr) {
char *buf = php_socket_strerror(err, NULL, 0);
/* no need to dup; we would only need to efree buf anyway */
if (zerrstr && errstr) {
/* no need to dup; we need to efree buf anyway */
zval_dtor(zerrstr);
ZVAL_STRING(zerrstr, buf, 0);
ZVAL_STRING(zerrstr, errstr, 0);
}
RETURN_FALSE;
}
@ -287,12 +123,6 @@ PHP_FUNCTION(pfsockopen)
}
/* }}} */
/* {{{ RSHUTDOWN_FUNCTION(fsock) */
PHP_RSHUTDOWN_FUNCTION(fsock)
{
return SUCCESS;
}
/* }}} */
/*
* Local variables:
* tab-width: 4

View file

@ -40,10 +40,6 @@
PHP_FUNCTION(fsockopen);
PHP_FUNCTION(pfsockopen);
PHPAPI int php_lookup_hostname(const char *addr, struct in_addr *in);
PHP_RSHUTDOWN_FUNCTION(fsock);
/*
* Local variables:
* tab-width: 4

View file

@ -142,10 +142,8 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, char *path, ch
char *scratch;
int result;
int i, use_ssl;
#if HAVE_OPENSSL_EXT
int use_ssl_on_data=0;
php_stream *reuseid=NULL;
#endif
char *tpath, *ttpath, *hoststart=NULL;
size_t file_size = 0;
@ -182,7 +180,6 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, char *path, ch
goto errexit;
}
#if HAVE_OPENSSL_EXT
if (use_ssl) {
/* send the AUTH TLS request name */
@ -212,7 +209,9 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, char *path, ch
}
if (use_ssl) {
if (use_ssl && php_stream_sock_ssl_activate(stream, 1) == FAILURE) {
if (php_stream_xport_crypto_setup(stream,
STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL TSRMLS_CC) < 0
|| php_stream_xport_crypto_enable(stream, 1 TSRMLS_CC) < 0) {
php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "Unable to activate SSL mode");
php_stream_close(stream);
stream = NULL;
@ -240,8 +239,6 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, char *path, ch
#endif
}
#endif
/* send the user name */
php_stream_write_string(stream, "USER ");
if (resource->user != NULL) {
@ -433,14 +430,15 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, char *path, ch
php_stream_context_set(datastream, context);
php_stream_notify_progress_init(context, 0, file_size);
#if HAVE_OPENSSL_EXT
if (use_ssl_on_data && php_stream_sock_ssl_activate_with_method(datastream, 1, SSLv23_method(), reuseid TSRMLS_CC) == FAILURE) {
if (use_ssl_on_data && (php_stream_xport_crypto_setup(stream,
STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL TSRMLS_CC) < 0 ||
php_stream_xport_crypto_enable(stream, 1 TSRMLS_CC) < 0)) {
php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "Unable to activate SSL mode");
php_stream_close(datastream);
datastream = NULL;
goto errexit;
}
#endif
/* remember control stream */
datastream->wrapperdata = (zval *)stream;

View file

@ -99,6 +99,8 @@ php_stream *php_stream_url_wrap_http(php_stream_wrapper *wrapper, char *path, ch
char tmp_line[128];
size_t chunk_size = 0, file_size = 0;
int eol_detect;
char *transport_string, *errstr = NULL;
int transport_len;
if (strpbrk(mode, "awx+")) {
php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "HTTP wrapper does not support writeable connections.");
@ -110,14 +112,26 @@ php_stream *php_stream_url_wrap_http(php_stream_wrapper *wrapper, char *path, ch
return NULL;
use_ssl = resource->scheme && (strlen(resource->scheme) > 4) && resource->scheme[4] == 's';
/* choose default ports */
if (use_ssl && resource->port == 0)
resource->port = 443;
else if (resource->port == 0)
resource->port = 80;
stream = php_stream_sock_open_host(resource->host, resource->port, SOCK_STREAM, NULL, 0);
transport_len = spprintf(&transport_string, 0, "%s://%s:%d", use_ssl ? "ssl" : "tcp", resource->host, resource->port);
stream = php_stream_xport_create(transport_string, transport_len, options,
STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT,
NULL, NULL, context, &errstr, NULL);
if (errstr) {
php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "%s", errstr);
efree(errstr);
errstr = NULL;
}
efree(transport_string);
if (stream == NULL)
goto out;
@ -133,17 +147,6 @@ php_stream *php_stream_url_wrap_http(php_stream_wrapper *wrapper, char *path, ch
php_stream_context_set(stream, context);
php_stream_notify_info(context, PHP_STREAM_NOTIFY_CONNECT, NULL, 0);
#if HAVE_OPENSSL_EXT
if (use_ssl) {
if (php_stream_sock_ssl_activate(stream, 1) == FAILURE) {
php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "Unable to activate SSL mode");
php_stream_close(stream);
stream = NULL;
goto out;
}
}
#endif
scratch_len = strlen(path) + 32;
scratch = emalloc(scratch_len);

View file

@ -48,10 +48,6 @@
#include <fcntl.h>
#endif
#ifdef HAVE_OPENSSL_EXT
#include <openssl/err.h>
#endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
@ -167,100 +163,228 @@ static void php_network_freeaddresses(struct sockaddr **sal)
/* {{{ php_network_getaddresses
* Returns number of addresses, 0 for none/error
*/
static int php_network_getaddresses(const char *host, struct sockaddr ***sal TSRMLS_DC)
static int php_network_getaddresses(const char *host, struct sockaddr ***sal, char **error_string TSRMLS_DC)
{
struct sockaddr **sap;
int n;
#ifdef HAVE_GETADDRINFO
struct addrinfo hints, *res, *sai;
#else
struct hostent *host_info;
struct in_addr in;
#endif
if (host == NULL) {
return 0;
}
{
#ifdef HAVE_GETADDRINFO
struct addrinfo hints, *res, *sai;
memset(&hints, '\0', sizeof(hints));
memset(&hints, '\0', sizeof(hints));
# ifdef HAVE_IPV6
hints.ai_family = AF_UNSPEC;
# else
hints.ai_family = AF_INET;
# endif
if ((n = getaddrinfo(host, NULL, &hints, &res))) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_network_getaddresses: getaddrinfo failed: %s", PHP_GAI_STRERROR(n));
return 0;
} else if (res == NULL) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_network_getaddresses: getaddrinfo failed (null result pointer)");
return 0;
# ifdef HAVE_IPV6
hints.ai_family = AF_UNSPEC;
# else
hints.ai_family = AF_INET;
# endif
if ((n = getaddrinfo(host, NULL, &hints, &res)) || res == NULL) {
char *str = res == NULL ? "null result pointer" : PHP_GAI_STRERROR(n);
if (error_string) {
spprintf(error_string, 0, "getaddrinfo: %s", str);
} else {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_network_getaddresses: getaddrinfo failed: %s", str);
}
return 0;
}
sai = res;
for (n = 1; (sai = sai->ai_next) != NULL; n++);
*sal = emalloc((n + 1) * sizeof(*sal));
sai = res;
sap = *sal;
do {
switch (sai->ai_family) {
# ifdef HAVE_IPV6
sai = res;
for (n = 1; (sai = sai->ai_next) != NULL; n++) {
;
}
*sal = emalloc((n + 1) * sizeof(*sal));
sai = res;
sap = *sal;
do {
switch (sai->ai_family) {
# if HAVE_IPV6
case AF_INET6:
*sap = emalloc(sizeof(struct sockaddr_in6));
*(struct sockaddr_in6 *)*sap =
*((struct sockaddr_in6 *)sai->ai_addr);
sap++;
break;
# endif
# endif
case AF_INET:
*sap = emalloc(sizeof(struct sockaddr_in));
*(struct sockaddr_in *)*sap =
*((struct sockaddr_in *)sai->ai_addr);
sap++;
break;
}
} while ((sai = sai->ai_next) != NULL);
freeaddrinfo(res);
#else
struct hostent *host_info;
struct in_addr in;
if (!inet_aton(host, &in)) {
/* XXX NOT THREAD SAFE
* (but it *is* thread safe under win32)
*/
host_info = gethostbyname(host);
if (host_info == NULL) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_network_getaddresses: gethostbyname failed");
return 0;
}
in = *((struct in_addr *) host_info->h_addr);
}
} while ((sai = sai->ai_next) != NULL);
freeaddrinfo(res);
#else
*sal = emalloc(2 * sizeof(*sal));
sap = *sal;
*sap = emalloc(sizeof(struct sockaddr_in));
(*sap)->sa_family = AF_INET;
((struct sockaddr_in *)*sap)->sin_addr = in;
sap++;
n = 1;
#endif
if (!inet_aton(host, &in)) {
/* XXX NOT THREAD SAFE
* (but it *is* thread safe under win32)
*/
host_info = gethostbyname(host);
if (host_info == NULL) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_network_getaddresses: gethostbyname failed");
return 0;
}
in = *((struct in_addr *) host_info->h_addr);
}
*sal = emalloc(2 * sizeof(*sal));
sap = *sal;
*sap = emalloc(sizeof(struct sockaddr_in));
(*sap)->sa_family = AF_INET;
((struct sockaddr_in *)*sap)->sin_addr = in;
sap++;
n = 1;
#endif
*sap = NULL;
return n;
}
/* }}} */
#ifndef O_NONBLOCK
#define O_NONBLOCK O_NDELAY
#endif
#if !defined(__BEOS__)
# define HAVE_NON_BLOCKING_CONNECT 1
# ifdef PHP_WIN32
typedef u_long php_non_blocking_flags_t;
# define SET_SOCKET_BLOCKING_MODE(sock, save) \
save = TRUE; ioctlsocket(sock, FIONBIO, &save)
# define RESTORE_SOCKET_BLOCKING_MODE(sock, save) \
ioctlsocket(sock, FIONBIO, &save)
# else
typedef int php_non_blocking_flags_t;
# define SET_SOCKET_BLOCKING_MODE(sock, save) \
save = fcntl(sock, F_GETFL, 0); \
fcntl(sock, F_SETFL, save | O_NONBLOCK)
# define RESTORE_SOCKET_BLOCKING_MODE(sock, save) \
fcntl(sock, F_SETFL, save)
# endif
#endif
/* Connect to a socket using an interruptible connect with optional timeout.
* Optionally, the connect can be made asynchronously, which will implicitly
* enable non-blocking mode on the socket.
* */
/* {{{ php_network_connect_socket */
PHPAPI int php_network_connect_socket(int sockfd,
const struct sockaddr *addr,
socklen_t addrlen,
int asynchronous,
struct timeval *timeout,
char **error_string,
int *error_code)
{
#if HAVE_NON_BLOCKING_CONNECT
php_non_blocking_flags_t orig_flags;
int n;
int error = 0;
socklen_t len;
int ret = 0;
fd_set rset;
fd_set wset;
fd_set eset;
SET_SOCKET_BLOCKING_MODE(sockfd, orig_flags);
if ((n = connect(sockfd, addr, addrlen)) < 0) {
error = php_socket_errno();
if (error_code) {
*error_code = error;
}
if (error != EINPROGRESS) {
if (error_string) {
*error_string = php_socket_strerror(error, NULL, 0);
}
return -1;
}
if (asynchronous && error == EINPROGRESS) {
/* this is fine by us */
return 0;
}
}
if (n == 0) {
goto ok;
}
FD_ZERO(&rset);
FD_ZERO(&eset);
FD_SET(sockfd, &rset);
FD_SET(sockfd, &eset);
wset = rset;
if ((n = select(sockfd + 1, &rset, &wset, &eset, timeout)) == 0) {
error = ETIMEDOUT;
}
if(FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) {
len = sizeof(error);
/*
BSD-derived systems set errno correctly
Solaris returns -1 from getsockopt in case of error
*/
if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
ret = -1;
}
} else {
/* whoops: sockfd has disappeared */
ret = -1;
}
ok:
if (!asynchronous) {
/* back to blocking mode */
RESTORE_SOCKET_BLOCKING_MODE(sockfd, orig_flags);
}
if (error_code) {
*error_code = error;
}
if (error && error_string) {
*error_string = php_socket_strerror(error, NULL, 0);
ret = -1;
}
return ret;
#else
if (asynchronous) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Asynchronous connect() not supported on this platform");
}
return connect(sockfd, addr, addrlen);
#endif
}
/* }}} */
/* {{{ php_connect_nonb */
PHPAPI int php_connect_nonb(int sockfd,
const struct sockaddr *addr,
socklen_t addrlen,
struct timeval *timeout)
{
/* probably won't work on Win32, someone else might try it (read: fix it ;) */
#if (!defined(__BEOS__) && !defined(PHP_WIN32)) && (defined(O_NONBLOCK) || defined(O_NDELAY))
#ifndef O_NONBLOCK
#define O_NONBLOCK O_NDELAY
#endif
int flags;
int n;
@ -395,6 +519,144 @@ PHPAPI int php_connect_nonb_win32(SOCKET sockfd,
/* }}} */
#endif
/* {{{ sub_times */
static inline void sub_times(struct timeval a, struct timeval b, struct timeval *result)
{
result->tv_usec = a.tv_usec - b.tv_usec;
if (result->tv_usec < 0L) {
a.tv_sec--;
result->tv_usec += 1000000L;
}
result->tv_sec = a.tv_sec - b.tv_sec;
if (result->tv_sec < 0L) {
result->tv_sec++;
result->tv_usec -= 1000000L;
}
}
/* }}} */
/* Connect to a remote host using an interruptible connect with optional timeout.
* Optionally, the connect can be made asynchronously, which will implicitly
* enable non-blocking mode on the socket.
* Returns the connected (or connecting) socket, or -1 on failure.
* */
/* {{{ php_network_connect_socket_to_host */
int php_network_connect_socket_to_host(const char *host, unsigned short port,
int socktype, int asynchronous, struct timeval *timeout, char **error_string,
int *error_code
TSRMLS_DC)
{
int num_addrs, sock, n, fatal = 0;
struct sockaddr **sal, **psal, *sa;
struct timeval working_timeout;
socklen_t socklen;
#if HAVE_GETTIMEOFDAY
struct timeval limit_time, time_now;
#endif
num_addrs = php_network_getaddresses(host, &psal, error_string TSRMLS_CC);
if (num_addrs == 0) {
/* could not resolve address(es) */
return -1;
}
if (timeout) {
memcpy(&working_timeout, timeout, sizeof(working_timeout));
#if HAVE_GETTIMEOFDAY
gettimeofday(&limit_time, NULL);
limit_time.tv_sec += working_timeout.tv_sec;
limit_time.tv_usec += working_timeout.tv_usec;
if (limit_time.tv_usec >= 1000000) {
limit_time.tv_usec -= 1000000;
limit_time.tv_sec++;
}
#endif
}
for (sal = psal; !fatal && *sal != NULL; sal++) {
sa = *sal;
/* create a socket for this address */
sock = socket(sa->sa_family, socktype, 0);
if (sock == SOCK_ERR) {
continue;
}
switch (sa->sa_family) {
#if HAVE_GETADDRINFO && HAVE_IPV6
case AF_INET6:
((struct sockaddr_in6 *)sa)->sin6_family = sa->sa_family;
((struct sockaddr_in6 *)sa)->sin6_port = htons(port);
socklen = sizeof(struct sockaddr_in6);
break;
#endif
case AF_INET:
((struct sockaddr_in *)sa)->sin_family = sa->sa_family;
((struct sockaddr_in *)sa)->sin_port = htons(port);
socklen = sizeof(struct sockaddr_in);
break;
default:
/* Unknown family */
sa = NULL;
}
if (sa) {
/* make a connection attempt */
n = php_network_connect_socket(sock, sa, socklen, asynchronous,
timeout ? &working_timeout : NULL,
error_string, error_code);
if (n != SOCK_CONN_ERR) {
goto connected;
}
/* adjust timeout for next attempt */
#if HAVE_GETTIMEOFDAY
if (timeout) {
gettimeofday(&time_now, NULL);
if (timercmp(&time_now, &limit_time, >=)) {
/* time limit expired; don't attempt any further connections */
fatal = 1;
} else {
/* work out remaining time */
sub_times(limit_time, time_now, &working_timeout);
}
}
#else
if (err == PHP_TIMEOUT_ERROR_VALUE) {
/* Don't even bother trying to connect to the next alternative;
* we have no way to determine how long we have already taken
* and it is quite likely that the next attempt will fail too. */
fatal = 1;
} else {
/* re-use the same initial timeout.
* Not the best thing, but in practice it should be good-enough */
if (timeout) {
memcpy(&working_timeout, timeout, sizeof(working_timeout));
}
}
#endif
}
close(sock);
}
sock = -1;
connected:
php_network_freeaddresses(psal);
return sock;
}
/* }}} */
/* {{{ php_hostconnect
* Creates a socket of type socktype and connects to the given host and
* port, returns the created socket on success, else returns -1.
@ -408,7 +670,7 @@ int php_hostconnect(const char *host, unsigned short port, int socktype, struct
int set_timeout = 0;
int err;
n = php_network_getaddresses(host, &sal TSRMLS_CC);
n = php_network_getaddresses(host, &sal, NULL TSRMLS_CC);
if (n == 0)
return -1;
@ -546,6 +808,12 @@ int php_sockaddr_size(php_sockaddr_storage *addr)
}
/* }}} */
/* Given a socket error code, if buf == NULL:
* emallocs storage for the error message and returns
* else
* sprintf message into provided buffer and returns buf
*/
/* {{{ php_socket_strerror */
PHPAPI char *php_socket_strerror(long err, char *buf, size_t bufsize)
{
#ifndef PHP_WIN32
@ -589,7 +857,9 @@ PHPAPI char *php_socket_strerror(long err, char *buf, size_t bufsize)
return buf;
#endif
}
/* }}} */
/* deprecated */
PHPAPI php_stream *_php_stream_sock_open_from_socket(int socket, const char *persistent_id STREAMS_DC TSRMLS_DC)
{
php_stream *stream;
@ -603,7 +873,7 @@ PHPAPI php_stream *_php_stream_sock_open_from_socket(int socket, const char *per
sock->timeout.tv_usec = 0;
sock->socket = socket;
stream = php_stream_alloc_rel(&php_stream_socket_ops, sock, persistent_id, "r+");
stream = php_stream_alloc_rel(&php_stream_generic_socket_ops, sock, persistent_id, "r+");
stream->flags |= PHP_STREAM_FLAG_AVOID_BLOCKING;
if (stream == NULL)
@ -615,125 +885,20 @@ PHPAPI php_stream *_php_stream_sock_open_from_socket(int socket, const char *per
PHPAPI php_stream *_php_stream_sock_open_host(const char *host, unsigned short port,
int socktype, struct timeval *timeout, const char *persistent_id STREAMS_DC TSRMLS_DC)
{
int socket;
char *res;
long reslen;
php_stream *stream;
socket = php_hostconnect(host, port, socktype, timeout TSRMLS_CC);
reslen = spprintf(&res, 0, "tcp://%s:%d", host, port);
if (socket == -1)
return NULL;
stream = php_stream_xport_create(res, reslen, ENFORCE_SAFE_MODE | REPORT_ERRORS,
STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT, persistent_id, timeout, NULL, NULL, NULL);
stream = php_stream_sock_open_from_socket_rel(socket, persistent_id);
if (stream == NULL)
closesocket(socket);
efree(res);
return stream;
}
PHPAPI php_stream *_php_stream_sock_open_unix(const char *path, int pathlen, const char *persistent_id,
struct timeval *timeout STREAMS_DC TSRMLS_DC)
{
#if defined(AF_UNIX)
int socketd;
struct sockaddr_un unix_addr;
php_stream *stream;
socketd = socket(PF_UNIX, SOCK_STREAM, 0);
if (socketd == SOCK_ERR)
return NULL;
memset(&unix_addr, 0, sizeof(unix_addr));
unix_addr.sun_family = AF_UNIX;
/* we need to be binary safe on systems that support an abstract
* namespace */
if (pathlen >= sizeof(unix_addr.sun_path)) {
/* On linux, when the path begins with a NUL byte we are
* referring to an abstract namespace. In theory we should
* allow an extra byte below, since we don't need the NULL.
* BUT, to get into this branch of code, the name is too long,
* so we don't care. */
pathlen = sizeof(unix_addr.sun_path) - 1;
}
memcpy(unix_addr.sun_path, path, pathlen);
if (php_connect_nonb(socketd, (struct sockaddr *) &unix_addr, sizeof(unix_addr), timeout) == SOCK_CONN_ERR)
return NULL;
stream = php_stream_sock_open_from_socket_rel(socketd, persistent_id);
if (stream == NULL)
closesocket(socketd);
return stream;
#else
return NULL;
#endif
}
#if HAVE_OPENSSL_EXT
PHPAPI int php_stream_sock_ssl_activate_with_method(php_stream *stream, int activate, SSL_METHOD *method, php_stream *session_stream TSRMLS_DC)
{
php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
php_netstream_data_t *psock = NULL;
SSL_CTX *ctx = NULL;
if (!php_stream_is(stream, PHP_STREAM_IS_SOCKET)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_stream_sock_ssl_activate_with_method: stream is not a network stream");
return FAILURE;
}
if (session_stream) {
if (!php_stream_is(session_stream, PHP_STREAM_IS_SOCKET)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_stream_sock_ssl_activate_with_method: session_stream is not a network stream");
return FAILURE;
}
psock = (php_netstream_data_t*)session_stream->abstract;
}
if (activate == sock->ssl_active)
return SUCCESS; /* already in desired mode */
if (activate && sock->ssl_handle == NULL) {
ctx = SSL_CTX_new(method);
if (ctx == NULL) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_stream_sock_ssl_activate_with_method: failed to create an SSL context");
return FAILURE;
}
sock->ssl_handle = SSL_new(ctx);
if (sock->ssl_handle == NULL) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_stream_sock_ssl_activate_with_method: failed to create an SSL handle");
SSL_CTX_free(ctx);
return FAILURE;
}
SSL_set_fd(sock->ssl_handle, sock->socket);
if (psock) {
SSL_copy_session_id(sock->ssl_handle, psock->ssl_handle);
}
}
if (activate) {
if (SSL_connect(sock->ssl_handle) <= 0) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_stream_sock_ssl_activate_with_method: SSL handshake/connection failed");
SSL_shutdown(sock->ssl_handle);
return FAILURE;
}
sock->ssl_active = activate;
}
else {
SSL_shutdown(sock->ssl_handle);
sock->ssl_active = 0;
}
return SUCCESS;
}
#endif
PHPAPI int php_set_sock_blocking(int socketd, int block TSRMLS_DC)
{
int ret = SUCCESS;
@ -764,347 +929,6 @@ PHPAPI int php_set_sock_blocking(int socketd, int block TSRMLS_DC)
return ret;
}
#if HAVE_OPENSSL_EXT
static int handle_ssl_error(php_stream *stream, int nr_bytes TSRMLS_DC)
{
php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
int err = SSL_get_error(sock->ssl_handle, nr_bytes);
char esbuf[512];
char *ebuf = NULL, *wptr = NULL;
size_t ebuf_size = 0;
unsigned long code;
int retry = 1;
switch(err) {
case SSL_ERROR_ZERO_RETURN:
/* SSL terminated (but socket may still be active) */
retry = 0;
break;
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
/* re-negotiation, or perhaps the SSL layer needs more
* packets: retry in next iteration */
break;
case SSL_ERROR_SYSCALL:
if (ERR_peek_error() == 0) {
if (nr_bytes == 0) {
php_error_docref(NULL TSRMLS_CC, E_WARNING,
"SSL: fatal protocol error");
stream->eof = 1;
retry = 0;
} else {
char *estr = php_socket_strerror(php_socket_errno(), NULL, 0);
php_error_docref(NULL TSRMLS_CC, E_WARNING,
"SSL: %s", estr);
efree(estr);
retry = 0;
}
break;
}
/* fall through */
default:
/* some other error */
while ((code = ERR_get_error()) != 0) {
/* allow room for a NUL and an optional \n */
if (ebuf) {
esbuf[0] = '\n';
esbuf[1] = '\0';
ERR_error_string_n(code, esbuf + 1, sizeof(esbuf) - 2);
} else {
esbuf[0] = '\0';
ERR_error_string_n(code, esbuf, sizeof(esbuf) - 1);
}
code = strlen(esbuf);
esbuf[code] = '\0';
ebuf = erealloc(ebuf, ebuf_size + code + 1);
if (wptr == NULL) {
wptr = ebuf;
}
/* also copies the NUL */
memcpy(wptr, esbuf, code + 1);
wptr += code;
}
php_error_docref(NULL TSRMLS_CC, E_WARNING,
"SSL operation failed with code %d.%s%s",
err,
ebuf ? "OpenSSL Error messages:\n" : "",
ebuf ? ebuf : "");
retry = 0;
}
return retry;
}
#endif
static size_t php_sockop_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
{
php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
size_t didwrite;
#if HAVE_OPENSSL_EXT
if (sock->ssl_active) {
int retry = 1;
do {
didwrite = SSL_write(sock->ssl_handle, buf, count);
if (didwrite <= 0) {
retry = handle_ssl_error(stream, didwrite TSRMLS_CC);
} else {
break;
}
} while(retry);
} else
#endif
{
didwrite = send(sock->socket, buf, count, 0);
if (didwrite <= 0) {
char *estr = php_socket_strerror(php_socket_errno(), NULL, 0);
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "send of %d bytes failed with errno=%d %s",
count, php_socket_errno(), estr);
efree(estr);
}
}
if (didwrite > 0)
php_stream_notify_progress_increment(stream->context, didwrite, 0);
return didwrite;
}
#if ZEND_DEBUG && DEBUG_MAIN_NETWORK
static inline void dump_sock_state(char *msg, php_netstream_data_t *sock TSRMLS_DC)
{
printf("%s: blocked=%d timeout_event=%d eof=%d inbuf=%d timeout=%d\n", msg, sock->is_blocked, sock->timeout_event, sock->eof, TOREAD(sock), sock->timeout);
}
# define DUMP_SOCK_STATE(msg, sock) dump_sock_state(msg, sock TSRMLS_CC)
#else
# define DUMP_SOCK_STATE(msg, sock) /* nothing */
#endif
static void php_sock_stream_wait_for_data(php_stream *stream, php_netstream_data_t *sock TSRMLS_DC)
{
fd_set fdr, tfdr;
int retval;
struct timeval timeout, *ptimeout;
FD_ZERO(&fdr);
FD_SET(sock->socket, &fdr);
sock->timeout_event = 0;
if (sock->timeout.tv_sec == -1)
ptimeout = NULL;
else
ptimeout = &timeout;
while(1) {
tfdr = fdr;
timeout = sock->timeout;
DUMP_SOCK_STATE("wait_for_data", sock);
retval = select(sock->socket + 1, &tfdr, NULL, NULL, ptimeout);
if (retval == 0)
sock->timeout_event = 1;
if (retval >= 0)
break;
}
DUMP_SOCK_STATE("wait_for_data: done", sock);
}
static size_t php_sockop_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
{
php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
size_t nr_bytes = 0;
#if HAVE_OPENSSL_EXT
if (sock->ssl_active) {
int retry = 1;
do {
nr_bytes = SSL_read(sock->ssl_handle, buf, count);
if (nr_bytes <= 0) {
retry = handle_ssl_error(stream, nr_bytes TSRMLS_CC);
if (retry == 0 && !SSL_pending(sock->ssl_handle)) {
stream->eof = 1;
}
} else {
/* we got the data */
break;
}
} while (retry);
}
else
#endif
{
if (sock->is_blocked) {
php_sock_stream_wait_for_data(stream, sock TSRMLS_CC);
if (sock->timeout_event)
return 0;
}
nr_bytes = recv(sock->socket, buf, count, 0);
if (nr_bytes == 0 || (nr_bytes == -1 && php_socket_errno() != EWOULDBLOCK)) {
stream->eof = 1;
}
}
if (nr_bytes > 0)
php_stream_notify_progress_increment(stream->context, nr_bytes, 0);
return nr_bytes;
}
static int php_sockop_close(php_stream *stream, int close_handle TSRMLS_DC)
{
php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
fd_set wrfds, efds;
int n;
struct timeval timeout;
if (close_handle) {
#if HAVE_OPENSSL_EXT
if (sock->ssl_active) {
SSL_shutdown(sock->ssl_handle);
sock->ssl_active = 0;
}
if (sock->ssl_handle) {
SSL_free(sock->ssl_handle);
sock->ssl_handle = NULL;
}
#endif
/* prevent more data from coming in */
shutdown(sock->socket, SHUT_RD);
/* try to make sure that the OS sends all data before we close the connection.
* Essentially, we are waiting for the socket to become writeable, which means
* that all pending data has been sent.
* We use a small timeout which should encourage the OS to send the data,
* but at the same time avoid hanging indefintely.
* */
do {
FD_ZERO(&wrfds);
FD_SET(sock->socket, &wrfds);
efds = wrfds;
timeout.tv_sec = 0;
timeout.tv_usec = 5000; /* arbitrary */
n = select(sock->socket + 1, NULL, &wrfds, &efds, &timeout);
} while (n == -1 && php_socket_errno() == EINTR);
closesocket(sock->socket);
}
pefree(sock, php_stream_is_persistent(stream));
return 0;
}
static int php_sockop_flush(php_stream *stream TSRMLS_DC)
{
php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
return fsync(sock->socket);
}
static int php_sockop_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC)
{
php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
return fstat(sock->socket, &ssb->sb);
}
static int php_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC)
{
int oldmode;
php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
switch(option) {
case PHP_STREAM_OPTION_BLOCKING:
oldmode = sock->is_blocked;
/* no need to change anything */
if (value == oldmode)
return oldmode;
if (SUCCESS == php_set_sock_blocking(sock->socket, value TSRMLS_CC)) {
sock->is_blocked = value;
return oldmode;
}
return PHP_STREAM_OPTION_RETURN_ERR;
case PHP_STREAM_OPTION_READ_TIMEOUT:
sock->timeout = *(struct timeval*)ptrparam;
sock->timeout_event = 0;
return PHP_STREAM_OPTION_RETURN_OK;
default:
return PHP_STREAM_OPTION_RETURN_NOTIMPL;
}
}
static int php_sockop_cast(php_stream *stream, int castas, void **ret TSRMLS_DC)
{
php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
switch(castas) {
case PHP_STREAM_AS_STDIO:
#if HAVE_OPENSSL_EXT
if (sock->ssl_active)
return FAILURE;
#endif
if (ret) {
*ret = fdopen(sock->socket, stream->mode);
if (*ret)
return SUCCESS;
return FAILURE;
}
return SUCCESS;
case PHP_STREAM_AS_FD:
case PHP_STREAM_AS_SOCKETD:
#if HAVE_OPENSSL_EXT
if (sock->ssl_active)
return FAILURE;
#endif
if (ret)
*ret = (void*)sock->socket;
return SUCCESS;
default:
return FAILURE;
}
}
php_stream_ops php_stream_socket_ops = {
php_sockop_write, php_sockop_read,
php_sockop_close, php_sockop_flush,
"socket",
NULL, /* seek */
php_sockop_cast,
php_sockop_stat,
php_sockop_set_option,
};
/*
* Local variables:

View file

@ -40,6 +40,7 @@
#ifdef PHP_WIN32
#define EWOULDBLOCK WSAEWOULDBLOCK
#define EINPROGRESS WSAEWOULDBLOCK
# define fsync _commit
# define ftruncate(a, b) chsize(a, b)
#endif /* defined(PHP_WIN32) */
@ -109,6 +110,18 @@ typedef struct {
} php_sockaddr_storage;
#endif
PHPAPI int php_network_connect_socket_to_host(const char *host, unsigned short port,
int socktype, int asynchronous, struct timeval *timeout, char **error_string,
int *error_code
TSRMLS_DC);
PHPAPI int php_network_connect_socket(int sockfd,
const struct sockaddr *addr,
socklen_t addrlen,
int asynchronous,
struct timeval *timeout,
char **error_string,
int *error_code);
int php_hostconnect(const char *host, unsigned short port, int socktype, struct timeval *timeout TSRMLS_DC);
PHPAPI int php_connect_nonb(int sockfd, const struct sockaddr *addr, socklen_t addrlen, struct timeval *timeout);
@ -125,29 +138,19 @@ struct _php_netstream_data_t {
char is_blocked;
struct timeval timeout;
char timeout_event;
#if HAVE_OPENSSL_EXT
/* openssl specific bits here */
SSL *ssl_handle;
int ssl_active;
#endif
};
typedef struct _php_netstream_data_t php_netstream_data_t;
#define PHP_NETSTREAM_DATA_FROM_STREAM(stream) (php_netstream_data_t*)(stream)->abstract
extern php_stream_ops php_stream_socket_ops;
extern php_stream_ops php_stream_generic_socket_ops;
#define PHP_STREAM_IS_SOCKET (&php_stream_socket_ops)
PHPAPI php_stream *_php_stream_sock_open_from_socket(int socket, const char *persistent_id STREAMS_DC TSRMLS_DC );
/* open a connection to a host using php_hostconnect and return a stream */
PHPAPI php_stream *_php_stream_sock_open_host(const char *host, unsigned short port,
int socktype, struct timeval *timeout, const char *persistent_id STREAMS_DC TSRMLS_DC);
PHPAPI php_stream *_php_stream_sock_open_unix(const char *path, int pathlen, const char *persistent_id,
struct timeval *timeout STREAMS_DC TSRMLS_DC);
#define php_stream_sock_open_from_socket(socket, persistent) _php_stream_sock_open_from_socket((socket), (persistent) STREAMS_CC TSRMLS_CC)
#define php_stream_sock_open_host(host, port, socktype, timeout, persistent) _php_stream_sock_open_host((host), (port), (socktype), (timeout), (persistent) STREAMS_CC TSRMLS_CC)
#define php_stream_sock_open_unix(path, pathlen, persistent, timeval) _php_stream_sock_open_unix((path), (pathlen), (persistent), (timeval) STREAMS_CC TSRMLS_CC)
/* {{{ memory debug */
#define php_stream_sock_open_from_socket_rel(socket, persistent) _php_stream_sock_open_from_socket((socket), (persistent) STREAMS_REL_CC TSRMLS_CC)
@ -156,12 +159,6 @@ PHPAPI php_stream *_php_stream_sock_open_unix(const char *path, int pathlen, con
/* }}} */
#if HAVE_OPENSSL_EXT
PHPAPI int php_stream_sock_ssl_activate_with_method(php_stream *stream, int activate, SSL_METHOD *method, php_stream *session_stream TSRMLS_DC);
#define php_stream_sock_ssl_activate(stream, activate) php_stream_sock_ssl_activate_with_method((stream), (activate), SSLv23_client_method(), NULL TSRMLS_CC)
#endif
#endif /* _PHP_NETWORK_H */
/*

View file

@ -332,8 +332,22 @@ PHPAPI int _php_stream_set_option(php_stream *stream, int option, int value, voi
/* whether or not locking is supported */
#define PHP_STREAM_LOCK_SUPPORTED 1
#define php_stream_supports_lock(stream) php_stream_set_option((stream), PHP_STREAM_OPTION_LOCKING, 0, (void *) PHP_STREAM_LOCK_SUPPORTED TSRMLS_CC) == 0 ? 1 : 0
#define php_stream_lock(stream, mode) php_stream_set_option((stream), PHP_STREAM_OPTION_LOCKING, (mode), (void *) NULL TSRMLS_CC)
#define php_stream_supports_lock(stream) _php_stream_set_option((stream), PHP_STREAM_OPTION_LOCKING, 0, (void *) PHP_STREAM_LOCK_SUPPORTED TSRMLS_CC) == 0 ? 1 : 0
#define php_stream_lock(stream, mode) _php_stream_set_option((stream), PHP_STREAM_OPTION_LOCKING, (mode), (void *) NULL TSRMLS_CC)
/* option code used by the php_stream_xport_XXX api */
#define PHP_STREAM_OPTION_XPORT_API 7 /* see php_stream_transport.h */
#define PHP_STREAM_OPTION_CRYPTO_API 8 /* see php_stream_transport.h */
#define PHP_STREAM_OPTION_MMAP_API 9 /* see php_stream_mmap.h */
#define PHP_STREAM_OPTION_TRUNCATE_API 10
#define PHP_STREAM_TRUNCATE_SUPPORTED 0
#define PHP_STREAM_TRUNCATE_SET_SIZE 1 /* ptrparam is a pointer to a size_t */
#define php_stream_truncate_supported(stream) (_php_stream_set_option((stream), PHP_STREAM_OPTION_TRUNCATE_API, PHP_STREAM_TRUNCATE_SUPPORTED, NULL TSRMLS_CC) == PHP_STREAM_OPTION_RETURN_OK ? 1 : 0)
PHPAPI int _php_stream_truncate_set_size(php_stream *stream, size_t newsize TSRMLS_DC);
#define php_stream_truncate_set_size(stream, size) _php_stream_truncate_set_size((stream), (size) TSRMLS_CC)
#define PHP_STREAM_OPTION_RETURN_OK 0 /* option set OK */
#define PHP_STREAM_OPTION_RETURN_ERR -1 /* problem setting option */
@ -356,8 +370,10 @@ PHPAPI size_t _php_stream_copy_to_mem(php_stream *src, char **buf, size_t maxlen
PHPAPI size_t _php_stream_passthru(php_stream * src STREAMS_DC TSRMLS_DC);
#define php_stream_passthru(stream) _php_stream_passthru((stream) STREAMS_CC TSRMLS_CC)
#include "streams/php_stream_transport.h"
#include "streams/php_stream_plain_wrapper.h"
#include "streams/php_stream_userspace.h"
#include "streams/php_stream_mmap.h"
/* coerce the stream into some other form */
/* cast as a stdio FILE * */

View file

@ -296,28 +296,6 @@ PHPAPI FILE * _php_stream_open_wrapper_as_file(char *path, char *mode, int optio
if (stream == NULL)
return NULL;
#ifdef PHP_WIN32
/* Avoid possible strange problems when working with socket based streams */
if ((options & STREAM_OPEN_FOR_INCLUDE) && php_stream_is(stream, PHP_STREAM_IS_SOCKET)) {
char buf[CHUNK_SIZE];
fp = php_open_temporary_file(NULL, "php", NULL TSRMLS_CC);
if (fp) {
while (!php_stream_eof(stream)) {
size_t didread = php_stream_read(stream, buf, sizeof(buf));
if (didread > 0) {
fwrite(buf, 1, didread, fp);
} else {
break;
}
}
php_stream_close(stream);
rewind(fp);
return fp;
}
}
#endif
if (php_stream_cast(stream, PHP_STREAM_AS_STDIO|PHP_STREAM_CAST_TRY_HARD|PHP_STREAM_CAST_RELEASE,
(void**)&fp, REPORT_ERRORS) == FAILURE)

52
main/streams/mmap.c Normal file
View file

@ -0,0 +1,52 @@
/*
+----------------------------------------------------------------------+
| PHP Version 4 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2003 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 2.02 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available at through the world-wide-web at |
| http://www.php.net/license/2_02.txt. |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Author: Wez Furlong <wez@thebrainroom.com> |
+----------------------------------------------------------------------+
*/
/* $Id$ */
/* Memory Mapping interface for streams */
#include "php.h"
#include "php_streams_int.h"
PHPAPI char *_php_stream_mmap_range(php_stream *stream, size_t offset, size_t length, php_stream_mmap_operation_t mode, size_t *mapped_len TSRMLS_DC)
{
php_stream_mmap_range range = { offset, length, mode, NULL };
/* TODO: Enforce system policy and limits for mmap sizes ? */
if (PHP_STREAM_OPTION_RETURN_OK == php_stream_set_option(stream, PHP_STREAM_OPTION_MMAP_API, PHP_STREAM_MMAP_MAP_RANGE, &range)) {
if (mapped_len) {
*mapped_len = range.length;
}
return range.mapped;
}
return NULL;
}
PHPAPI int _php_stream_mmap_unmap(php_stream *stream TSRMLS_DC)
{
return php_stream_set_option(stream, PHP_STREAM_OPTION_MMAP_API, PHP_STREAM_MMAP_UNMAP, NULL) == PHP_STREAM_OPTION_RETURN_OK ? 1 : 0;
}
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
*/

View file

@ -0,0 +1,82 @@
/*
+----------------------------------------------------------------------+
| PHP Version 4 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2003 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 2.02 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available at through the world-wide-web at |
| http://www.php.net/license/2_02.txt. |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Author: Wez Furlong <wez@thebrainroom.com> |
+----------------------------------------------------------------------+
*/
/* $Id$ */
/* Memory Mapping interface for streams.
* The intention is to provide a uniform interface over the most common
* operations that are used within PHP itself, rather than a complete
* API for all memory mapping needs.
*
* ATM, we support only mmap(), but win32 memory mapping support will
* follow soon.
* */
typedef enum {
/* Does the stream support mmap ? */
PHP_STREAM_MMAP_SUPPORTED,
/* Request a range and offset to be mapped;
* while mapped, you MUST NOT use any read/write functions
* on the stream (win9x compatibility) */
PHP_STREAM_MMAP_MAP_RANGE,
/* Unmap the last range that was mapped for the stream */
PHP_STREAM_MMAP_UNMAP
} php_stream_mmap_operation_t;
typedef enum {
PHP_STREAM_MAP_MODE_READONLY,
PHP_STREAM_MAP_MODE_READWRITE,
PHP_STREAM_MAP_MODE_SHARED_READONLY,
PHP_STREAM_MAP_MODE_SHARED_READWRITE
} php_stream_mmap_access_t;
typedef struct {
/* requested offset and length.
* If length is 0, the whole file is mapped */
size_t offset;
size_t length;
php_stream_mmap_access_t mode;
/* returned mapped address */
char *mapped;
} php_stream_mmap_range;
#define php_stream_mmap_supported(stream) (_php_stream_set_option((stream), PHP_STREAM_OPTION_MMAP_API, PHP_STREAM_MMAP_SUPPORTED, NULL TSRMLS_CC) == 0 ? 1 : 0)
/* Returns 1 if the stream in its current state can be memory mapped,
* 0 otherwise */
#define php_stream_mmap_possible(stream) (!php_stream_is_filtered((stream)) && php_stream_mmap_supported((stream)))
PHPAPI char *_php_stream_mmap_range(php_stream *stream, size_t offset, size_t length, php_stream_mmap_operation_t mode, size_t *mapped_len TSRMLS_DC);
#define php_stream_mmap_range(stream, offset, length, mode, mapped_len) _php_stream_mmap_range((stream), (offset), (length), (mode), (mapped_len) TSRMLS_CC)
/* un-maps the last mapped range */
PHPAPI int _php_stream_mmap_unmap(php_stream *stream TSRMLS_DC);
#define php_stream_mmap_unmap(stream) _php_stream_mmap_unmap((stream) TSRMLS_CC)
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
*/

View file

@ -0,0 +1,153 @@
/*
+----------------------------------------------------------------------+
| PHP Version 4 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2003 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 2.02 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available at through the world-wide-web at |
| http://www.php.net/license/2_02.txt. |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Author: Wez Furlong <wez@thebrainroom.com> |
+----------------------------------------------------------------------+
*/
/* $Id$ */
typedef php_stream *(php_stream_transport_factory_func)(const char *proto, long protolen,
char *resourcename, long resourcenamelen,
const char *persistent_id, int options, int flags,
struct timeval *timeout,
php_stream_context *context STREAMS_DC TSRMLS_DC);
typedef php_stream_transport_factory_func *php_stream_transport_factory;
PHPAPI int php_stream_xport_register(char *protocol, php_stream_transport_factory factory TSRMLS_DC);
PHPAPI int php_stream_xport_unregister(char *protocol TSRMLS_DC);
#define STREAM_XPORT_CLIENT 0
#define STREAM_XPORT_SERVER 1
#define STREAM_XPORT_CONNECT 2
#define STREAM_XPORT_BIND 4
#define STREAM_XPORT_LISTEN 8
#define STREAM_XPORT_CONNECT_ASYNC 16
/* Open a client or server socket connection */
PHPAPI php_stream *_php_stream_xport_create(const char *name, long namelen, int options,
int flags, const char *persistent_id,
struct timeval *timeout,
php_stream_context *context,
char **error_string,
int *error_code
STREAMS_DC TSRMLS_DC);
#define php_stream_xport_create(name, namelen, options, flags, persistent_id, timeout, context, estr, ecode) \
_php_stream_xport_create(name, namelen, options, flags, persistent_id, timeout, context, estr, ecode STREAMS_CC TSRMLS_CC)
/* Bind the stream to a local address */
PHPAPI int php_stream_xport_bind(php_stream *stream,
const char *name, long namelen,
char **error_text
TSRMLS_DC);
/* Connect to a remote address */
PHPAPI int php_stream_xport_connect(php_stream *stream,
const char *name, long namelen,
int asynchronous,
struct timeval *timeout,
char **error_text,
int *error_code
TSRMLS_DC);
/* Prepare to listen */
PHPAPI int php_stream_xport_listen(php_stream *stream,
int backlog,
char **error_text
TSRMLS_DC);
/* Get the next client and their address as a string, or the underlying address
* structure. You must efree either of these if you request them */
PHPAPI int php_stream_xport_accept(php_stream *stream, php_stream **client,
char **textaddr, long *textaddrlen,
void **addr, size_t *addrlen,
struct timeval *timeout,
char **error_text
TSRMLS_DC);
/* Structure definition for the set_option interface that the above functions wrap */
typedef struct _php_stream_xport_param {
enum {
STREAM_XPORT_OP_BIND, STREAM_XPORT_OP_CONNECT,
STREAM_XPORT_OP_LISTEN, STREAM_XPORT_OP_ACCEPT,
STREAM_XPORT_OP_CONNECT_ASYNC
} op;
int want_addr:1;
int want_textaddr:1;
int want_errortext:1;
struct {
char *name;
long namelen;
int backlog;
struct timeval *timeout;
} inputs;
struct {
php_stream *client;
int returncode;
void *addr;
size_t addrlen;
char *textaddr;
long textaddrlen;
char *error_text;
int error_code;
} outputs;
} php_stream_xport_param;
/* These functions provide crypto support on the underlying transport */
typedef enum {
STREAM_CRYPTO_METHOD_SSLv2_CLIENT,
STREAM_CRYPTO_METHOD_SSLv3_CLIENT,
STREAM_CRYPTO_METHOD_SSLv23_CLIENT,
STREAM_CRYPTO_METHOD_TLS_CLIENT,
STREAM_CRYPTO_METHOD_SSLv2_SERVER,
STREAM_CRYPTO_METHOD_SSLv3_SERVER,
STREAM_CRYPTO_METHOD_SSLv23_SERVER,
STREAM_CRYPTO_METHOD_TLS_SERVER
} php_stream_xport_crypt_method_t;
PHPAPI int php_stream_xport_crypto_setup(php_stream *stream, php_stream_xport_crypt_method_t crypto_method, php_stream *session_stream TSRMLS_DC);
PHPAPI int php_stream_xport_crypto_enable(php_stream *stream, int activate TSRMLS_DC);
typedef struct _php_stream_xport_crypto_param {
enum {
STREAM_XPORT_CRYPTO_OP_SETUP,
STREAM_XPORT_CRYPTO_OP_ENABLE
} op;
struct {
int activate;
php_stream_xport_crypt_method_t method;
php_stream *session;
} inputs;
struct {
int returncode;
} outputs;
} php_stream_xport_crypto_param;
PHPAPI HashTable *php_stream_xport_get_hash(void);
PHPAPI php_stream_transport_factory_func php_stream_generic_socket_factory;
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
*/

View file

@ -56,4 +56,6 @@ extern php_stream_wrapper php_plain_files_wrapper;
#define S_ISREG(mode) (((mode)&S_IFMT) == S_IFREG)
#endif
void php_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper TSRMLS_DC);
void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper, const char *path, const char *caption TSRMLS_DC);

View file

@ -32,6 +32,9 @@
#if HAVE_SYS_FILE_H
#include <sys/file.h>
#endif
#ifdef HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif
#include "php_streams_int.h"
@ -135,12 +138,17 @@ typedef struct {
int fd; /* underlying file descriptor */
int is_process_pipe; /* use pclose instead of fclose */
int is_pipe; /* don't try and seek */
int lock_flag; /* stores the lock state */
int lock_flag; /* stores the lock state */
char *temp_file_name; /* if non-null, this is the path to a temporary file that
* is to be deleted when the stream is closed */
#if HAVE_FLUSHIO
char last_op;
#endif
#if HAVE_MMAP
char *last_mapped_addr;
size_t last_mapped_len;
#endif
} php_stdio_stream_data;
PHPAPI php_stream *_php_stream_fopen_temporary_file(const char *dir, const char *pfx, char **opened_path STREAMS_DC TSRMLS_DC)
@ -348,6 +356,13 @@ static int php_stdiop_close(php_stream *stream, int close_handle TSRMLS_DC)
php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract;
assert(data != NULL);
#if HAVE_MMAP
if (data->last_mapped_addr) {
munmap(data->last_mapped_addr, data->last_mapped_len);
data->last_mapped_addr = NULL;
}
#endif
if (close_handle) {
if (data->lock_flag != LOCK_UN) {
@ -572,8 +587,76 @@ static int php_stdiop_set_option(php_stream *stream, int option, int value, void
}
break;
case PHP_STREAM_OPTION_MMAP_API:
#if HAVE_MMAP
{
php_stream_mmap_range *range = (php_stream_mmap_range*)ptrparam;
struct stat sbuf;
int prot, flags;
switch (value) {
case PHP_STREAM_MMAP_SUPPORTED:
return fd == -1 ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK;
case PHP_STREAM_MMAP_MAP_RANGE:
fstat(fd, &sbuf);
if (range->length == 0 || range->length > sbuf.st_size) {
range->length = sbuf.st_size;
}
switch (range->mode) {
case PHP_STREAM_MAP_MODE_READONLY:
prot = PROT_READ;
flags = MAP_PRIVATE;
break;
case PHP_STREAM_MAP_MODE_READWRITE:
prot = PROT_READ | PROT_WRITE;
flags = MAP_PRIVATE;
break;
case PHP_STREAM_MAP_MODE_SHARED_READONLY:
prot = PROT_READ;
flags = MAP_SHARED;
break;
case PHP_STREAM_MAP_MODE_SHARED_READWRITE:
prot = PROT_READ | PROT_WRITE;
flags = MAP_SHARED;
break;
default:
return PHP_STREAM_OPTION_RETURN_ERR;
}
range->mapped = (char*)mmap(NULL, range->length, prot, flags, fd, range->offset);
if (range->mapped == (char*)MAP_FAILED) {
range->mapped = NULL;
return PHP_STREAM_OPTION_RETURN_ERR;
}
/* remember the mapping */
data->last_mapped_addr = range->mapped;
data->last_mapped_len = range->length;
return PHP_STREAM_OPTION_RETURN_OK;
case PHP_STREAM_MMAP_UNMAP:
if (data->last_mapped_addr) {
munmap(data->last_mapped_addr, data->last_mapped_len);
data->last_mapped_addr = NULL;
return PHP_STREAM_OPTION_RETURN_OK;
}
return PHP_STREAM_OPTION_RETURN_ERR;
}
}
#endif
return PHP_STREAM_OPTION_RETURN_NOTIMPL;
case PHP_STREAM_OPTION_TRUNCATE_API:
switch (value) {
case PHP_STREAM_TRUNCATE_SUPPORTED:
return fd == -1 ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK;
case PHP_STREAM_TRUNCATE_SET_SIZE:
return ftruncate(fd, *(size_t*)ptrparam) == 0 ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR;
}
default:
return -1;
return PHP_STREAM_OPTION_RETURN_NOTIMPL;
}
}
@ -588,6 +671,7 @@ PHPAPI php_stream_ops php_stream_stdio_ops = {
};
/* }}} */
/* {{{ plain files opendir/readdir implementation */
static size_t php_plain_files_dirstream_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
{
DIR *dir = (DIR*)stream->abstract;
@ -658,7 +742,7 @@ static php_stream *php_plain_files_dir_opener(php_stream_wrapper *wrapper, char
return stream;
}
/* }}} */
static php_stream *php_plain_files_stream_opener(php_stream_wrapper *wrapper, char *path, char *mode,

View file

@ -29,9 +29,6 @@
#include "ext/standard/file.h"
#include "ext/standard/basic_functions.h" /* for BG(mmap_file) (not strictly required) */
#include "ext/standard/php_string.h" /* for php_memnstr, used by php_stream_get_record() */
#ifdef HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif
#include <stddef.h>
#include <fcntl.h>
#include "php_streams_int.h"
@ -103,7 +100,7 @@ PHPAPI int php_stream_from_persistent_id(const char *persistent_id, php_stream *
/* }}} */
/* {{{ wrapper error reporting */
static void display_wrapper_errors(php_stream_wrapper *wrapper, const char *path, const char *caption TSRMLS_DC)
void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper, const char *path, const char *caption TSRMLS_DC)
{
char *tmp = estrdup(path);
char *msg;
@ -152,7 +149,7 @@ static void display_wrapper_errors(php_stream_wrapper *wrapper, const char *path
efree(msg);
}
static void tidy_wrapper_error_log(php_stream_wrapper *wrapper TSRMLS_DC)
void php_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper TSRMLS_DC)
{
if (wrapper) {
/* tidy up the error stack */
@ -1061,52 +1058,37 @@ PHPAPI int _php_stream_set_option(php_stream *stream, int option, int value, voi
return ret;
}
PHPAPI int _php_stream_truncate_set_size(php_stream *stream, size_t newsize TSRMLS_DC)
{
return php_stream_set_option(stream, PHP_STREAM_OPTION_TRUNCATE_API, PHP_STREAM_TRUNCATE_SET_SIZE, &newsize);
}
PHPAPI size_t _php_stream_passthru(php_stream * stream STREAMS_DC TSRMLS_DC)
{
size_t bcount = 0;
int ready = 0;
char buf[8192];
#ifdef HAVE_MMAP
int fd;
#endif
int b;
#ifdef HAVE_MMAP
if (!php_stream_is(stream, PHP_STREAM_IS_SOCKET)
&& !php_stream_is_filtered(stream)
&& php_stream_tell(stream) == 0
&& SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD, (void*)&fd, 0))
{
struct stat sbuf;
off_t off;
void *p;
size_t len;
if (php_stream_mmap_possible(stream)) {
char *p;
size_t mapped;
fstat(fd, &sbuf);
p = php_stream_mmap_range(stream, php_stream_tell(stream), 0, PHP_STREAM_MAP_MODE_SHARED_READONLY, &mapped);
if (sbuf.st_size > sizeof(buf)) {
off = php_stream_tell(stream);
len = sbuf.st_size - off;
p = mmap(0, len, PROT_READ, MAP_SHARED, fd, off);
if (p != (void *) MAP_FAILED) {
BG(mmap_file) = p;
BG(mmap_len) = len;
PHPWRITE(p, len);
BG(mmap_file) = NULL;
munmap(p, len);
bcount += len;
ready = 1;
}
if (p) {
PHPWRITE(p, mapped);
php_stream_mmap_unmap(stream);
return mapped;
}
}
#endif
if(!ready) {
int b;
while ((b = php_stream_read(stream, buf, sizeof(buf))) > 0) {
PHPWRITE(buf, b);
bcount += b;
}
while ((b = php_stream_read(stream, buf, sizeof(buf))) > 0) {
PHPWRITE(buf, b);
bcount += b;
}
return bcount;
}
@ -1118,9 +1100,6 @@ PHPAPI size_t _php_stream_copy_to_mem(php_stream *src, char **buf, size_t maxlen
size_t len = 0, max_len;
int step = CHUNK_SIZE;
int min_room = CHUNK_SIZE / 4;
#if HAVE_MMAP
int srcfd;
#endif
if (buf)
*buf = NULL;
@ -1131,50 +1110,25 @@ PHPAPI size_t _php_stream_copy_to_mem(php_stream *src, char **buf, size_t maxlen
if (maxlen == PHP_STREAM_COPY_ALL)
maxlen = 0;
#if HAVE_MMAP
/* try and optimize the case where we are copying from the start of a plain file.
* We could probably make this work in more situations, but I don't trust the stdio
* buffering layer.
* */
if ( php_stream_is(src, PHP_STREAM_IS_STDIO) &&
!php_stream_is_filtered(src) &&
php_stream_tell(src) == 0 &&
SUCCESS == php_stream_cast(src, PHP_STREAM_AS_FD, (void**)&srcfd, 0))
{
struct stat sbuf;
if (php_stream_mmap_possible(src)) {
char *p;
size_t mapped;
if (fstat(srcfd, &sbuf) == 0) {
void *srcfile;
p = php_stream_mmap_range(src, php_stream_tell(src), maxlen, PHP_STREAM_MAP_MODE_SHARED_READONLY, &mapped);
#if STREAM_DEBUG
fprintf(stderr, "mmap attempt: maxlen=%d filesize=%ld\n", maxlen, sbuf.st_size);
#endif
if (maxlen > sbuf.st_size || maxlen == 0)
maxlen = sbuf.st_size;
#if STREAM_DEBUG
fprintf(stderr, "mmap attempt: will map maxlen=%d\n", maxlen);
#endif
srcfile = mmap(NULL, maxlen, PROT_READ, MAP_SHARED, srcfd, 0);
if (srcfile != (void*)MAP_FAILED) {
if (p) {
*buf = pemalloc_rel_orig(mapped + 1, persistent);
*buf = pemalloc_rel_orig(maxlen + 1, persistent);
if (*buf) {
memcpy(*buf, srcfile, maxlen);
(*buf)[maxlen] = '\0';
ret = maxlen;
}
munmap(srcfile, maxlen);
return ret;
if (*buf) {
memcpy(*buf, p, mapped);
(*buf)[mapped] = '\0';
}
php_stream_mmap_unmap(src);
return mapped;
}
/* fall through - we might be able to copy in smaller chunks */
}
#endif
ptr = *buf = pemalloc_rel_orig(step, persistent);
max_len = step;
@ -1204,9 +1158,6 @@ PHPAPI size_t _php_stream_copy_to_stream(php_stream *src, php_stream *dest, size
size_t haveread = 0;
size_t didread;
php_stream_statbuf ssbuf;
#if HAVE_MMAP
int srcfd;
#endif
if (maxlen == 0)
return 0;
@ -1214,41 +1165,6 @@ PHPAPI size_t _php_stream_copy_to_stream(php_stream *src, php_stream *dest, size
if (maxlen == PHP_STREAM_COPY_ALL)
maxlen = 0;
#if HAVE_MMAP
/* try and optimize the case where we are copying from the start of a plain file.
* We could probably make this work in more situations, but I don't trust the stdio
* buffering layer.
* */
if ( php_stream_is(src, PHP_STREAM_IS_STDIO) &&
!php_stream_is_filtered(src) &&
php_stream_tell(src) == 0 &&
SUCCESS == php_stream_cast(src, PHP_STREAM_AS_FD, (void**)&srcfd, 0))
{
struct stat sbuf;
if (fstat(srcfd, &sbuf) == 0) {
void *srcfile;
/* in the event that the source file is 0 bytes, return 1 to indicate success
* because opening the file to write had already created a copy */
if(sbuf.st_size ==0)
return 1;
if (maxlen > sbuf.st_size || maxlen == 0)
maxlen = sbuf.st_size;
srcfile = mmap(NULL, maxlen, PROT_READ, MAP_SHARED, srcfd, 0);
if (srcfile != (void*)MAP_FAILED) {
haveread = php_stream_write(dest, srcfile, maxlen);
munmap(srcfile, maxlen);
return haveread;
}
}
/* fall through - we might be able to copy in smaller chunks */
}
#endif
if (php_stream_stat(src, &ssbuf) == 0) {
/* in the event that the source file is 0 bytes, return 1 to indicate success
* because opening the file to write had already created a copy */
@ -1257,6 +1173,21 @@ PHPAPI size_t _php_stream_copy_to_stream(php_stream *src, php_stream *dest, size
}
}
if (php_stream_mmap_possible(src)) {
char *p;
size_t mapped;
p = php_stream_mmap_range(src, php_stream_tell(src), maxlen, PHP_STREAM_MAP_MODE_SHARED_READONLY, &mapped);
if (p) {
haveread = php_stream_write(dest, p, mapped);
php_stream_mmap_unmap(src);
return mapped;
}
}
while(1) {
readchunk = sizeof(buf);
@ -1323,6 +1254,18 @@ int php_init_stream_wrappers(int module_number TSRMLS_DC)
zend_hash_init(&url_stream_wrappers_hash, 0, NULL, NULL, 1) == SUCCESS
&&
zend_hash_init(php_get_stream_filters_hash(), 0, NULL, NULL, 1) == SUCCESS
&&
zend_hash_init(php_stream_xport_get_hash(), 0, NULL, NULL, 1) == SUCCESS
&&
php_stream_xport_register("tcp", php_stream_generic_socket_factory TSRMLS_CC) == SUCCESS
&&
php_stream_xport_register("udp", php_stream_generic_socket_factory TSRMLS_CC) == SUCCESS
#ifdef AF_UNIX
&&
php_stream_xport_register("unix", php_stream_generic_socket_factory TSRMLS_CC) == SUCCESS
&&
php_stream_xport_register("udg", php_stream_generic_socket_factory TSRMLS_CC) == SUCCESS
#endif
) ? SUCCESS : FAILURE;
}
@ -1330,6 +1273,7 @@ int php_shutdown_stream_wrappers(int module_number TSRMLS_DC)
{
zend_hash_destroy(&url_stream_wrappers_hash);
zend_hash_destroy(php_get_stream_filters_hash());
zend_hash_destroy(php_stream_xport_get_hash());
return SUCCESS;
}
@ -1451,9 +1395,9 @@ PHPAPI php_stream *_php_stream_opendir(char *path, int options,
php_stream_wrapper_log_error(wrapper, options ^ REPORT_ERRORS TSRMLS_CC, "not implemented");
}
if (stream == NULL && (options & REPORT_ERRORS)) {
display_wrapper_errors(wrapper, path, "failed to open dir" TSRMLS_CC);
php_stream_display_wrapper_errors(wrapper, path, "failed to open dir" TSRMLS_CC);
}
tidy_wrapper_error_log(wrapper TSRMLS_CC);
php_stream_tidy_wrapper_error_log(wrapper TSRMLS_CC);
return stream;
}
@ -1498,15 +1442,13 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(char *path, char *mode, int optio
if (wrapper) {
/* prepare error stack */
wrapper->err_count = 0;
wrapper->err_stack = NULL;
stream = wrapper->wops->stream_opener(wrapper,
path_to_open, mode, options ^ REPORT_ERRORS,
opened_path, context STREAMS_REL_CC TSRMLS_CC);
if (stream)
if (stream) {
stream->wrapper = wrapper;
}
}
#if ZEND_DEBUG
@ -1554,9 +1496,9 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(char *path, char *mode, int optio
}
if (stream == NULL && (options & REPORT_ERRORS)) {
display_wrapper_errors(wrapper, path, "failed to open stream" TSRMLS_CC);
php_stream_display_wrapper_errors(wrapper, path, "failed to open stream" TSRMLS_CC);
}
tidy_wrapper_error_log(wrapper TSRMLS_CC);
php_stream_tidy_wrapper_error_log(wrapper TSRMLS_CC);
#if ZEND_DEBUG
if (stream == NULL && copy_of_path != NULL) {
efree(copy_of_path);

341
main/streams/transports.c Normal file
View file

@ -0,0 +1,341 @@
/*
+----------------------------------------------------------------------+
| PHP Version 4 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2003 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 2.02 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available at through the world-wide-web at |
| http://www.php.net/license/2_02.txt. |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Author: Wez Furlong <wez@thebrainroom.com> |
+----------------------------------------------------------------------+
*/
/* $Id$ */
#include "php.h"
#include "php_streams_int.h"
#include "ext/standard/file.h"
static HashTable xport_hash;
PHPAPI HashTable *php_stream_xport_get_hash(void)
{
return &xport_hash;
}
PHPAPI int php_stream_xport_register(char *protocol, php_stream_transport_factory factory TSRMLS_DC)
{
return zend_hash_update(&xport_hash, protocol, strlen(protocol), &factory, sizeof(factory), NULL);
}
PHPAPI int php_stream_xport_unregister(char *protocol TSRMLS_DC)
{
return zend_hash_del(&xport_hash, protocol, strlen(protocol));
}
#define ERR_REPORT(out_err, fmt, arg) \
if (out_err) { spprintf(out_err, 0, fmt, arg); } \
else { php_error_docref(NULL TSRMLS_CC, E_WARNING, fmt, arg); }
#define ERR_RETURN(out_err, local_err, fmt) \
if (out_err) { *out_err = local_err; } \
else { php_error_docref(NULL TSRMLS_CC, E_WARNING, fmt, local_err ? local_err : "Unspecified error"); \
if (local_err) { efree(local_err); local_err = NULL; } \
}
PHPAPI php_stream *_php_stream_xport_create(const char *name, long namelen, int options,
int flags, const char *persistent_id,
struct timeval *timeout,
php_stream_context *context,
char **error_string,
int *error_code
STREAMS_DC TSRMLS_DC)
{
php_stream *stream = NULL;
php_stream_transport_factory *factory = NULL;
const char *p, *protocol = NULL;
int n = 0, failed = 0;
char *error_text = NULL;
struct timeval default_timeout = { FG(default_socket_timeout), 0 };
if (timeout == NULL) {
timeout = &default_timeout;
}
/* check for a cached persistent socket */
if (persistent_id) {
switch(php_stream_from_persistent_id(persistent_id, &stream TSRMLS_CC)) {
case PHP_STREAM_PERSISTENT_SUCCESS:
/* TODO: check if the socket is still live */
return stream;
case PHP_STREAM_PERSISTENT_FAILURE:
default:
/* failed; get a new one */
}
}
for (p = name; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++) {
n++;
}
if ((*p == ':') && (n > 1) && !strncmp("://", p, 3)) {
protocol = name;
name = p + 3;
namelen -= n + 3;
} else {
protocol = "tcp";
n = 3;
}
if (protocol) {
if (FAILURE == zend_hash_find(&xport_hash, (char*)protocol, n, (void**)&factory)) {
char wrapper_name[32];
if (n >= sizeof(wrapper_name))
n = sizeof(wrapper_name) - 1;
PHP_STRLCPY(wrapper_name, protocol, sizeof(wrapper_name), n);
ERR_REPORT(error_string, "Unable to find the socket transport \"%s\" - did you forget to enable it when you configured PHP?",
wrapper_name);
return NULL;
}
}
if (factory == NULL) {
/* should never happen */
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not find a factory !?");
return NULL;
}
stream = (*factory)(protocol, n,
(char*)name, namelen, persistent_id, options, flags, timeout,
context STREAMS_REL_CC TSRMLS_CC);
if (stream) {
stream->context = context;
if ((flags & STREAM_XPORT_SERVER) == 0) {
/* client */
if (flags & STREAM_XPORT_CONNECT) {
if (0 != php_stream_xport_connect(stream, name, namelen,
flags & STREAM_XPORT_OP_CONNECT_ASYNC ? 1 : 0,
timeout, &error_text, error_code TSRMLS_CC)) {
ERR_RETURN(error_string, error_text, "connect() failed: %s");
failed = 1;
}
}
} else {
/* server */
if (flags & STREAM_XPORT_BIND) {
if (0 != php_stream_xport_bind(stream, name, namelen, &error_text TSRMLS_CC)) {
ERR_RETURN(error_string, error_text, "bind() failed: %s");
failed = 1;
} else if (flags & STREAM_XPORT_LISTEN) {
if (0 != php_stream_xport_listen(stream, 5, &error_text TSRMLS_CC)) {
ERR_RETURN(error_string, error_text, "listen() failed: %s");
failed = 1;
}
}
}
}
}
if (failed) {
/* failure means that they don't get a stream to play with */
php_stream_close(stream);
stream = NULL;
}
return stream;
}
/* Bind the stream to a local address */
PHPAPI int php_stream_xport_bind(php_stream *stream,
const char *name, long namelen,
char **error_text
TSRMLS_DC)
{
php_stream_xport_param param;
int ret;
memset(&param, 0, sizeof(param));
param.op = STREAM_XPORT_OP_BIND;
param.inputs.name = (char*)name;
param.inputs.namelen = namelen;
param.want_errortext = error_text ? 1 : 0;
ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, &param);
if (ret == PHP_STREAM_OPTION_RETURN_OK) {
if (error_text) {
*error_text = param.outputs.error_text;
}
return param.outputs.returncode;
}
return ret;
}
/* Connect to a remote address */
PHPAPI int php_stream_xport_connect(php_stream *stream,
const char *name, long namelen,
int asynchronous,
struct timeval *timeout,
char **error_text,
int *error_code
TSRMLS_DC)
{
php_stream_xport_param param;
int ret;
memset(&param, 0, sizeof(param));
param.op = asynchronous ? STREAM_XPORT_OP_CONNECT_ASYNC: STREAM_XPORT_OP_CONNECT;
param.inputs.name = (char*)name;
param.inputs.namelen = namelen;
param.inputs.timeout = timeout;
param.want_errortext = error_text ? 1 : 0;
ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, &param);
if (ret == PHP_STREAM_OPTION_RETURN_OK) {
if (error_text) {
*error_text = param.outputs.error_text;
}
if (error_code) {
*error_code = param.outputs.error_code;
}
return param.outputs.returncode;
}
return ret;
}
/* Prepare to listen */
PHPAPI int php_stream_xport_listen(php_stream *stream, int backlog, char **error_text TSRMLS_DC)
{
php_stream_xport_param param;
int ret;
memset(&param, 0, sizeof(param));
param.op = STREAM_XPORT_OP_LISTEN;
param.inputs.backlog = backlog;
param.want_errortext = error_text ? 1 : 0;
ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, &param);
if (ret == PHP_STREAM_OPTION_RETURN_OK) {
if (error_text) {
*error_text = param.outputs.error_text;
}
return param.outputs.returncode;
}
return ret;
}
/* Get the next client and their address (as a string) */
PHPAPI int php_stream_xport_accept(php_stream *stream, php_stream **client,
char **textaddr, long *textaddrlen,
void **addr, size_t *addrlen,
struct timeval *timeout,
char **error_text
TSRMLS_DC)
{
php_stream_xport_param param;
int ret;
memset(&param, 0, sizeof(param));
param.op = STREAM_XPORT_OP_BIND;
param.inputs.timeout = timeout;
param.want_addr = addr ? 1 : 0;
param.want_textaddr = textaddr ? 1 : 0;
param.want_errortext = error_text ? 1 : 0;
ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, &param);
if (ret == PHP_STREAM_OPTION_RETURN_OK) {
*client = param.outputs.client;
if (addr) {
*addr = param.outputs.addr;
*addrlen = param.outputs.addrlen;
}
if (textaddr) {
*textaddr = param.outputs.textaddr;
*textaddrlen = param.outputs.textaddrlen;
}
if (error_text) {
*error_text = param.outputs.error_text;
}
return param.outputs.returncode;
}
return ret;
}
PHPAPI int php_stream_xport_crypto_setup(php_stream *stream, php_stream_xport_crypt_method_t crypto_method, php_stream *session_stream TSRMLS_DC)
{
php_stream_xport_crypto_param param;
int ret;
memset(&param, 0, sizeof(param));
param.op = STREAM_XPORT_CRYPTO_OP_SETUP;
param.inputs.method = crypto_method;
param.inputs.session = session_stream;
ret = php_stream_set_option(stream, PHP_STREAM_OPTION_CRYPTO_API, 0, &param);
if (ret == PHP_STREAM_OPTION_RETURN_OK) {
return param.outputs.returncode;
}
php_error_docref("streams.crypto" TSRMLS_CC, E_WARNING, "this stream does not support SSL/crypto");
return ret;
}
PHPAPI int php_stream_xport_crypto_enable(php_stream *stream, int activate TSRMLS_DC)
{
php_stream_xport_crypto_param param;
int ret;
memset(&param, 0, sizeof(param));
param.op = STREAM_XPORT_CRYPTO_OP_ENABLE;
param.inputs.activate = activate;
ret = php_stream_set_option(stream, PHP_STREAM_OPTION_CRYPTO_API, 0, &param);
if (ret == PHP_STREAM_OPTION_RETURN_OK) {
return param.outputs.returncode;
}
php_error_docref("streams.crypto" TSRMLS_CC, E_WARNING, "this stream does not support SSL/crypto");
return ret;
}
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
*/

502
main/streams/xp_socket.c Normal file
View file

@ -0,0 +1,502 @@
/*
+----------------------------------------------------------------------+
| PHP Version 4 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2003 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 2.02 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available at through the world-wide-web at |
| http://www.php.net/license/2_02.txt. |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Author: Wez Furlong <wez@thebrainroom.com> |
+----------------------------------------------------------------------+
*/
/* $Id$ */
#include "php.h"
#include "ext/standard/file.h"
#include "streams/php_streams_int.h"
#include "php_network.h"
#if defined(AF_UNIX)
#include <sys/un.h>
#endif
static int php_tcp_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC);
/* {{{ Generic socket stream operations */
static size_t php_sockop_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
{
php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
size_t didwrite;
if (sock->socket == -1) {
return 0;
}
didwrite = send(sock->socket, buf, count, 0);
if (didwrite <= 0) {
char *estr = php_socket_strerror(php_socket_errno(), NULL, 0);
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "send of %d bytes failed with errno=%d %s",
count, php_socket_errno(), estr);
efree(estr);
}
if (didwrite > 0) {
php_stream_notify_progress_increment(stream->context, didwrite, 0);
}
return didwrite;
}
static void php_sock_stream_wait_for_data(php_stream *stream, php_netstream_data_t *sock TSRMLS_DC)
{
fd_set fdr, tfdr;
int retval;
struct timeval timeout, *ptimeout;
if (sock->socket == -1) {
return;
}
FD_ZERO(&fdr);
FD_SET(sock->socket, &fdr);
sock->timeout_event = 0;
if (sock->timeout.tv_sec == -1)
ptimeout = NULL;
else
ptimeout = &timeout;
while(1) {
tfdr = fdr;
timeout = sock->timeout;
retval = select(sock->socket + 1, &tfdr, NULL, NULL, ptimeout);
if (retval == 0)
sock->timeout_event = 1;
if (retval >= 0)
break;
}
}
static size_t php_sockop_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
{
php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
int nr_bytes = 0;
if (sock->socket == -1) {
return 0;
}
if (sock->is_blocked) {
php_sock_stream_wait_for_data(stream, sock TSRMLS_CC);
if (sock->timeout_event)
return 0;
}
nr_bytes = recv(sock->socket, buf, count, 0);
if (nr_bytes == 0 || (nr_bytes == -1 && php_socket_errno() != EWOULDBLOCK)) {
stream->eof = 1;
}
if (nr_bytes > 0)
php_stream_notify_progress_increment(stream->context, nr_bytes, 0);
return nr_bytes;
}
static int php_sockop_close(php_stream *stream, int close_handle TSRMLS_DC)
{
php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
fd_set wrfds, efds;
int n;
struct timeval timeout;
if (close_handle) {
if (sock->socket != -1) {
/* prevent more data from coming in */
shutdown(sock->socket, SHUT_RD);
/* try to make sure that the OS sends all data before we close the connection.
* Essentially, we are waiting for the socket to become writeable, which means
* that all pending data has been sent.
* We use a small timeout which should encourage the OS to send the data,
* but at the same time avoid hanging indefintely.
* */
do {
FD_ZERO(&wrfds);
FD_SET(sock->socket, &wrfds);
efds = wrfds;
timeout.tv_sec = 0;
timeout.tv_usec = 5000; /* arbitrary */
n = select(sock->socket + 1, NULL, &wrfds, &efds, &timeout);
} while (n == -1 && php_socket_errno() == EINTR);
closesocket(sock->socket);
sock->socket = -1;
}
}
pefree(sock, php_stream_is_persistent(stream));
return 0;
}
static int php_sockop_flush(php_stream *stream TSRMLS_DC)
{
php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
return fsync(sock->socket);
}
static int php_sockop_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC)
{
php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
return fstat(sock->socket, &ssb->sb);
}
static int php_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC)
{
int oldmode;
php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
php_stream_xport_param *xparam;
switch(option) {
case PHP_STREAM_OPTION_BLOCKING:
oldmode = sock->is_blocked;
/* no need to change anything */
if (value == oldmode)
return oldmode;
if (SUCCESS == php_set_sock_blocking(sock->socket, value TSRMLS_CC)) {
sock->is_blocked = value;
return oldmode;
}
return PHP_STREAM_OPTION_RETURN_ERR;
case PHP_STREAM_OPTION_READ_TIMEOUT:
sock->timeout = *(struct timeval*)ptrparam;
sock->timeout_event = 0;
return PHP_STREAM_OPTION_RETURN_OK;
case PHP_STREAM_OPTION_XPORT_API:
xparam = (php_stream_xport_param *)ptrparam;
switch (xparam->op) {
case STREAM_XPORT_OP_LISTEN:
xparam->outputs.returncode = listen(sock->socket, 5);
return PHP_STREAM_OPTION_RETURN_OK;
default:
return PHP_STREAM_OPTION_RETURN_NOTIMPL;
}
default:
return PHP_STREAM_OPTION_RETURN_NOTIMPL;
}
}
static int php_sockop_cast(php_stream *stream, int castas, void **ret TSRMLS_DC)
{
php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
switch(castas) {
case PHP_STREAM_AS_STDIO:
if (ret) {
*ret = fdopen(sock->socket, stream->mode);
if (*ret)
return SUCCESS;
return FAILURE;
}
return SUCCESS;
case PHP_STREAM_AS_FD:
case PHP_STREAM_AS_SOCKETD:
if (ret)
*ret = (void*)sock->socket;
return SUCCESS;
default:
return FAILURE;
}
}
/* }}} */
/* These may look identical, but we need them this way so that
* we can determine which type of socket we are dealing with
* by inspecting stream->ops.
* A "useful" side-effect is that the user's scripts can then
* make similar decisions using stream_get_meta_data.
* */
php_stream_ops php_stream_generic_socket_ops = {
php_sockop_write, php_sockop_read,
php_sockop_close, php_sockop_flush,
"generic_socket",
NULL, /* seek */
php_sockop_cast,
php_sockop_stat,
php_sockop_set_option,
};
php_stream_ops php_stream_socket_ops = {
php_sockop_write, php_sockop_read,
php_sockop_close, php_sockop_flush,
"tcp_socket",
NULL, /* seek */
php_sockop_cast,
php_sockop_stat,
php_tcp_sockop_set_option,
};
php_stream_ops php_stream_udp_socket_ops = {
php_sockop_write, php_sockop_read,
php_sockop_close, php_sockop_flush,
"udp_socket",
NULL, /* seek */
php_sockop_cast,
php_sockop_stat,
php_tcp_sockop_set_option,
};
#ifdef AF_UNIX
php_stream_ops php_stream_unix_socket_ops = {
php_sockop_write, php_sockop_read,
php_sockop_close, php_sockop_flush,
"unix_socket",
NULL, /* seek */
php_sockop_cast,
php_sockop_stat,
php_tcp_sockop_set_option,
};
php_stream_ops php_stream_unixdg_socket_ops = {
php_sockop_write, php_sockop_read,
php_sockop_close, php_sockop_flush,
"udg_socket",
NULL, /* seek */
php_sockop_cast,
php_sockop_stat,
php_tcp_sockop_set_option,
};
#endif
/* network socket operations */
static inline int php_tcp_sockop_bind(php_stream *stream, php_netstream_data_t *sock,
php_stream_xport_param *xparam TSRMLS_DC)
{
return -1;
}
static inline int php_tcp_sockop_connect(php_stream *stream, php_netstream_data_t *sock,
php_stream_xport_param *xparam TSRMLS_DC)
{
char *colon;
char *host = NULL;
int portno, err;
int ret;
#ifdef AF_UNIX
if (stream->ops == &php_stream_unix_socket_ops || stream->ops == &php_stream_unixdg_socket_ops) {
struct sockaddr_un unix_addr;
sock->socket = socket(PF_UNIX, stream->ops == &php_stream_unix_socket_ops ? SOCK_STREAM : SOCK_DGRAM, 0);
if (sock->socket == SOCK_ERR) {
if (xparam->want_errortext) {
spprintf(&xparam->outputs.error_text, 0, "Failed to create unix socket");
}
return -1;
}
memset(&unix_addr, 0, sizeof(unix_addr));
unix_addr.sun_family = AF_UNIX;
/* we need to be binary safe on systems that support an abstract
* namespace */
if (xparam->inputs.namelen >= sizeof(unix_addr.sun_path)) {
/* On linux, when the path begins with a NUL byte we are
* referring to an abstract namespace. In theory we should
* allow an extra byte below, since we don't need the NULL.
* BUT, to get into this branch of code, the name is too long,
* so we don't care. */
xparam->inputs.namelen = sizeof(unix_addr.sun_path) - 1;
}
memcpy(unix_addr.sun_path, xparam->inputs.name, xparam->inputs.namelen);
ret = php_network_connect_socket(sock->socket,
(const struct sockaddr *)&unix_addr, sizeof(unix_addr),
xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC, xparam->inputs.timeout,
xparam->want_errortext ? &xparam->outputs.error_text : NULL,
&err);
xparam->outputs.error_code = err;
goto out;
}
#endif
colon = memchr(xparam->inputs.name, ':', xparam->inputs.namelen);
if (colon) {
portno = atoi(colon + 1);
host = estrndup(xparam->inputs.name, colon - xparam->inputs.name);
} else {
if (xparam->want_errortext) {
spprintf(&xparam->outputs.error_text, 0, "Failed to parse address \"%s\"", xparam->inputs.name);
}
return -1;
}
/* Note: the test here for php_stream_udp_socket_ops is important, because we
* want the default to be TCP sockets so that the openssl extension can
* re-use this code. */
sock->socket = php_network_connect_socket_to_host(host, portno,
stream->ops == &php_stream_udp_socket_ops ? SOCK_DGRAM : SOCK_STREAM,
xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC,
xparam->inputs.timeout, xparam->want_errortext ? &xparam->outputs.error_text : NULL,
&err
TSRMLS_CC);
ret = sock->socket == -1 ? -1 : 0;
xparam->outputs.error_code = err;
if (host) {
efree(host);
}
#ifdef AF_UNIX
out:
#endif
if (ret >= 0 && xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC && err == EINPROGRESS) {
/* indicates pending connection */
return 1;
}
return ret;
}
static inline int php_tcp_sockop_accept(php_stream *stream, php_netstream_data_t *sock,
php_stream_xport_param *xparam TSRMLS_DC)
{
return -1;
}
static int php_tcp_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC)
{
php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
php_stream_xport_param *xparam;
switch(option) {
case PHP_STREAM_OPTION_XPORT_API:
xparam = (php_stream_xport_param *)ptrparam;
switch(xparam->op) {
case STREAM_XPORT_OP_CONNECT:
case STREAM_XPORT_OP_CONNECT_ASYNC:
xparam->outputs.returncode = php_tcp_sockop_connect(stream, sock, xparam TSRMLS_CC);
return PHP_STREAM_OPTION_RETURN_OK;
case STREAM_XPORT_OP_BIND:
xparam->outputs.returncode = php_tcp_sockop_bind(stream, sock, xparam TSRMLS_CC);
return PHP_STREAM_OPTION_RETURN_OK;
case STREAM_XPORT_OP_ACCEPT:
xparam->outputs.returncode = php_tcp_sockop_accept(stream, sock, xparam TSRMLS_CC);
return PHP_STREAM_OPTION_RETURN_OK;
default:
/* fall through */
}
/* fall through */
default:
return php_sockop_set_option(stream, option, value, ptrparam TSRMLS_CC);
}
}
PHPAPI php_stream *php_stream_generic_socket_factory(const char *proto, long protolen,
char *resourcename, long resourcenamelen,
const char *persistent_id, int options, int flags,
struct timeval *timeout,
php_stream_context *context STREAMS_DC TSRMLS_DC)
{
php_stream *stream = NULL;
php_netstream_data_t *sock;
php_stream_ops *ops;
/* which type of socket ? */
if (strncmp(proto, "tcp", protolen) == 0) {
ops = &php_stream_socket_ops;
} else if (strncmp(proto, "udp", protolen) == 0) {
ops = &php_stream_udp_socket_ops;
}
#ifdef AF_UNIX
else if (strncmp(proto, "unix", protolen) == 0) {
ops = &php_stream_unix_socket_ops;
} else if (strncmp(proto, "udg", protolen) == 0) {
ops = &php_stream_unixdg_socket_ops;
}
#endif
else {
/* should never happen */
return NULL;
}
sock = pemalloc(sizeof(php_netstream_data_t), persistent_id ? 1 : 0);
memset(sock, 0, sizeof(php_netstream_data_t));
sock->is_blocked = 1;
sock->timeout.tv_sec = FG(default_socket_timeout);
sock->timeout.tv_usec = 0;
/* we don't know the socket until we have determined if we are binding or
* connecting */
sock->socket = -1;
stream = php_stream_alloc_rel(ops, sock, persistent_id, "r+");
if (stream == NULL) {
pefree(sock, persistent_id ? 1 : 0);
return NULL;
}
if (flags == 0) {
return stream;
}
return stream;
}
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
*/