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