diff options
author | Wez Furlong <wez@php.net> | 2003-02-28 19:53:21 +0000 |
---|---|---|
committer | Wez Furlong <wez@php.net> | 2003-02-28 19:53:21 +0000 |
commit | 1b53a2d12e520adec5cbbc60bf8f2b6d8e54eece (patch) | |
tree | 9ec880f9d210dc979ef007e1d69c4c53666f8a46 /main | |
parent | 14bf872003ff96b60960d5b822a0bb846bff176f (diff) | |
download | php-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.c | 456 | ||||
-rw-r--r-- | main/php_network.h | 32 | ||||
-rw-r--r-- | main/streams/php_stream_transport.h | 14 | ||||
-rw-r--r-- | main/streams/transports.c | 35 | ||||
-rw-r--r-- | main/streams/xp_socket.c | 171 |
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(¶m, 0, sizeof(param)); - param.op = STREAM_XPORT_OP_BIND; + param.op = STREAM_XPORT_OP_ACCEPT; param.inputs.timeout = timeout; param.want_addr = addr ? 1 : 0; param.want_textaddr = textaddr ? 1 : 0; @@ -290,6 +290,37 @@ PHPAPI int php_stream_xport_accept(php_stream *stream, php_stream **client, return ret; } +PHPAPI int php_stream_xport_get_name(php_stream *stream, int want_peer, + char **textaddr, int *textaddrlen, + void **addr, size_t *addrlen + TSRMLS_DC) +{ + php_stream_xport_param param; + int ret; + + memset(¶m, 0, sizeof(param)); + + param.op = want_peer ? STREAM_XPORT_OP_GET_PEER_NAME : STREAM_XPORT_OP_GET_NAME; + param.want_addr = addr ? 1 : 0; + param.want_textaddr = textaddr ? 1 : 0; + + ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, ¶m); + + if (ret == PHP_STREAM_OPTION_RETURN_OK) { + if (addr) { + *addr = param.outputs.addr; + *addrlen = param.outputs.addrlen; + } + if (textaddr) { + *textaddr = param.outputs.textaddr; + *textaddrlen = param.outputs.textaddrlen; + } + + return param.outputs.returncode; + } + return ret; +} + PHPAPI int php_stream_xport_crypto_setup(php_stream *stream, php_stream_xport_crypt_method_t crypto_method, php_stream *session_stream TSRMLS_DC) { php_stream_xport_crypto_param param; 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 */ |