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:
Wez Furlong 2003-02-28 19:53:21 +00:00
parent 14bf872003
commit 1b53a2d12e
14 changed files with 778 additions and 315 deletions

10
NEWS
View File

@ -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

View File

@ -461,6 +461,8 @@ getservbyport \
getrusage \
gettimeofday \
gmtime_r \
inet_ntoa \
inet_ntop \
isascii \
link \
localtime_r \

View File

@ -135,7 +135,9 @@ ftp_open(const char *host, short port, long timeout_sec TSRMLS_DC)
tv.tv_sec = timeout_sec;
tv.tv_usec = 0;
ftp->fd = php_hostconnect(host, (unsigned short) (port ? port : 21), SOCK_STREAM, &tv TSRMLS_CC);
ftp->fd = php_network_connect_socket_to_host(host,
(unsigned short) (port ? port : 21), SOCK_STREAM,
0, &tv, NULL, NULL TSRMLS_CC);
if (ftp->fd == -1) {
goto bail;
}

View File

@ -288,7 +288,6 @@ static inline int php_openssl_setup_crypto(php_stream *stream,
method = TLSv1_server_method();
break;
default:
printf("unknown method\n");
return -1;
}
@ -307,19 +306,16 @@ static inline int php_openssl_setup_crypto(php_stream *stream,
}
if (!SSL_set_fd(sslsock->ssl_handle, sslsock->s.socket)) {
printf("failed to set fd %d\n", sslsock->s.socket);
handle_ssl_error(stream, 0 TSRMLS_CC);
}
if (cparam->inputs.session) {
printf("sess=%p\n", cparam->inputs.session);
if (cparam->inputs.session->ops != &php_openssl_socket_ops) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied session stream must be an SSL enabled stream");
} else {
SSL_copy_session_id(sslsock->ssl_handle, ((php_openssl_netstream_data_t*)cparam->inputs.session->abstract)->ssl_handle);
}
}
printf("crypto prepared for fd=%d\n", sslsock->s.socket);
return 0;
}
@ -337,13 +333,11 @@ static inline int php_openssl_enable_crypto(php_stream *stream,
if (n <= 0) {
retry = handle_ssl_error(stream, n TSRMLS_CC);
printf("error; retry = %d\n", retry);
} else {
break;
}
} while (retry);
printf("enabled_crypto: n=%d\n", n);
if (n == 1) {
sslsock->ssl_active = 1;
}
@ -359,6 +353,47 @@ static inline int php_openssl_enable_crypto(php_stream *stream,
return -1;
}
static inline int php_openssl_tcp_sockop_accept(php_stream *stream, php_openssl_netstream_data_t *sock,
php_stream_xport_param *xparam STREAMS_DC TSRMLS_DC)
{
int clisock;
xparam->outputs.client = NULL;
clisock = php_network_accept_incoming(sock->s.socket,
xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
xparam->want_textaddr ? &xparam->outputs.textaddrlen : NULL,
xparam->want_addr ? &xparam->outputs.addr : NULL,
xparam->want_addr ? &xparam->outputs.addrlen : NULL,
xparam->inputs.timeout,
xparam->want_errortext ? &xparam->outputs.error_text : NULL,
&xparam->outputs.error_code
TSRMLS_CC);
if (clisock >= 0) {
php_openssl_netstream_data_t *clisockdata;
clisockdata = pemalloc(sizeof(*clisockdata), stream->is_persistent);
if (clisockdata == NULL) {
close(clisock);
/* technically a fatal error */
} else {
/* copy underlying tcp fields */
memset(clisockdata, 0, sizeof(*clisockdata));
memcpy(clisockdata, sock, sizeof(clisockdata->s));
clisockdata->s.socket = clisock;
xparam->outputs.client = php_stream_alloc_rel(stream->ops, clisockdata, NULL, "r+");
if (xparam->outputs.client) {
xparam->outputs.client->context = stream->context;
}
}
}
return xparam->outputs.client == NULL ? -1 : 0;
}
static int php_openssl_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC)
{
php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
@ -403,6 +438,14 @@ static int php_openssl_sockop_set_option(php_stream *stream, int option, int val
}
return PHP_STREAM_OPTION_RETURN_OK;
break;
case STREAM_XPORT_OP_ACCEPT:
/* we need to copy the additional fields that the underlying tcp transport
* doesn't know about */
xparam->outputs.returncode = php_openssl_tcp_sockop_accept(stream, sslsock, xparam STREAMS_CC TSRMLS_CC);
return PHP_STREAM_OPTION_RETURN_OK;
break;
default:
/* fall through */
break;
@ -464,7 +507,7 @@ PHPAPI php_stream *php_openssl_ssl_socket_factory(const char *proto, long protol
php_openssl_netstream_data_t *sslsock = NULL;
sslsock = pemalloc(sizeof(php_openssl_netstream_data_t), persistent_id ? 1 : 0);
memset(sslsock, 0, sizeof(php_openssl_netstream_data_t));
memset(sslsock, 0, sizeof(*sslsock));
sslsock->s.is_blocked = 1;
sslsock->s.timeout.tv_sec = FG(default_socket_timeout);
@ -488,6 +531,8 @@ PHPAPI php_stream *php_openssl_ssl_socket_factory(const char *proto, long protol
sslsock->enable_on_connect = 1;
sslsock->method = STREAM_CRYPTO_METHOD_TLS_CLIENT;
}
printf("enable_on_connect = %d --> proto %s\n", sslsock->enable_on_connect, proto);
return stream;
}

View File

@ -96,8 +96,8 @@ int basic_globals_id;
php_basic_globals basic_globals;
#endif
#include "php_fopen_wrappers.h"
#include "streamsfuncs.h"
static unsigned char first_and_second__args_force_ref[] = { 2, BYREF_FORCE, BYREF_FORCE };
static unsigned char second_and_third_args_force_ref[] = { 3, BYREF_NONE, BYREF_FORCE, BYREF_FORCE };
@ -679,6 +679,10 @@ function_entry basic_functions[] = {
PHP_FE(stream_context_get_options, NULL)
PHP_FE(stream_filter_prepend, NULL)
PHP_FE(stream_filter_append, NULL)
PHP_FE(stream_socket_client, second_and_third_args_force_ref)
PHP_FE(stream_socket_server, second_and_third_args_force_ref)
PHP_FE(stream_socket_accept, third_arg_force_ref)
PHP_FE(stream_socket_get_name, NULL)
PHP_FE(fgetcsv, NULL)
PHP_FE(flock, NULL)
PHP_FE(get_meta_tags, NULL)

View File

@ -82,7 +82,7 @@
#endif
#include "fsock.h"
#include "fopen_wrappers.h"
#include "php_streams.h"
#include "streamsfuncs.h"
#include "php_globals.h"
#ifdef HAVE_SYS_FILE_H
@ -187,6 +187,12 @@ PHP_MINIT_FUNCTION(file)
REGISTER_LONG_CONSTANT("STREAM_FILTER_WRITE", PHP_STREAM_FILTER_WRITE, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("STREAM_FILTER_ALL", PHP_STREAM_FILTER_ALL, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("STREAM_CLIENT_PERSISTENT", PHP_STREAM_CLIENT_PERSISTENT, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("STREAM_CLIENT_ASYNC_CONNECT", PHP_STREAM_CLIENT_ASYNC_CONNECT, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("STREAM_SERVER_BIND", STREAM_XPORT_BIND, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("STREAM_SERVER_LISTEN", STREAM_XPORT_LISTEN, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("FILE_USE_INCLUDE_PATH", PHP_FILE_USE_INCLUDE_PATH, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("FILE_IGNORE_NEW_LINES", PHP_FILE_IGNORE_NEW_LINES, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("FILE_SKIP_EMPTY_LINES", PHP_FILE_SKIP_EMPTY_LINES, CONST_CS | CONST_PERSISTENT);

View File

@ -54,13 +54,6 @@ PHP_FUNCTION(unlink);
PHP_FUNCTION(copy);
PHP_FUNCTION(file);
PHP_FUNCTION(file_get_contents);
PHP_FUNCTION(set_socket_blocking); /* deprecated */
PHP_FUNCTION(stream_set_blocking);
PHP_FUNCTION(stream_select);
PHP_FUNCTION(stream_set_timeout);
PHP_FUNCTION(stream_set_write_buffer);
PHP_FUNCTION(stream_get_wrappers);
PHP_FUNCTION(stream_get_line);
PHP_FUNCTION(get_meta_tags);
PHP_FUNCTION(flock);
PHP_FUNCTION(fd_set);
@ -72,14 +65,6 @@ PHP_FUNCTION(fnmatch);
PHP_NAMED_FUNCTION(php_if_ftruncate);
PHP_NAMED_FUNCTION(php_if_fstat);
PHP_FUNCTION(stream_get_meta_data);
PHP_FUNCTION(stream_register_wrapper);
PHP_FUNCTION(stream_context_create);
PHP_FUNCTION(stream_context_set_params);
PHP_FUNCTION(stream_context_set_option);
PHP_FUNCTION(stream_context_get_options);
PHP_FUNCTION(stream_filter_prepend);
PHP_FUNCTION(stream_filter_append);
PHP_MINIT_FUNCTION(user_streams);
PHPAPI int php_le_stream_context(void);

View File

@ -83,7 +83,7 @@ static void php_fsockopen_stream(INTERNAL_FUNCTION_PARAMETERS, int persistent)
efree(hostname);
}
if (stream == NULL) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to connect to %s:%d", host, port);
php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to connect to %s:%d (%s)", host, port, errstr == NULL ? "Unknown error" : errstr);
}
if (hashkey) {
@ -102,6 +102,10 @@ static void php_fsockopen_stream(INTERNAL_FUNCTION_PARAMETERS, int persistent)
}
RETURN_FALSE;
}
if (errstr) {
efree(errstr);
}
php_stream_to_zval(stream, return_value);
}

View File

@ -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)

View File

@ -275,9 +275,6 @@ typedef int php_non_blocking_flags_t;
# endif
#endif
/* Connect to a socket using an interruptible connect with optional timeout.
* Optionally, the connect can be made asynchronously, which will implicitly
* enable non-blocking mode on the socket.
@ -376,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
*/

View File

@ -119,12 +119,33 @@ PHPAPI int php_network_connect_socket(int sockfd,
char **error_string,
int *error_code);
int php_hostconnect(const char *host, unsigned short port, int socktype, struct timeval *timeout TSRMLS_DC);
PHPAPI int php_connect_nonb(int sockfd, const struct sockaddr *addr, socklen_t addrlen, struct timeval *timeout);
#define php_connect_nonb(sock, addr, addrlen, timeout) \
php_network_connect_socket((sock), (addr), (addrlen), 0, (timeout), NULL, NULL)
#ifdef PHP_WIN32
PHPAPI int php_connect_nonb_win32(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen, struct timeval *timeout);
#endif
PHPAPI int php_network_bind_socket_to_local_addr(const char *host, unsigned port,
int socktype, char **error_string, int *error_code
TSRMLS_DC);
PHPAPI int php_network_accept_incoming(int srvsock,
char **textaddr, long *textaddrlen,
struct sockaddr **addr,
socklen_t *addrlen,
struct timeval *timeout,
char **error_string,
int *error_code
TSRMLS_DC);
PHPAPI int php_network_get_sock_name(int sock,
char **textaddr, long *textaddrlen,
struct sockaddr **addr,
socklen_t *addrlen
TSRMLS_DC);
PHPAPI int php_network_get_peer_name(int sock,
char **textaddr, long *textaddrlen,
struct sockaddr **addr,
socklen_t *addrlen
TSRMLS_DC);
void php_any_addr(int family, php_sockaddr_storage *addr, unsigned short port);
int php_sockaddr_size(php_sockaddr_storage *addr);
@ -134,6 +155,7 @@ struct _php_netstream_data_t {
char is_blocked;
struct timeval timeout;
char timeout_event;
size_t ownsize;
};
typedef struct _php_netstream_data_t php_netstream_data_t;
extern php_stream_ops php_stream_socket_ops;

View File

@ -72,19 +72,27 @@ PHPAPI int php_stream_xport_listen(php_stream *stream,
/* Get the next client and their address as a string, or the underlying address
* structure. You must efree either of these if you request them */
PHPAPI int php_stream_xport_accept(php_stream *stream, php_stream **client,
char **textaddr, long *textaddrlen,
char **textaddr, int *textaddrlen,
void **addr, size_t *addrlen,
struct timeval *timeout,
char **error_text
TSRMLS_DC);
/* Get the name of either the socket or it's peer */
PHPAPI int php_stream_xport_get_name(php_stream *stream, int want_peer,
char **textaddr, int *textaddrlen,
void **addr, size_t *addrlen
TSRMLS_DC);
/* Structure definition for the set_option interface that the above functions wrap */
typedef struct _php_stream_xport_param {
enum {
STREAM_XPORT_OP_BIND, STREAM_XPORT_OP_CONNECT,
STREAM_XPORT_OP_LISTEN, STREAM_XPORT_OP_ACCEPT,
STREAM_XPORT_OP_CONNECT_ASYNC
STREAM_XPORT_OP_CONNECT_ASYNC,
STREAM_XPORT_OP_GET_NAME,
STREAM_XPORT_OP_GET_PEER_NAME
} op;
int want_addr:1;
int want_textaddr:1;
@ -99,7 +107,7 @@ typedef struct _php_stream_xport_param {
struct {
php_stream *client;
int returncode;
void *addr;
struct sockaddr *addr;
size_t addrlen;
char *textaddr;
long textaddrlen;

View File

@ -252,7 +252,7 @@ PHPAPI int php_stream_xport_listen(php_stream *stream, int backlog, char **error
/* Get the next client and their address (as a string) */
PHPAPI int php_stream_xport_accept(php_stream *stream, php_stream **client,
char **textaddr, long *textaddrlen,
char **textaddr, int *textaddrlen,
void **addr, size_t *addrlen,
struct timeval *timeout,
char **error_text
@ -263,7 +263,7 @@ PHPAPI int php_stream_xport_accept(php_stream *stream, php_stream **client,
memset(&param, 0, sizeof(param));
param.op = STREAM_XPORT_OP_BIND;
param.op = STREAM_XPORT_OP_ACCEPT;
param.inputs.timeout = timeout;
param.want_addr = addr ? 1 : 0;
param.want_textaddr = textaddr ? 1 : 0;
@ -290,6 +290,37 @@ PHPAPI int php_stream_xport_accept(php_stream *stream, php_stream **client,
return ret;
}
PHPAPI int php_stream_xport_get_name(php_stream *stream, int want_peer,
char **textaddr, int *textaddrlen,
void **addr, size_t *addrlen
TSRMLS_DC)
{
php_stream_xport_param param;
int ret;
memset(&param, 0, sizeof(param));
param.op = want_peer ? STREAM_XPORT_OP_GET_PEER_NAME : STREAM_XPORT_OP_GET_NAME;
param.want_addr = addr ? 1 : 0;
param.want_textaddr = textaddr ? 1 : 0;
ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, &param);
if (ret == PHP_STREAM_OPTION_RETURN_OK) {
if (addr) {
*addr = param.outputs.addr;
*addrlen = param.outputs.addrlen;
}
if (textaddr) {
*textaddr = param.outputs.textaddr;
*textaddrlen = param.outputs.textaddrlen;
}
return param.outputs.returncode;
}
return ret;
}
PHPAPI int php_stream_xport_crypto_setup(php_stream *stream, php_stream_xport_crypt_method_t crypto_method, php_stream *session_stream TSRMLS_DC)
{
php_stream_xport_crypto_param param;

View File

@ -217,6 +217,25 @@ static int php_sockop_set_option(php_stream *stream, int option, int value, void
xparam->outputs.returncode = listen(sock->socket, 5);
return PHP_STREAM_OPTION_RETURN_OK;
case STREAM_XPORT_OP_GET_NAME:
xparam->outputs.returncode = php_network_get_sock_name(sock->socket,
xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
xparam->want_textaddr ? &xparam->outputs.textaddrlen : NULL,
xparam->want_addr ? &xparam->outputs.addr : NULL,
xparam->want_addr ? &xparam->outputs.addrlen : NULL
TSRMLS_CC);
return PHP_STREAM_OPTION_RETURN_OK;
case STREAM_XPORT_OP_GET_PEER_NAME:
xparam->outputs.returncode = php_network_get_peer_name(sock->socket,
xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
xparam->want_textaddr ? &xparam->outputs.textaddrlen : NULL,
xparam->want_addr ? &xparam->outputs.addr : NULL,
xparam->want_addr ? &xparam->outputs.addrlen : NULL
TSRMLS_CC);
return PHP_STREAM_OPTION_RETURN_OK;
default:
return PHP_STREAM_OPTION_RETURN_NOTIMPL;
}
@ -311,17 +330,97 @@ php_stream_ops php_stream_unixdg_socket_ops = {
/* network socket operations */
#ifdef AF_UNIX
static inline int parse_unix_address(php_stream_xport_param *xparam, struct sockaddr_un *unix_addr TSRMLS_DC)
{
memset(unix_addr, 0, sizeof(*unix_addr));
unix_addr->sun_family = AF_UNIX;
/* we need to be binary safe on systems that support an abstract
* namespace */
if (xparam->inputs.namelen >= sizeof(unix_addr->sun_path)) {
/* On linux, when the path begins with a NUL byte we are
* referring to an abstract namespace. In theory we should
* allow an extra byte below, since we don't need the NULL.
* BUT, to get into this branch of code, the name is too long,
* so we don't care. */
xparam->inputs.namelen = sizeof(unix_addr->sun_path) - 1;
}
memcpy(unix_addr->sun_path, xparam->inputs.name, xparam->inputs.namelen);
return 1;
}
#endif
static inline char *parse_ip_address(php_stream_xport_param *xparam, int *portno TSRMLS_DC)
{
char *colon;
char *host = NULL;
colon = memchr(xparam->inputs.name, ':', xparam->inputs.namelen);
if (colon) {
*portno = atoi(colon + 1);
host = estrndup(xparam->inputs.name, colon - xparam->inputs.name);
} else {
if (xparam->want_errortext) {
spprintf(&xparam->outputs.error_text, 0, "Failed to parse address \"%s\"", xparam->inputs.name);
}
return NULL;
}
return host;
}
static inline int php_tcp_sockop_bind(php_stream *stream, php_netstream_data_t *sock,
php_stream_xport_param *xparam TSRMLS_DC)
{
char *host = NULL;
int portno, err;
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 */