. 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.
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
getrusage \
gettimeofday \
gmtime_r \
+inet_ntoa \
+inet_ntop \
isascii \
link \
localtime_r \
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;
}
method = TLSv1_server_method();
break;
default:
- printf("unknown method\n");
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;
}
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 -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;
}
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;
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);
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;
}
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 };
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)
#endif
#include "fsock.h"
#include "fopen_wrappers.h"
-#include "php_streams.h"
+#include "streamsfuncs.h"
#include "php_globals.h"
#ifdef HAVE_SYS_FILE_H
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);
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);
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);
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) {
}
RETURN_FALSE;
}
+
+ if (errstr) {
+ efree(errstr);
+ }
php_stream_to_zval(stream, return_value);
}
#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)
# 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_connect_nonb */
-PHPAPI int php_connect_nonb(int sockfd,
- const struct sockaddr *addr,
- socklen_t addrlen,
- struct timeval *timeout)
+/* {{{ sub_times */
+static inline void sub_times(struct timeval a, struct timeval b, struct timeval *result)
{
-#if (!defined(__BEOS__) && !defined(PHP_WIN32)) && (defined(O_NONBLOCK) || defined(O_NDELAY))
+ result->tv_usec = a.tv_usec - b.tv_usec;
+ if (result->tv_usec < 0L) {
+ a.tv_sec--;
+ result->tv_usec += 1000000L;
+ }
+ result->tv_sec = a.tv_sec - b.tv_sec;
+ if (result->tv_sec < 0L) {
+ result->tv_sec++;
+ result->tv_usec -= 1000000L;
+ }
+}
+/* }}} */
+/* Bind to a local IP address.
+ * Returns the bound socket, or -1 on failure.
+ * */
+/* {{{ php_network_bind_socket_to_local_addr */
+int php_network_bind_socket_to_local_addr(const char *host, unsigned port,
+ int socktype, char **error_string, int *error_code
+ TSRMLS_DC)
+{
+ int num_addrs, sock, n, err = 0;
+ struct sockaddr **sal, **psal, *sa;
+ socklen_t socklen;
- int flags;
- int n;
- int error = 0;
- socklen_t len;
- int ret = 0;
- fd_set rset;
- fd_set wset;
- fd_set eset;
+ num_addrs = php_network_getaddresses(host, &psal, error_string TSRMLS_CC);
- if (timeout == NULL) {
- /* blocking mode */
- return connect(sockfd, addr, addrlen);
+ if (num_addrs == 0) {
+ /* could not resolve address(es) */
+ return -1;
}
-
- flags = fcntl(sockfd, F_GETFL, 0);
- fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
- if ((n = connect(sockfd, addr, addrlen)) < 0) {
- if (errno != EINPROGRESS) {
- return -1;
- }
- }
+ for (sal = psal; *sal != NULL; sal++) {
+ sa = *sal;
- if (n == 0) {
- goto ok;
- }
+ /* create a socket for this address */
+ sock = socket(sa->sa_family, socktype, 0);
- FD_ZERO(&rset);
- FD_ZERO(&eset);
- FD_SET(sockfd, &rset);
- FD_SET(sockfd, &eset);
+ if (sock == SOCK_ERR) {
+ continue;
+ }
- wset = rset;
+ switch (sa->sa_family) {
+#if HAVE_GETADDRINFO && HAVE_IPV6
+ case AF_INET6:
+ ((struct sockaddr_in6 *)sa)->sin6_family = sa->sa_family;
+ ((struct sockaddr_in6 *)sa)->sin6_port = htons(port);
+ socklen = sizeof(struct sockaddr_in6);
+ break;
+#endif
+ case AF_INET:
+ ((struct sockaddr_in *)sa)->sin_family = sa->sa_family;
+ ((struct sockaddr_in *)sa)->sin_port = htons(port);
+ socklen = sizeof(struct sockaddr_in);
+ break;
+ default:
+ /* Unknown family */
+ sa = NULL;
+ }
- if ((n = select(sockfd + 1, &rset, &wset, &eset, timeout)) == 0) {
- error = ETIMEDOUT;
- }
+ if (sa) {
+ /* attempt to bind */
+
+#ifdef SO_REUSEADDR
+ {
+ int val = 1;
+ setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
+ }
+#endif
+
+ n = bind(sock, sa, socklen);
- if(FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) {
- len = sizeof(error);
- /*
- BSD-derived systems set errno correctly
- Solaris returns -1 from getsockopt in case of error
- */
- if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
- ret = -1;
+ if (n != SOCK_CONN_ERR) {
+ goto bound;
+ }
+
+ err = php_socket_errno();
}
- } else {
- /* whoops: sockfd has disappeared */
- ret = -1;
- }
-ok:
- fcntl(sockfd, F_SETFL, flags);
+ close(sock);
+ }
+ sock = -1;
- if(error) {
- errno = error;
- ret = -1;
+ if (error_code) {
+ *error_code = err;
}
- return ret;
-#else /* !defined(PHP_WIN32) && ... */
-#ifdef PHP_WIN32
- return php_connect_nonb_win32((SOCKET) sockfd, addr, addrlen, timeout);
-#endif
- return connect(sockfd, addr, addrlen);
-#endif
+ if (error_string) {
+ *error_string = php_socket_strerror(err, NULL, 0);
+ }
+
+bound:
+
+ php_network_freeaddresses(psal);
+
+ return sock;
+
}
/* }}} */
-#ifdef PHP_WIN32
-/* {{{ php_connect_nonb_win32 */
-PHPAPI int php_connect_nonb_win32(SOCKET sockfd,
- const struct sockaddr *addr,
- socklen_t addrlen,
- struct timeval *timeout)
+static void populate_name(
+ /* input address */
+ struct sockaddr *sa, socklen_t sl,
+ /* output readable address */
+ char **textaddr, long *textaddrlen,
+ /* output address */
+ struct sockaddr **addr,
+ socklen_t *addrlen
+ TSRMLS_DC)
{
- int error = 0, error_len, ret;
- u_long non_block = TRUE, block = FALSE;
-
- fd_set rset, wset;
-
- if (timeout == NULL) {
- /* blocking mode */
- return connect(sockfd, addr, addrlen);
+ if (addr) {
+ *addr = emalloc(sl);
+ memcpy(*addr, sa, sl);
+ *addrlen = sl;
}
-
- /* Set the socket to be non-blocking */
- ioctlsocket(sockfd, FIONBIO, &non_block);
- if (connect(sockfd, addr, addrlen) == SOCKET_ERROR) {
- if (WSAGetLastError() != WSAEWOULDBLOCK) {
- return SOCKET_ERROR;
- }
- }
+ if (textaddr) {
+#if HAVE_IPV6 && HAVE_INET_NTOP
+ char abuf[256];
+#endif
+ char *buf = NULL;
- FD_ZERO(&rset);
- FD_SET(sockfd, &rset);
+ switch (sa->sa_family) {
+ case AF_INET:
+ /* generally not thread safe, but it *is* thread safe under win32 */
+ buf = inet_ntoa(((struct sockaddr_in*)sa)->sin_addr);
+ if (buf) {
+ *textaddrlen = strlen(buf);
+ *textaddr = estrndup(buf, *textaddrlen);
+ }
- FD_ZERO(&wset);
- FD_SET(sockfd, &wset);
+ break;
- if ((ret = select(sockfd + 1, &rset, &wset, NULL, timeout)) == 0) {
- WSASetLastError(WSAETIMEDOUT);
- return SOCKET_ERROR;
- }
+#if HAVE_IPV6
+ case AF_INET6:
+ buf = (char*)inet_ntop(sa->sa_family, &((struct sockaddr_in6*)sa)->sin6_addr, (char *)&abuf, sizeof(abuf));
+ if (buf) {
+ *textaddrlen = strlen(buf);
+ *textaddr = estrndup(buf, *textaddrlen);
+ }
- if (ret == SOCKET_ERROR) {
- return SOCKET_ERROR;
- }
+ break;
+#endif
+#ifdef AF_UNIX
+ case AF_UNIX:
+ {
+ struct sockaddr_un *ua = (struct sockaddr_un*)sa;
+
+ if (ua->sun_path[0] == '\0') {
+ /* abstract name */
+ int len = strlen(ua->sun_path + 1) + 1;
+ *textaddrlen = len;
+ *textaddr = emalloc(len + 1);
+ memcpy(*textaddr, ua->sun_path, len);
+ (*textaddr)[len] = '\0';
+ } else {
+ *textaddrlen = strlen(ua->sun_path);
+ *textaddr = estrndup(ua->sun_path, *textaddrlen);
+ }
+ }
+ break;
+#endif
- if(FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) {
- error_len = sizeof(error);
- if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (char *) &error, &error_len) == SOCKET_ERROR) {
- return SOCKET_ERROR;
}
- } else {
- /* whoops: sockfd has disappeared */
- return SOCKET_ERROR;
+
}
+}
- /* Set the socket back to blocking */
- ioctlsocket(sockfd, FIONBIO, &block);
+PHPAPI int php_network_get_peer_name(int sock,
+ char **textaddr, long *textaddrlen,
+ struct sockaddr **addr,
+ socklen_t *addrlen
+ TSRMLS_DC)
+{
+ php_sockaddr_storage sa;
+ socklen_t sl = sizeof(sa);
+
+ if (getpeername(sock, (struct sockaddr*)&sa, &sl) == 0) {
+ populate_name((struct sockaddr*)&sa, sl,
+ textaddr, textaddrlen,
+ addr, addrlen
+ TSRMLS_CC);
+ return 0;
+ }
+ return -1;
+}
- if (error) {
- WSASetLastError(error);
- return SOCKET_ERROR;
+PHPAPI int php_network_get_sock_name(int sock,
+ char **textaddr, long *textaddrlen,
+ struct sockaddr **addr,
+ socklen_t *addrlen
+ TSRMLS_DC)
+{
+ php_sockaddr_storage sa;
+ socklen_t sl = sizeof(sa);
+
+ if (getsockname(sock, (struct sockaddr*)&sa, &sl) == 0) {
+ populate_name((struct sockaddr*)&sa, sl,
+ textaddr, textaddrlen,
+ addr, addrlen
+ TSRMLS_CC);
+ return 0;
}
+ return -1;
- return 0;
}
-/* }}} */
-#endif
-/* {{{ sub_times */
-static inline void sub_times(struct timeval a, struct timeval b, struct timeval *result)
+
+/* Accept a client connection from a server socket,
+ * using an optional timeout.
+ * Returns the peer address in addr/addrlen (it will emalloc
+ * these, so be sure to efree the result).
+ * If you specify textaddr/textaddrlen, a text-printable
+ * version of the address will be emalloc'd and returned.
+ * */
+
+/* {{{ php_network_accept_incoming */
+PHPAPI int php_network_accept_incoming(int srvsock,
+ char **textaddr, long *textaddrlen,
+ struct sockaddr **addr,
+ socklen_t *addrlen,
+ struct timeval *timeout,
+ char **error_string,
+ int *error_code
+ TSRMLS_DC)
{
- result->tv_usec = a.tv_usec - b.tv_usec;
- if (result->tv_usec < 0L) {
- a.tv_sec--;
- result->tv_usec += 1000000L;
+ int clisock = -1;
+ fd_set rset;
+ int error, n;
+ php_sockaddr_storage sa;
+ socklen_t sl;
+
+ FD_ZERO(&rset);
+ FD_SET(srvsock, &rset);
+
+ n = select(srvsock + 1, &rset, NULL, NULL, timeout);
+
+ if (n == 0) {
+ error = PHP_TIMEOUT_ERROR_VALUE;
+ } else if (n == -1) {
+ error = php_socket_errno();
+ } else if (FD_ISSET(srvsock, &rset)) {
+ sl = sizeof(sa);
+
+ clisock = accept(srvsock, (struct sockaddr*)&sa, &sl);
+
+ if (clisock >= 0) {
+ populate_name((struct sockaddr*)&sa, sl,
+ textaddr, textaddrlen,
+ addr, addrlen
+ TSRMLS_CC);
+ } else {
+ error = php_socket_errno();
+ }
}
- result->tv_sec = a.tv_sec - b.tv_sec;
- if (result->tv_sec < 0L) {
- result->tv_sec++;
- result->tv_usec -= 1000000L;
+
+ if (error_code) {
+ *error_code = error;
}
+ if (error_string) {
+ *error_string = php_socket_strerror(error, NULL, 0);
+ }
+
+ return clisock;
}
/* }}} */
+
+
/* Connect to a remote host using an interruptible connect with optional timeout.
* Optionally, the connect can be made asynchronously, which will implicitly
}
/* }}} */
-
-
-/* {{{ 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
*/
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);
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;
/* 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;
struct {
php_stream *client;
int returncode;
- void *addr;
+ struct sockaddr *addr;
size_t addrlen;
char *textaddr;
long textaddrlen;
/* 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
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;
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;
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;
}
/* network socket operations */
+#ifdef AF_UNIX
+static inline int parse_unix_address(php_stream_xport_param *xparam, struct sockaddr_un *unix_addr TSRMLS_DC)
+{
+ memset(unix_addr, 0, sizeof(*unix_addr));
+ unix_addr->sun_family = AF_UNIX;
+
+ /* we need to be binary safe on systems that support an abstract
+ * namespace */
+ if (xparam->inputs.namelen >= sizeof(unix_addr->sun_path)) {
+ /* On linux, when the path begins with a NUL byte we are
+ * referring to an abstract namespace. In theory we should
+ * allow an extra byte below, since we don't need the NULL.
+ * BUT, to get into this branch of code, the name is too long,
+ * so we don't care. */
+ xparam->inputs.namelen = sizeof(unix_addr->sun_path) - 1;
+ }
+
+ memcpy(unix_addr->sun_path, xparam->inputs.name, xparam->inputs.namelen);
+
+ return 1;
+}
+#endif
+
+static inline char *parse_ip_address(php_stream_xport_param *xparam, int *portno TSRMLS_DC)
+{
+ char *colon;
+ char *host = NULL;
+
+ colon = memchr(xparam->inputs.name, ':', xparam->inputs.namelen);
+ if (colon) {
+ *portno = atoi(colon + 1);
+ host = estrndup(xparam->inputs.name, colon - xparam->inputs.name);
+ } else {
+ if (xparam->want_errortext) {
+ spprintf(&xparam->outputs.error_text, 0, "Failed to parse address \"%s\"", xparam->inputs.name);
+ }
+ return NULL;
+ }
+
+ return host;
+}
+
static inline int php_tcp_sockop_bind(php_stream *stream, php_netstream_data_t *sock,
php_stream_xport_param *xparam TSRMLS_DC)
{
+ char *host = NULL;
+ int portno, err;
+
+#ifdef AF_UNIX
+ if (stream->ops == &php_stream_unix_socket_ops || stream->ops == &php_stream_unixdg_socket_ops) {
+ struct sockaddr_un unix_addr;
+
+ sock->socket = socket(PF_UNIX, stream->ops == &php_stream_unix_socket_ops ? SOCK_STREAM : SOCK_DGRAM, 0);
+
+ if (sock->socket == SOCK_ERR) {
+ if (xparam->want_errortext) {
+ spprintf(&xparam->outputs.error_text, 0, "Failed to create unix%s socket %s",
+ stream->ops == &php_stream_unix_socket_ops ? "" : "datagram",
+ strerror(errno));
+ }
+ return -1;
+ }
+
+ parse_unix_address(xparam, &unix_addr TSRMLS_CC);
+
+ return bind(sock->socket, &unix_addr, sizeof(unix_addr));
+ }
+#endif
- return -1;
+ host = parse_ip_address(xparam, &portno TSRMLS_CC);
+
+ if (host == NULL) {
+ return -1;
+ }
+
+ sock->socket = php_network_bind_socket_to_local_addr(host, portno,
+ stream->ops == &php_stream_udp_socket_ops ? SOCK_DGRAM : SOCK_STREAM,
+ xparam->want_errortext ? &xparam->outputs.error_text : NULL,
+ &err
+ TSRMLS_CC);
+
+ if (host) {
+ efree(host);
+ }
+
+ return sock->socket == -1 ? -1 : 0;
}
static inline int php_tcp_sockop_connect(php_stream *stream, php_netstream_data_t *sock,
php_stream_xport_param *xparam TSRMLS_DC)
{
- char *colon;
char *host = NULL;
int portno, err;
int ret;
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),
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;
}
}
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)
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 */