summaryrefslogtreecommitdiff
path: root/main
diff options
context:
space:
mode:
authorWez Furlong <wez@php.net>2003-02-28 19:53:21 +0000
committerWez Furlong <wez@php.net>2003-02-28 19:53:21 +0000
commit1b53a2d12e520adec5cbbc60bf8f2b6d8e54eece (patch)
tree9ec880f9d210dc979ef007e1d69c4c53666f8a46 /main
parent14bf872003ff96b60960d5b822a0bb846bff176f (diff)
downloadphp-git-1b53a2d12e520adec5cbbc60bf8f2b6d8e54eece.tar.gz
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.
Diffstat (limited to 'main')
-rw-r--r--main/network.c456
-rw-r--r--main/php_network.h32
-rw-r--r--main/streams/php_stream_transport.h14
-rw-r--r--main/streams/transports.c35
-rw-r--r--main/streams/xp_socket.c171
5 files changed, 444 insertions, 264 deletions
diff --git a/main/network.c b/main/network.c
index cc499bb3c1..f04447745a 100644
--- a/main/network.c
+++ b/main/network.c
@@ -275,9 +275,6 @@ typedef int php_non_blocking_flags_t;
# endif
#endif
-
-
-
/* Connect to a socket using an interruptible connect with optional timeout.
* Optionally, the connect can be made asynchronously, which will implicitly
* enable non-blocking mode on the socket.
@@ -376,163 +373,276 @@ ok:
}
/* }}} */
-/* {{{ 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
@@ -654,110 +764,6 @@ connected:
}
/* }}} */
-
-
-/* {{{ php_hostconnect
- * Creates a socket of type socktype and connects to the given host and
- * port, returns the created socket on success, else returns -1.
- * timeout gives timeout in seconds, 0 means blocking mode.
- */
-int php_hostconnect(const char *host, unsigned short port, int socktype, struct timeval *timeout TSRMLS_DC)
-{
- int n, repeatto, s;
- struct sockaddr **sal, **psal;
- struct timeval individual_timeout;
- int set_timeout = 0;
- int err;
-
- n = php_network_getaddresses(host, &sal, NULL TSRMLS_CC);
-
- if (n == 0)
- return -1;
-
- if (timeout != NULL) {
- /* is this a good idea? 5s? */
- repeatto = timeout->tv_sec / n > 5;
- if (repeatto) {
- individual_timeout.tv_sec = timeout->tv_sec / n;
- } else {
- individual_timeout.tv_sec = timeout->tv_sec;
- }
-
- individual_timeout.tv_usec = timeout->tv_usec;
- } else {
- individual_timeout.tv_sec = 0;
- individual_timeout.tv_usec = 0;
- }
-
- /* Boolean indicating whether to pass a timeout */
- set_timeout = individual_timeout.tv_sec + individual_timeout.tv_usec;
-
- psal = sal;
- while (*sal != NULL) {
- s = socket((*sal)->sa_family, socktype, 0);
- if (s != SOCK_ERR) {
- switch ((*sal)->sa_family) {
-#if defined( HAVE_GETADDRINFO ) && defined( HAVE_IPV6 )
- case AF_INET6:
- {
- struct sockaddr_in6 *sa =
- (struct sockaddr_in6 *)*sal;
-
- sa->sin6_family = (*sal)->sa_family;
- sa->sin6_port = htons(port);
- if (php_connect_nonb(s, (struct sockaddr *) sa,
- sizeof(*sa), (set_timeout) ? &individual_timeout : NULL) != SOCK_CONN_ERR)
- goto ok;
- }
- break;
-#endif
- case AF_INET:
- {
- struct sockaddr_in *sa =
- (struct sockaddr_in *)*sal;
-
- sa->sin_family = (*sal)->sa_family;
- sa->sin_port = htons(port);
- if (php_connect_nonb(s, (struct sockaddr *) sa,
- sizeof(*sa), (set_timeout) ? &individual_timeout : NULL) != SOCK_CONN_ERR)
- goto ok;
-
- }
- break;
- }
-#ifdef PHP_WIN32
- /* Preserve the last error */
- err = WSAGetLastError();
-#else
- err = errno;
-#endif
- close (s);
- }
- sal++;
-
- if (err == PHP_TIMEOUT_ERROR_VALUE) {
- /* if the first attempt timed out, it's highly likely
- * that any subsequent attempts will do so also */
- break;
- }
-
- }
- php_network_freeaddresses(psal);
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_hostconnect: connect failed");
-
-#ifdef PHP_WIN32
- /* Restore the last error */
- WSASetLastError(err);
-#endif
-
- return -1;
-
- ok:
- php_network_freeaddresses(psal);
- return s;
-}
-/* }}} */
-
/* {{{ php_any_addr
* Fills the any (wildcard) address into php_sockaddr_storage
*/
diff --git a/main/php_network.h b/main/php_network.h
index f3870d2aa1..3439964353 100644
--- a/main/php_network.h
+++ b/main/php_network.h
@@ -119,12 +119,33 @@ PHPAPI int php_network_connect_socket(int sockfd,
char **error_string,
int *error_code);
-int php_hostconnect(const char *host, unsigned short port, int socktype, struct timeval *timeout TSRMLS_DC);
-PHPAPI int php_connect_nonb(int sockfd, const struct sockaddr *addr, socklen_t addrlen, struct timeval *timeout);
+#define php_connect_nonb(sock, addr, addrlen, timeout) \
+ php_network_connect_socket((sock), (addr), (addrlen), 0, (timeout), NULL, NULL)
-#ifdef PHP_WIN32
-PHPAPI int php_connect_nonb_win32(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen, struct timeval *timeout);
-#endif
+PHPAPI int php_network_bind_socket_to_local_addr(const char *host, unsigned port,
+ int socktype, char **error_string, int *error_code
+ TSRMLS_DC);
+
+PHPAPI int php_network_accept_incoming(int srvsock,
+ char **textaddr, long *textaddrlen,
+ struct sockaddr **addr,
+ socklen_t *addrlen,
+ struct timeval *timeout,
+ char **error_string,
+ int *error_code
+ TSRMLS_DC);
+
+PHPAPI int php_network_get_sock_name(int sock,
+ char **textaddr, long *textaddrlen,
+ struct sockaddr **addr,
+ socklen_t *addrlen
+ TSRMLS_DC);
+
+PHPAPI int php_network_get_peer_name(int sock,
+ char **textaddr, long *textaddrlen,
+ struct sockaddr **addr,
+ socklen_t *addrlen
+ TSRMLS_DC);
void php_any_addr(int family, php_sockaddr_storage *addr, unsigned short port);
int php_sockaddr_size(php_sockaddr_storage *addr);
@@ -134,6 +155,7 @@ struct _php_netstream_data_t {
char is_blocked;
struct timeval timeout;
char timeout_event;
+ size_t ownsize;
};
typedef struct _php_netstream_data_t php_netstream_data_t;
extern php_stream_ops php_stream_socket_ops;
diff --git a/main/streams/php_stream_transport.h b/main/streams/php_stream_transport.h
index 89642b5652..c1cd622f2b 100644
--- a/main/streams/php_stream_transport.h
+++ b/main/streams/php_stream_transport.h
@@ -72,19 +72,27 @@ PHPAPI int php_stream_xport_listen(php_stream *stream,
/* Get the next client and their address as a string, or the underlying address
* structure. You must efree either of these if you request them */
PHPAPI int php_stream_xport_accept(php_stream *stream, php_stream **client,
- char **textaddr, long *textaddrlen,
+ char **textaddr, int *textaddrlen,
void **addr, size_t *addrlen,
struct timeval *timeout,
char **error_text
TSRMLS_DC);
+/* Get the name of either the socket or it's peer */
+PHPAPI int php_stream_xport_get_name(php_stream *stream, int want_peer,
+ char **textaddr, int *textaddrlen,
+ void **addr, size_t *addrlen
+ TSRMLS_DC);
+
/* Structure definition for the set_option interface that the above functions wrap */
typedef struct _php_stream_xport_param {
enum {
STREAM_XPORT_OP_BIND, STREAM_XPORT_OP_CONNECT,
STREAM_XPORT_OP_LISTEN, STREAM_XPORT_OP_ACCEPT,
- STREAM_XPORT_OP_CONNECT_ASYNC
+ STREAM_XPORT_OP_CONNECT_ASYNC,
+ STREAM_XPORT_OP_GET_NAME,
+ STREAM_XPORT_OP_GET_PEER_NAME
} op;
int want_addr:1;
int want_textaddr:1;
@@ -99,7 +107,7 @@ typedef struct _php_stream_xport_param {
struct {
php_stream *client;
int returncode;
- void *addr;
+ struct sockaddr *addr;
size_t addrlen;
char *textaddr;
long textaddrlen;
diff --git a/main/streams/transports.c b/main/streams/transports.c
index 4f12e193ed..0bfddd39c0 100644
--- a/main/streams/transports.c
+++ b/main/streams/transports.c
@@ -252,7 +252,7 @@ PHPAPI int php_stream_xport_listen(php_stream *stream, int backlog, char **error
/* Get the next client and their address (as a string) */
PHPAPI int php_stream_xport_accept(php_stream *stream, php_stream **client,
- char **textaddr, long *textaddrlen,
+ char **textaddr, int *textaddrlen,
void **addr, size_t *addrlen,
struct timeval *timeout,
char **error_text
@@ -263,7 +263,7 @@ PHPAPI int php_stream_xport_accept(php_stream *stream, php_stream **client,
memset(&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;
diff --git a/main/streams/xp_socket.c b/main/streams/xp_socket.c
index 34d617b8ce..c2547bd7d3 100644
--- a/main/streams/xp_socket.c
+++ b/main/streams/xp_socket.c
@@ -217,6 +217,25 @@ static int php_sockop_set_option(php_stream *stream, int option, int value, void
xparam->outputs.returncode = listen(sock->socket, 5);
return PHP_STREAM_OPTION_RETURN_OK;
+ case STREAM_XPORT_OP_GET_NAME:
+ xparam->outputs.returncode = php_network_get_sock_name(sock->socket,
+ xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
+ xparam->want_textaddr ? &xparam->outputs.textaddrlen : NULL,
+ xparam->want_addr ? &xparam->outputs.addr : NULL,
+ xparam->want_addr ? &xparam->outputs.addrlen : NULL
+ TSRMLS_CC);
+ return PHP_STREAM_OPTION_RETURN_OK;
+
+ case STREAM_XPORT_OP_GET_PEER_NAME:
+ xparam->outputs.returncode = php_network_get_peer_name(sock->socket,
+ xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
+ xparam->want_textaddr ? &xparam->outputs.textaddrlen : NULL,
+ xparam->want_addr ? &xparam->outputs.addr : NULL,
+ xparam->want_addr ? &xparam->outputs.addrlen : NULL
+ TSRMLS_CC);
+ return PHP_STREAM_OPTION_RETURN_OK;
+
+
default:
return PHP_STREAM_OPTION_RETURN_NOTIMPL;
}
@@ -311,17 +330,97 @@ php_stream_ops php_stream_unixdg_socket_ops = {
/* network socket operations */
+#ifdef AF_UNIX
+static inline int parse_unix_address(php_stream_xport_param *xparam, struct sockaddr_un *unix_addr TSRMLS_DC)
+{
+ memset(unix_addr, 0, sizeof(*unix_addr));
+ unix_addr->sun_family = AF_UNIX;
+
+ /* we need to be binary safe on systems that support an abstract
+ * namespace */
+ if (xparam->inputs.namelen >= sizeof(unix_addr->sun_path)) {
+ /* On linux, when the path begins with a NUL byte we are
+ * referring to an abstract namespace. In theory we should
+ * allow an extra byte below, since we don't need the NULL.
+ * BUT, to get into this branch of code, the name is too long,
+ * so we don't care. */
+ xparam->inputs.namelen = sizeof(unix_addr->sun_path) - 1;
+ }
+
+ memcpy(unix_addr->sun_path, xparam->inputs.name, xparam->inputs.namelen);
+
+ return 1;
+}
+#endif
+
+static inline char *parse_ip_address(php_stream_xport_param *xparam, int *portno TSRMLS_DC)
+{
+ char *colon;
+ char *host = NULL;
+
+ colon = memchr(xparam->inputs.name, ':', xparam->inputs.namelen);
+ if (colon) {
+ *portno = atoi(colon + 1);
+ host = estrndup(xparam->inputs.name, colon - xparam->inputs.name);
+ } else {
+ if (xparam->want_errortext) {
+ spprintf(&xparam->outputs.error_text, 0, "Failed to parse address \"%s\"", xparam->inputs.name);
+ }
+ return NULL;
+ }
+
+ return host;
+}
+
static inline int php_tcp_sockop_bind(php_stream *stream, php_netstream_data_t *sock,
php_stream_xport_param *xparam TSRMLS_DC)
{
+ char *host = NULL;
+ int portno, err;
+
+#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;
@@ -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 */