network_io/win32/socket_pipe.c (144 lines of code) (raw):
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "apr_arch_networkio.h"
#include "apr_portable.h"
static apr_status_t create_socket_pipe(SOCKET *rd, SOCKET *wr)
{
FD_SET rs;
SOCKET ls;
struct timeval socktm;
struct sockaddr_in pa;
struct sockaddr_in la;
struct sockaddr_in ca;
int nrd;
apr_status_t rv;
int ll = sizeof(la);
int lc = sizeof(ca);
unsigned long bm = 1;
char uid[8];
char iid[8];
*rd = INVALID_SOCKET;
*wr = INVALID_SOCKET;
/* Create the unique socket identifier
* so that we know the connection originated
* from us.
*/
rv = apr_generate_random_bytes(uid, sizeof(uid));
if (rv != APR_SUCCESS) {
return rv;
}
if ((ls = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {
return apr_get_netos_error();
}
pa.sin_family = AF_INET;
pa.sin_port = 0;
pa.sin_addr.s_addr = inet_addr("127.0.0.1");
if (bind(ls, (SOCKADDR *)&pa, sizeof(pa)) == SOCKET_ERROR) {
rv = apr_get_netos_error();
goto cleanup;
}
if (getsockname(ls, (SOCKADDR *)&la, &ll) == SOCKET_ERROR) {
rv = apr_get_netos_error();
goto cleanup;
}
if (listen(ls, 1) == SOCKET_ERROR) {
rv = apr_get_netos_error();
goto cleanup;
}
if ((*wr = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {
rv = apr_get_netos_error();
goto cleanup;
}
if (connect(*wr, (SOCKADDR *)&la, sizeof(la)) == SOCKET_ERROR) {
rv = apr_get_netos_error();
goto cleanup;
}
if (send(*wr, uid, sizeof(uid), 0) != sizeof(uid)) {
if ((rv = apr_get_netos_error()) == 0) {
rv = APR_EINVAL;
}
goto cleanup;
}
if (ioctlsocket(ls, FIONBIO, &bm) == SOCKET_ERROR) {
rv = apr_get_netos_error();
goto cleanup;
}
for (;;) {
int ns;
int nc = 0;
/* Listening socket is nonblocking by now.
* The accept should create the socket
* immediatelly because we are connected already.
* However on buys systems this can take a while
* until winsock gets a chance to handle the events.
*/
FD_ZERO(&rs);
FD_SET(ls, &rs);
socktm.tv_sec = 1;
socktm.tv_usec = 0;
if ((ns = select(0, &rs, NULL, NULL, &socktm)) == SOCKET_ERROR) {
/* Accept still not signaled */
Sleep(100);
continue;
}
if (ns == 0) {
/* No connections in the last second */
continue;
}
if ((*rd = accept(ls, (SOCKADDR *)&ca, &lc)) == INVALID_SOCKET) {
rv = apr_get_netos_error();
goto cleanup;
}
/* Verify the connection by reading/waiting for the identification */
bm = 0;
if (ioctlsocket(*rd, FIONBIO, &bm) == SOCKET_ERROR) {
rv = apr_get_netos_error();
goto cleanup;
}
nrd = recv(*rd, iid, sizeof(iid), 0);
if (nrd == SOCKET_ERROR) {
rv = apr_get_netos_error();
goto cleanup;
}
if (nrd == (int)sizeof(uid) && memcmp(iid, uid, sizeof(uid)) == 0) {
/* Got the right identifier, return. */
break;
}
closesocket(*rd);
}
/* We don't need the listening socket any more */
closesocket(ls);
return 0;
cleanup:
/* Don't leak resources */
closesocket(ls);
if (*rd != INVALID_SOCKET)
closesocket(*rd);
if (*wr != INVALID_SOCKET)
closesocket(*wr);
*rd = INVALID_SOCKET;
*wr = INVALID_SOCKET;
return rv;
}
static apr_status_t socket_pipe_cleanup(void *thefile)
{
apr_socket_t *file = thefile;
if (file->socketdes != INVALID_SOCKET) {
shutdown(file->socketdes, SD_BOTH);
closesocket(file->socketdes);
file->socketdes = INVALID_SOCKET;
}
return APR_SUCCESS;
}
apr_status_t apr_socket_pipe_create(apr_socket_t **in,
apr_socket_t **out,
apr_pool_t *p)
{
apr_status_t rv;
SOCKET rd;
SOCKET wr;
*in = NULL;
*out = NULL;
if ((rv = create_socket_pipe(&rd, &wr)) != APR_SUCCESS) {
return rv;
}
apr_os_sock_put(in, &rd, p);
apr_os_sock_put(out, &wr, p);
/* read end of the pipe is non-blocking */
apr_socket_timeout_set(*in, 0);
apr_pool_cleanup_register(p, (void *)(*in), socket_pipe_cleanup,
apr_pool_cleanup_null);
apr_pool_cleanup_register(p, (void *)(*out), socket_pipe_cleanup,
apr_pool_cleanup_null);
return rv;
}
apr_status_t apr_socket_pipe_close(apr_socket_t *socket)
{
apr_status_t stat;
if ((stat = socket_pipe_cleanup(socket)) == APR_SUCCESS) {
apr_pool_cleanup_kill(socket->pool, socket, socket_pipe_cleanup);
return APR_SUCCESS;
}
return stat;
}