/*
 * os_win32.c --
 *
 *
 *  Copyright (c) 1995 Open Market, Inc.
 *  All rights reserved.
 *
 *  This file contains proprietary and confidential information and
 *  remains the unpublished property of Open Market, Inc. Use,
 *  disclosure, or reproduction is prohibited except as permitted by
 *  express written license agreement with Open Market, Inc.
 *
 *  Bill Snapper
 *  snapper@openmarket.com
 *
 * (Special thanks to Karen and Bill.  They made my job much easier and
 *  significantly more enjoyable.)
 */
#ifndef lint
static const char rcsid[] = "$Id: os_win32.c 92549 2013-05-20 13:34:55Z jfiat $";
#endif /* not lint */

#define WIN32_LEAN_AND_MEAN 
#include <windows.h>
#include <winsock2.h>
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <sys/timeb.h>
#include <process.h>

#define DLLAPI  __declspec(dllexport)

#include "fcgimisc.h"
#include "fcgios.h"

#define WIN32_OPEN_MAX 128 /* XXX: Small hack */

/*
 * millisecs to wait for a client connection before checking the 
 * shutdown flag (then go back to waiting for a connection, etc).
 */
#define ACCEPT_TIMEOUT 1000

#define MUTEX_VARNAME "_FCGI_MUTEX_"
#define SHUTDOWN_EVENT_NAME "_FCGI_SHUTDOWN_EVENT_"
#define LOCALHOST "localhost"

static HANDLE hIoCompPort = INVALID_HANDLE_VALUE;
static HANDLE hStdinCompPort = INVALID_HANDLE_VALUE;
static HANDLE hStdinThread = INVALID_HANDLE_VALUE;

static HANDLE stdioHandles[3] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE,
				 INVALID_HANDLE_VALUE};

// This is a nail for listening to more than one port..
static HANDLE acceptMutex = INVALID_HANDLE_VALUE;

static BOOLEAN shutdownPending = FALSE;
static BOOLEAN shutdownNow = FALSE;

/*
 * An enumeration of the file types
 * supported by the FD_TABLE structure.
 *
 * XXX: Not all currently supported.  This allows for future
 *      functionality.
 */
typedef enum {
    FD_UNUSED,
    FD_FILE_SYNC,
    FD_FILE_ASYNC,
    FD_SOCKET_SYNC,
    FD_SOCKET_ASYNC,
    FD_PIPE_SYNC,
    FD_PIPE_ASYNC
} FILE_TYPE;

typedef union {
    HANDLE fileHandle;
    SOCKET sock;
    unsigned int value;
} DESCRIPTOR;

/*
 * Structure used to map file handle and socket handle
 * values into values that can be used to create unix-like
 * select bitmaps, read/write for both sockets/files.
 */
struct FD_TABLE {
    DESCRIPTOR fid;
    FILE_TYPE type;
    char *path;
    DWORD Errno;
    unsigned long instance;
    int status;
    int offset;			/* only valid for async file writes */
    LPDWORD offsetHighPtr;	/* pointers to offset high and low words */
    LPDWORD offsetLowPtr;	/* only valid for async file writes (logs) */
    HANDLE  hMapMutex;		/* mutex handle for multi-proc offset update */
    LPVOID  ovList;		/* List of associated OVERLAPPED_REQUESTs */
};

/* 
 * XXX Note there is no dyanmic sizing of this table, so if the
 * number of open file descriptors exceeds WIN32_OPEN_MAX the 
 * app will blow up.
 */
static struct FD_TABLE fdTable[WIN32_OPEN_MAX];

static CRITICAL_SECTION  fdTableCritical;

struct OVERLAPPED_REQUEST {
    OVERLAPPED overlapped;
    unsigned long instance;	/* file instance (won't match after a close) */
    OS_AsyncProc procPtr;	/* callback routine */
    ClientData clientData;	/* callback argument */
    ClientData clientData1;	/* additional clientData */
};
typedef struct OVERLAPPED_REQUEST *POVERLAPPED_REQUEST;

static const char *bindPathPrefix = "\\\\.\\pipe\\FastCGI\\";

static FILE_TYPE listenType = FD_UNUSED;

// XXX This should be a DESCRIPTOR
static HANDLE hListen = INVALID_HANDLE_VALUE;

static BOOLEAN libInitialized = FALSE;

HANDLE strToHandle(char *str)
{
#ifdef _WIN64
    return (HANDLE)_atoi64(str);
#else
    return (HANDLE)atoi(str);
#endif
}


/*
 *--------------------------------------------------------------
 *
 * Win32NewDescriptor --
 *
 *	Set up for I/O descriptor masquerading.
 *
 * Results:
 *	Returns "fake id" which masquerades as a UNIX-style "small
 *	non-negative integer" file/socket descriptor.
 *	Win32_* routine below will "do the right thing" based on the
 *	descriptor's actual type. -1 indicates failure.
 *
 * Side effects:
 *	Entry in fdTable is reserved to represent the socket/file.
 *
 *--------------------------------------------------------------
 */
static int Win32NewDescriptor(FILE_TYPE type, int fd, int desiredFd)
{
    int index = -1;

    EnterCriticalSection(&fdTableCritical);

    /*
     * If desiredFd is set, try to get this entry (this is used for
     * mapping stdio handles).  Otherwise try to get the fd entry.
     * If this is not available, find a the first empty slot.  .
     */
    if (desiredFd >= 0 && desiredFd < WIN32_OPEN_MAX)
    {
        if (fdTable[desiredFd].type == FD_UNUSED) 
        {
            index = desiredFd;
        }
	}
    else if (fd > 0)
    {
        if (fd < WIN32_OPEN_MAX && fdTable[fd].type == FD_UNUSED)
        {
	        index = fd;
        }
        else 
        {
            int i;

            for (i = 1; i < WIN32_OPEN_MAX; ++i)
            {
	            if (fdTable[i].type == FD_UNUSED)
                {
                    index = i;
                    break;
                }
            }
        }
    }
    
    if (index != -1) 
    {
        fdTable[index].fid.value = fd;
        fdTable[index].type = type;
        fdTable[index].path = NULL;
        fdTable[index].Errno = NO_ERROR;
        fdTable[index].status = 0;
        fdTable[index].offset = -1;
        fdTable[index].offsetHighPtr = fdTable[index].offsetLowPtr = NULL;
        fdTable[index].hMapMutex = NULL;
        fdTable[index].ovList = NULL;
    }

    LeaveCriticalSection(&fdTableCritical);
    return index;
}

/*
 *--------------------------------------------------------------
 *
 * StdinThread--
 *
 *	This thread performs I/O on stadard input.  It is needed
 *      because you can't guarantee that all applications will
 *      create standard input with sufficient access to perform
 *      asynchronous I/O.  Since we don't want to block the app
 *      reading from stdin we make it look like it's using I/O
 *      completion ports to perform async I/O.
 *
 * Results:
 *	Data is read from stdin and posted to the io completion
 *      port.
 *
 * Side effects:
 *	None.
 *
 *--------------------------------------------------------------
 */
static void StdinThread(void * startup) 
{
    int doIo = TRUE;
    ULONG_PTR fd;
    unsigned long bytesRead;
    POVERLAPPED_REQUEST pOv;

    // Touch the arg to prevent warning
    startup = NULL;

    while(doIo) {
        /*
         * Block until a request to read from stdin comes in or a
         * request to terminate the thread arrives (fd = -1).
         */
        if (!GetQueuedCompletionStatus(hStdinCompPort, &bytesRead, &fd,
	    (LPOVERLAPPED *)&pOv, (DWORD)-1) && !pOv) {
            doIo = 0;
            break;
        }

	ASSERT((fd == STDIN_FILENO) || (fd == -1));
        if(fd == -1) {
            doIo = 0;
            break;
        }
        ASSERT(pOv->clientData1 != NULL);

        if(ReadFile(stdioHandles[STDIN_FILENO], pOv->clientData1, bytesRead,
                    &bytesRead, NULL)) {
            PostQueuedCompletionStatus(hIoCompPort, bytesRead,
                                       STDIN_FILENO, (LPOVERLAPPED)pOv);
        } else {
            doIo = 0;
            break;
        }
    }

    ExitThread(0);
}

void OS_ShutdownPending(void)
{
    shutdownPending = TRUE;
}

static void ShutdownRequestThread(void * arg)
{
    HANDLE shutdownEvent = (HANDLE) arg;
    
    WaitForSingleObject(shutdownEvent, INFINITE);

    shutdownPending = TRUE;

    if (listenType == FD_PIPE_SYNC)
    {
        // Its a hassle to get ConnectNamedPipe to return early,
        // so just wack the whole process - yes, this will toast
        // any requests in progress, but at least its a clean 
        // shutdown (its better than TerminateProcess())
        exit(0);
    }
       
    // FD_SOCKET_SYNC: When in Accept(), select() is used to poll
    // the shutdownPending flag - yeah this isn't pretty either
    // but its only one process doing it if an Accept mutex is used.
    // This at least buys no toasted requests.
}

/*
 *--------------------------------------------------------------
 *
 * OS_LibInit --
 *
 *	Set up the OS library for use.
 *
 * Results:
 *	Returns 0 if success, -1 if not.
 *
 * Side effects:
 *	Sockets initialized, pseudo file descriptors setup, etc.
 *
 *--------------------------------------------------------------
 */
int OS_LibInit(int stdioFds[3])
{
    WORD  wVersion;
    WSADATA wsaData;
    int err;
    int fakeFd;
    char *cLenPtr = NULL;
    char *val = NULL;
        
    if(libInitialized)
        return 0;

    InitializeCriticalSection(&fdTableCritical);   
        
    /*
     * Initialize windows sockets library.
     */
    wVersion = MAKEWORD(2,0);
    err = WSAStartup( wVersion, &wsaData );
    if (err) {
        fprintf(stderr, "Error starting Windows Sockets.  Error: %d",
		WSAGetLastError());
	exit(111);
    }

    /*
     * Create the I/O completion port to be used for our I/O queue.
     */
    if (hIoCompPort == INVALID_HANDLE_VALUE) {
	hIoCompPort = CreateIoCompletionPort (INVALID_HANDLE_VALUE, NULL,
					      0, 1);
	if(hIoCompPort == INVALID_HANDLE_VALUE) {
	    printf("<H2>OS_LibInit Failed CreateIoCompletionPort!  ERROR: %d</H2>\r\n\r\n",
	       GetLastError());
	    return -1;
	}
    }

    /*
     * If a shutdown event is in the env, save it (I don't see any to 
     * remove it from the environment out from under the application).
     * Spawn a thread to wait on the shutdown request.
     */
    val = getenv(SHUTDOWN_EVENT_NAME);
    if (val != NULL) 
    {
        HANDLE shutdownEvent = strToHandle(val);

        if (_beginthread(ShutdownRequestThread, 0, shutdownEvent) == -1)
        {
            return -1;
        }
    }

    if (acceptMutex == INVALID_HANDLE_VALUE)
    {
        /* If an accept mutex is in the env, use it */
        val = getenv(MUTEX_VARNAME);
        if (val != NULL) 
        {
            acceptMutex = strToHandle(val);
        }
    }

    /*
     * Determine if this library is being used to listen for FastCGI
     * connections.  This is communicated by STDIN containing a
     * valid handle to a listener object.  In this case, both the
     * "stdout" and "stderr" handles will be INVALID (ie. closed) by
     * the starting process.
     *
     * The trick is determining if this is a pipe or a socket...
     *
     * XXX: Add the async accept test to determine socket or handle to a
     *      pipe!!!
     */
    if((GetStdHandle(STD_OUTPUT_HANDLE) == INVALID_HANDLE_VALUE) &&
       (GetStdHandle(STD_ERROR_HANDLE)  == INVALID_HANDLE_VALUE) &&
       (GetStdHandle(STD_INPUT_HANDLE)  != INVALID_HANDLE_VALUE) ) 
    {
        DWORD pipeMode = PIPE_READMODE_BYTE | PIPE_WAIT;
        HANDLE oldStdIn = GetStdHandle(STD_INPUT_HANDLE);

        // Move the handle to a "low" number
        if (! DuplicateHandle(GetCurrentProcess(), oldStdIn,
                              GetCurrentProcess(), &hListen,
                              0, TRUE, DUPLICATE_SAME_ACCESS))
        {
            return -1;
        }

        if (! SetStdHandle(STD_INPUT_HANDLE, hListen))
        {
            return -1;
        }

        CloseHandle(oldStdIn);

	/*
	 * Set the pipe handle state so that it operates in wait mode.
	 *
	 * NOTE: The listenFd is not mapped to a pseudo file descriptor
	 *       as all work done on it is contained to the OS library.
	 *
	 * XXX: Initial assumption is that SetNamedPipeHandleState will
	 *      fail if this is an IP socket...
	 */
        if (SetNamedPipeHandleState(hListen, &pipeMode, NULL, NULL)) 
        {
            listenType = FD_PIPE_SYNC;
        } 
        else 
        {
            listenType = FD_SOCKET_SYNC;
        }
    }

    /*
     * If there are no stdioFds passed in, we're done.
     */
    if(stdioFds == NULL) {
        libInitialized = 1;
        return 0;
    }

    /*
     * Setup standard input asynchronous I/O.  There is actually a separate
     * thread spawned for this purpose.  The reason for this is that some
     * web servers use anonymous pipes for the connection between itself
     * and a CGI application.  Anonymous pipes can't perform asynchronous
     * I/O or use I/O completion ports.  Therefore in order to present a
     * consistent I/O dispatch model to an application we emulate I/O
     * completion port behavior by having the standard input thread posting
     * messages to the hIoCompPort which look like a complete overlapped
     * I/O structure.  This keeps the event dispatching simple from the
     * application perspective.
     */
    stdioHandles[STDIN_FILENO] = GetStdHandle(STD_INPUT_HANDLE);

    if(!SetHandleInformation(stdioHandles[STDIN_FILENO],
			     HANDLE_FLAG_INHERIT, 0)) {
/*
 * XXX: Causes error when run from command line.  Check KB
        err = GetLastError();
        DebugBreak();
	exit(99);
 */
    }

    ASSERT(INT_MIN <= (intptr_t)stdioHandles[STDIN_FILENO] &&
           (intptr_t)stdioHandles[STDIN_FILENO] <= INT_MAX);
    if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC,
                                     (int)(intptr_t)stdioHandles[STDIN_FILENO],
				     STDIN_FILENO)) == -1) {
        return -1;
    } else {
        /*
	 * Set stdin equal to our pseudo FD and create the I/O completion
	 * port to be used for async I/O.
	 */
	stdioFds[STDIN_FILENO] = fakeFd;
    }

    /*
     * Create the I/O completion port to be used for communicating with
     * the thread doing I/O on standard in.  This port will carry read
     * and possibly thread termination requests to the StdinThread.
     */
    if (hStdinCompPort == INVALID_HANDLE_VALUE) {
	hStdinCompPort = CreateIoCompletionPort (INVALID_HANDLE_VALUE, NULL,
					      0, 1);
	if(hStdinCompPort == INVALID_HANDLE_VALUE) {
	    printf("<H2>OS_LibInit Failed CreateIoCompletionPort: STDIN!  ERROR: %d</H2>\r\n\r\n",
	       GetLastError());
	    return -1;
	}
    }

    /*
     * Create the thread that will read stdin if the CONTENT_LENGTH
     * is non-zero.
     */
    if((cLenPtr = getenv("CONTENT_LENGTH")) != NULL &&
       atoi(cLenPtr) > 0) {
        hStdinThread = (HANDLE) _beginthread(StdinThread, 0, NULL);
        if (hStdinThread == (HANDLE)(LONG_PTR) -1) {
	    printf("<H2>OS_LibInit Failed to create STDIN thread!  ERROR: %d</H2>\r\n\r\n",
		   GetLastError());
	    return -1;
        }
    }

    /*
     * STDOUT will be used synchronously.
     *
     * XXX: May want to convert this so that it could be used for OVERLAPPED
     *      I/O later.  If so, model it after the Stdin I/O as stdout is
     *      also incapable of async I/O on some servers.
     */
    stdioHandles[STDOUT_FILENO] = GetStdHandle(STD_OUTPUT_HANDLE);
    if(!SetHandleInformation(stdioHandles[STDOUT_FILENO],
			     HANDLE_FLAG_INHERIT, FALSE)) {
        DebugBreak();
	exit(99);
    }

    ASSERT(INT_MIN <= (intptr_t)stdioHandles[STDOUT_FILENO] &&
           (intptr_t)stdioHandles[STDOUT_FILENO] <= INT_MAX);
    if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC,
                                     (int)(intptr_t)stdioHandles[STDOUT_FILENO],
				     STDOUT_FILENO)) == -1) {
        return -1;
    } else {
        /*
	 * Set stdout equal to our pseudo FD
	 */
	stdioFds[STDOUT_FILENO] = fakeFd;
    }

    stdioHandles[STDERR_FILENO] = GetStdHandle(STD_ERROR_HANDLE);
    if(!SetHandleInformation(stdioHandles[STDERR_FILENO],
			     HANDLE_FLAG_INHERIT, FALSE)) {
        DebugBreak();
	exit(99);
    }
    ASSERT(INT_MIN <= (intptr_t)stdioHandles[STDERR_FILENO] &&
           (intptr_t)stdioHandles[STDERR_FILENO] <= INT_MAX);
    if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC,
                                     (int)(intptr_t)stdioHandles[STDERR_FILENO],
				     STDERR_FILENO)) == -1) {
        return -1;
    } else {
        /*
	 * Set stderr equal to our pseudo FD
	 */
	stdioFds[STDERR_FILENO] = fakeFd;
    }

    return 0;
}

/*
 *--------------------------------------------------------------
 *
 * OS_LibShutdown --
 *
 *	Shutdown the OS library.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Memory freed, handles closed.
 *
 *--------------------------------------------------------------
 */
void OS_LibShutdown()
{

    if (hIoCompPort != INVALID_HANDLE_VALUE) 
    {
        CloseHandle(hIoCompPort);
        hIoCompPort = INVALID_HANDLE_VALUE;
    }

    if (hStdinCompPort != INVALID_HANDLE_VALUE) 
    {
        CloseHandle(hStdinCompPort);
        hStdinCompPort = INVALID_HANDLE_VALUE;
    }

    if (acceptMutex != INVALID_HANDLE_VALUE) 
    {
        ReleaseMutex(acceptMutex);
    }

    DisconnectNamedPipe(hListen);

    CancelIo(hListen);


    WSACleanup();
}

/*
 *--------------------------------------------------------------
 *
 * Win32FreeDescriptor --
 *
 *	Free I/O descriptor entry in fdTable.
 *
 * Results:
 *	Frees I/O descriptor entry in fdTable.
 *
 * Side effects:
 *	None.
 *
 *--------------------------------------------------------------
 */
static void Win32FreeDescriptor(int fd)
{
    /* Catch it if fd is a bogus value */
    ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));

    EnterCriticalSection(&fdTableCritical);
    
    if (fdTable[fd].type != FD_UNUSED)
    {   
        switch (fdTable[fd].type) 
        {
	    case FD_FILE_SYNC:
	    case FD_FILE_ASYNC:
        
	        /* Free file path string */
	        ASSERT(fdTable[fd].path != NULL);
	        free(fdTable[fd].path);
	        fdTable[fd].path = NULL;
	        break;

	    default:
	        break;
        }

        ASSERT(fdTable[fd].path == NULL);

        fdTable[fd].type = FD_UNUSED;
        fdTable[fd].path = NULL;
        fdTable[fd].Errno = NO_ERROR;
        fdTable[fd].offsetHighPtr = fdTable[fd].offsetLowPtr = NULL;

        if (fdTable[fd].hMapMutex != NULL) 
        {
            CloseHandle(fdTable[fd].hMapMutex);
            fdTable[fd].hMapMutex = NULL;
        }
    }

    LeaveCriticalSection(&fdTableCritical);

    return;
}

static short getPort(const char * bindPath)
{
    short port = 0;
    char * p = strchr(bindPath, ':');

    if (p && *++p) 
    {
        char buf[6];

        strncpy(buf, p, 6);
        buf[5] = '\0';

        port = (short) atoi(buf);
    }
 
    return port;
}

/*
 * OS_CreateLocalIpcFd --
 *
 *   This procedure is responsible for creating the listener pipe
 *   on Windows NT for local process communication.  It will create a
 *   named pipe and return a file descriptor to it to the caller.
 *
 * Results:
 *      Listener pipe created.  This call returns either a valid
 *      pseudo file descriptor or -1 on error.
 *
 * Side effects:
 *      Listener pipe and IPC address are stored in the FCGI info
 *         structure.
 *      'errno' will set on errors (-1 is returned).
 *
 *----------------------------------------------------------------------
 */
int OS_CreateLocalIpcFd(const char *bindPath, int backlog)
{
    int pseudoFd = -1;
    short port = getPort(bindPath);

    if (acceptMutex == INVALID_HANDLE_VALUE)
    {
        acceptMutex = CreateMutex(NULL, FALSE, NULL);
        if (acceptMutex == NULL) return -2;
        if (! SetHandleInformation(acceptMutex, HANDLE_FLAG_INHERIT, TRUE)) return -3;
    }

    // There's nothing to be gained (at the moment) by a shutdown Event    

    if (port && *bindPath != ':' && strncmp(bindPath, LOCALHOST, strlen(LOCALHOST)))
    {
	    fprintf(stderr, "To start a service on a TCP port can not "
			    "specify a host name.\n"
			    "You should either use \"localhost:<port>\" or "
			    " just use \":<port>.\"\n");
	    exit(1);
    }

    listenType = (port) ? FD_SOCKET_SYNC : FD_PIPE_ASYNC;
    
    if (port) 
    {
        SOCKET listenSock;
        struct  sockaddr_in	sockAddr;
        int sockLen = sizeof(sockAddr);
        
        memset(&sockAddr, 0, sizeof(sockAddr));
        sockAddr.sin_family = AF_INET;
        sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
        sockAddr.sin_port = htons(port);

        listenSock = socket(AF_INET, SOCK_STREAM, 0);
        if (listenSock == INVALID_SOCKET) 
        {
	        return -4;
	    }

	    if (bind(listenSock, (struct sockaddr *) &sockAddr, sockLen)  )
        {
	        return -12;
	    }

	    if (listen(listenSock, backlog)) 
        {
	        return -5;
	    }

        ASSERT(INT_MIN <= (intptr_t)listenSock &&
               (intptr_t)listenSock <= INT_MAX);
        pseudoFd = Win32NewDescriptor(listenType, (int)(intptr_t)listenSock, -1);
        
        if (pseudoFd == -1) 
        {
            closesocket(listenSock);
            return -6;
        }

        hListen = (HANDLE) listenSock;        
    }
    else
    {
        HANDLE hListenPipe = INVALID_HANDLE_VALUE;
        char *pipePath = malloc(strlen(bindPathPrefix) + strlen(bindPath) + 1);
        
        if (! pipePath) 
        {
            return -7;
        }

        strcpy(pipePath, bindPathPrefix);
        strcat(pipePath, bindPath);

        hListenPipe = CreateNamedPipe(pipePath,
		        PIPE_ACCESS_DUPLEX,
		        PIPE_TYPE_BYTE | PIPE_WAIT | PIPE_READMODE_BYTE,
		        PIPE_UNLIMITED_INSTANCES,
		        4096, 4096, 0, NULL);
        
        free(pipePath);

        if (hListenPipe == INVALID_HANDLE_VALUE)
        {
            return -8;
        }

        if (! SetHandleInformation(hListenPipe, HANDLE_FLAG_INHERIT, TRUE))
        {
            return -9;
        }

        ASSERT(INT_MIN <= (intptr_t)hListenPipe &&
               (intptr_t)hListenPipe <= INT_MAX);
        pseudoFd = Win32NewDescriptor(listenType, (int)(intptr_t)hListenPipe, -1);
        
        if (pseudoFd == -1) 
        {
            CloseHandle(hListenPipe);
            return -10;
        }

        hListen = (HANDLE) hListenPipe;
    }

    return pseudoFd;
}

/*
 *----------------------------------------------------------------------
 *
 * OS_FcgiConnect --
 *
 *	Create the pipe pathname connect to the remote application if
 *      possible.
 *
 * Results:
 *      -1 if fail or a valid handle if connection succeeds.
 *
 * Side effects:
 *      Remote connection established.
 *
 *----------------------------------------------------------------------
 */
int OS_FcgiConnect(char *bindPath)
{
    short port = getPort(bindPath);
    int pseudoFd = -1;
    
    if (port) 
    {
	    struct hostent *hp;
        char *host = NULL;
        struct sockaddr_in sockAddr;
        int sockLen = sizeof(sockAddr);
        SOCKET sock;
        
        if (*bindPath != ':')
        {
            char * p = strchr(bindPath, ':');
            intptr_t len = p - bindPath + 1;

            host = malloc(len);
            strncpy(host, bindPath, len);
            host[len] = '\0';
        }
        
        hp = gethostbyname(host ? host : LOCALHOST);

        if (host)
        {
            free(host);
        }

	    if (hp == NULL) 
        {
	        fprintf(stderr, "Unknown host: %s\n", bindPath);
	        return -1;
	    }
       
        memset(&sockAddr, 0, sizeof(sockAddr));
        sockAddr.sin_family = AF_INET;
	    memcpy(&sockAddr.sin_addr, hp->h_addr, hp->h_length);
	    sockAddr.sin_port = htons(port);

	    sock = socket(AF_INET, SOCK_STREAM, 0);
        if (sock == INVALID_SOCKET)
        {
            return -1;
        }

	    if (! connect(sock, (struct sockaddr *) &sockAddr, sockLen)) 
        {
	        closesocket(sock);
	        return -1;
	    }

        ASSERT(INT_MIN <= (intptr_t)sock && (intptr_t)sock <= INT_MAX);
        pseudoFd = Win32NewDescriptor(FD_SOCKET_SYNC, (int)(intptr_t)sock, -1);
	    if (pseudoFd == -1) 
        {
	        closesocket(sock);
            return -1;
	    }
    }
    else
    {
        char *pipePath = malloc(strlen(bindPathPrefix) + strlen(bindPath) + 1);
        HANDLE hPipe;
        
        if (! pipePath) 
        {
            return -1;
        }

        strcpy(pipePath, bindPathPrefix);
        strcat(pipePath, bindPath);

        hPipe = CreateFile(pipePath,
			    GENERIC_WRITE | GENERIC_READ,
			    FILE_SHARE_READ | FILE_SHARE_WRITE,
			    NULL,
			    OPEN_EXISTING,
			    FILE_FLAG_OVERLAPPED,
			    NULL);

        free(pipePath);

        if( hPipe == INVALID_HANDLE_VALUE) 
        {
            return -1;
        }

        ASSERT(INT_MIN <= (intptr_t)hPipe && (intptr_t)hPipe <= INT_MAX);
        pseudoFd = Win32NewDescriptor(FD_PIPE_ASYNC, (int)(intptr_t)hPipe, -1);
        
        if (pseudoFd == -1) 
        {
            CloseHandle(hPipe);
            return -1;
        } 
        
        /*
	     * Set stdin equal to our pseudo FD and create the I/O completion
	     * port to be used for async I/O.
	     */
        if (! CreateIoCompletionPort(hPipe, hIoCompPort, pseudoFd, 1))
        {
	        Win32FreeDescriptor(pseudoFd);
	        CloseHandle(hPipe);
	        return -1;
	    }
    }

    return pseudoFd;    
}

/*
 *--------------------------------------------------------------
 *
 * OS_Read --
 *
 *	Pass through to the appropriate NT read function.
 *
 * Results:
 *	Returns number of byes read. Mimics unix read:.
 *		n bytes read, 0 or -1 failure: errno contains actual error
 *
 * Side effects:
 *	None.
 *
 *--------------------------------------------------------------
 */
int OS_Read(int fd, char * buf, size_t len)
{
    DWORD bytesRead;
    int ret = -1;

    ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));

    if (shutdownNow) return -1;

    switch (fdTable[fd].type) 
    {
	case FD_FILE_SYNC:
	case FD_FILE_ASYNC:
	case FD_PIPE_SYNC:
	case FD_PIPE_ASYNC:

        ASSERT(0 <= len && len <= ULONG_MAX);
        if (ReadFile(fdTable[fd].fid.fileHandle, buf, (DWORD)len, &bytesRead, NULL))
        {
            ret = bytesRead;
        }
        else
        {
		    fdTable[fd].Errno = GetLastError();
	    }

        break;

	case FD_SOCKET_SYNC:
	case FD_SOCKET_ASYNC:

        ASSERT(0 <= len && len <= INT_MAX);
        ret = recv(fdTable[fd].fid.sock, buf, (int)len, 0);
	    if (ret == SOCKET_ERROR) 
        {
		    fdTable[fd].Errno = WSAGetLastError();
		    ret = -1;
	    }

        break;
        
    default:

        ASSERT(0);
    }

    return ret;
}

/*
 *--------------------------------------------------------------
 *
 * OS_Write --
 *
 *	Perform a synchronous OS write.
 *
 * Results:
 *	Returns number of bytes written. Mimics unix write:
 *		n bytes written, 0 or -1 failure (??? couldn't find man page).
 *
 * Side effects:
 *	none.
 *
 *--------------------------------------------------------------
 */
int OS_Write(int fd, char * buf, size_t len)
{
    DWORD bytesWritten;
    int ret = -1;

    ASSERT(fd >= 0 && fd < WIN32_OPEN_MAX);

    if (shutdownNow) return -1;

    switch (fdTable[fd].type) 
    {
	case FD_FILE_SYNC:
	case FD_FILE_ASYNC:
	case FD_PIPE_SYNC:
	case FD_PIPE_ASYNC:

        ASSERT(0 <= len && len <= ULONG_MAX);
        if (WriteFile(fdTable[fd].fid.fileHandle, buf, (DWORD)len, &bytesWritten, NULL))
        {
            ret = bytesWritten;
        }
        else
        {
		    fdTable[fd].Errno = GetLastError();
	    }

        break;

	case FD_SOCKET_SYNC:
	case FD_SOCKET_ASYNC:

        ASSERT(0 <= len && len <= INT_MAX);
        ret = send(fdTable[fd].fid.sock, buf, (int)len, 0);
        if (ret == SOCKET_ERROR) 
        {
		    fdTable[fd].Errno = WSAGetLastError();
		    ret = -1;
	    }

        break;

    default:

        ASSERT(0);
    }

    return ret;
}

/*
 *----------------------------------------------------------------------
 *
 * OS_SpawnChild --
 *
 *	Spawns a new server listener process, and stores the information
 *      relating to the child in the supplied record.  A wait handler is
 *	registered on the child's completion.  This involves creating
 *        a process on NT and preparing a command line with the required
 *        state (currently a -childproc flag and the server socket to use
 *        for accepting connections).
 *
 * Results:
 *      0 if success, -1 if error.
 *
 * Side effects:
 *      Child process spawned.
 *
 *----------------------------------------------------------------------
 */
int OS_SpawnChild(char *execPath, int listenFd)
{
    STARTUPINFO StartupInfo;
    PROCESS_INFORMATION pInfo;
    BOOL success;

    memset((void *)&StartupInfo, 0, sizeof(STARTUPINFO));
    StartupInfo.cb = sizeof (STARTUPINFO);
    StartupInfo.lpReserved = NULL;
    StartupInfo.lpReserved2 = NULL;
    StartupInfo.cbReserved2 = 0;
    StartupInfo.lpDesktop = NULL;

    /*
     * FastCGI on NT will set the listener pipe HANDLE in the stdin of
     * the new process.  The fact that there is a stdin and NULL handles
     * for stdout and stderr tells the FastCGI process that this is a
     * FastCGI process and not a CGI process.
     */
    StartupInfo.dwFlags = STARTF_USESTDHANDLES;
    /*
     * XXX: Do I have to dup the handle before spawning the process or is
     *      it sufficient to use the handle as it's reference counted
     *      by NT anyway?
     */
    StartupInfo.hStdInput  = fdTable[listenFd].fid.fileHandle;
    StartupInfo.hStdOutput = INVALID_HANDLE_VALUE;
    StartupInfo.hStdError  = INVALID_HANDLE_VALUE;

    /*
     * Make the listener socket inheritable.
     */
    success = SetHandleInformation(StartupInfo.hStdInput, HANDLE_FLAG_INHERIT,
				   TRUE);
    if(!success) {
        exit(99);
    }

    /*
     * XXX: Might want to apply some specific security attributes to the
     *      processes.
     */
    success = CreateProcess(execPath,	/* LPCSTR address of module name */
			NULL,           /* LPCSTR address of command line */
		        NULL,		/* Process security attributes */
			NULL,		/* Thread security attributes */
			TRUE,		/* Inheritable Handes inherited. */
			0,		/* DWORD creation flags  */
		        NULL,           /* Use parent environment block */
			NULL,		/* Address of current directory name */
			&StartupInfo,   /* Address of STARTUPINFO  */
			&pInfo);	/* Address of PROCESS_INFORMATION   */
    if(success) {
        return 0;
    } else {
        return -1;
    }
}

/*
 *--------------------------------------------------------------
 *
 * OS_AsyncReadStdin --
 *
 *	This initiates an asynchronous read on the standard
 *	input handle.  This handle is not guaranteed to be
 *      capable of performing asynchronous I/O so we send a
 *      message to the StdinThread to do the synchronous read.
 *
 * Results:
 *	-1 if error, 0 otherwise.
 *
 * Side effects:
 *	Asynchronous message is queued to the StdinThread and an
 *      overlapped structure is allocated/initialized.
 *
 *--------------------------------------------------------------
 */
int OS_AsyncReadStdin(void *buf, int len, OS_AsyncProc procPtr,
                      ClientData clientData)
{
    POVERLAPPED_REQUEST pOv;

    ASSERT(fdTable[STDIN_FILENO].type != FD_UNUSED);

    pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST));
    ASSERT(pOv);
    memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST));
    pOv->clientData1 = (ClientData)buf;
    pOv->instance = fdTable[STDIN_FILENO].instance;
    pOv->procPtr = procPtr;
    pOv->clientData = clientData;

    PostQueuedCompletionStatus(hStdinCompPort, len, STDIN_FILENO,
                               (LPOVERLAPPED)pOv);
    return 0;
}

/*
 *--------------------------------------------------------------
 *
 * OS_AsyncRead --
 *
 *	This initiates an asynchronous read on the file
 *	handle which may be a socket or named pipe.
 *
 *	We also must save the ProcPtr and ClientData, so later
 *	when the io completes, we know who to call.
 *
 *	We don't look at any results here (the ReadFile may
 *	return data if it is cached) but do all completion
 *	processing in OS_Select when we get the io completion
 *	port done notifications.  Then we call the callback.
 *
 * Results:
 *	-1 if error, 0 otherwise.
 *
 * Side effects:
 *	Asynchronous I/O operation is queued for completion.
 *
 *--------------------------------------------------------------
 */
int OS_AsyncRead(int fd, int offset, void *buf, int len,
		 OS_AsyncProc procPtr, ClientData clientData)
{
    DWORD bytesRead;
    POVERLAPPED_REQUEST pOv;

    /*
     * Catch any bogus fd values
     */
    ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
    /*
     * Confirm that this is an async fd
     */
    ASSERT(fdTable[fd].type != FD_UNUSED);
    ASSERT(fdTable[fd].type != FD_FILE_SYNC);
    ASSERT(fdTable[fd].type != FD_PIPE_SYNC);
    ASSERT(fdTable[fd].type != FD_SOCKET_SYNC);

    pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST));
    ASSERT(pOv);
    memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST));
    /*
     * Only file offsets should be non-zero, but make sure.
     */
    if (fdTable[fd].type == FD_FILE_ASYNC)
	if (fdTable[fd].offset >= 0)
	    pOv->overlapped.Offset = fdTable[fd].offset;
	else
	    pOv->overlapped.Offset = offset;
    pOv->instance = fdTable[fd].instance;
    pOv->procPtr = procPtr;
    pOv->clientData = clientData;
    bytesRead = fd;
    /*
     * ReadFile returns: TRUE success, FALSE failure
     */
    if (!ReadFile(fdTable[fd].fid.fileHandle, buf, len, &bytesRead,
	(LPOVERLAPPED)pOv)) {
	fdTable[fd].Errno = GetLastError();
	if(fdTable[fd].Errno == ERROR_NO_DATA ||
	   fdTable[fd].Errno == ERROR_PIPE_NOT_CONNECTED) {
	    PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv);
	    return 0;
	}
	if(fdTable[fd].Errno != ERROR_IO_PENDING) {
	    PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv);
	    return -1;
	}
	fdTable[fd].Errno = 0;
    }
    return 0;
}

/*
 *--------------------------------------------------------------
 *
 * OS_AsyncWrite --
 *
 *	This initiates an asynchronous write on the "fake" file
 *	descriptor (which may be a file, socket, or named pipe).
 *	We also must save the ProcPtr and ClientData, so later
 *	when the io completes, we know who to call.
 *
 *	We don't look at any results here (the WriteFile generally
 *	completes immediately) but do all completion processing
 *	in OS_DoIo when we get the io completion port done
 *	notifications.  Then we call the callback.
 *
 * Results:
 *	-1 if error, 0 otherwise.
 *
 * Side effects:
 *	Asynchronous I/O operation is queued for completion.
 *
 *--------------------------------------------------------------
 */
int OS_AsyncWrite(int fd, int offset, void *buf, int len,
		  OS_AsyncProc procPtr, ClientData clientData)
{
    DWORD bytesWritten;
    POVERLAPPED_REQUEST pOv;

    /*
     * Catch any bogus fd values
     */
    ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
    /*
     * Confirm that this is an async fd
     */
    ASSERT(fdTable[fd].type != FD_UNUSED);
    ASSERT(fdTable[fd].type != FD_FILE_SYNC);
    ASSERT(fdTable[fd].type != FD_PIPE_SYNC);
    ASSERT(fdTable[fd].type != FD_SOCKET_SYNC);

    pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST));
    ASSERT(pOv);
    memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST));
    /*
     * Only file offsets should be non-zero, but make sure.
     */
    if (fdTable[fd].type == FD_FILE_ASYNC)
	/*
	 * Only file opened via OS_AsyncWrite with
	 * O_APPEND will have an offset != -1.
	 */
	if (fdTable[fd].offset >= 0)
	    /*
	     * If the descriptor has a memory mapped file
	     * handle, take the offsets from there.
	     */
	    if (fdTable[fd].hMapMutex != NULL) {
		/*
		 * Wait infinitely; this *should* not cause problems.
		 */
		WaitForSingleObject(fdTable[fd].hMapMutex, INFINITE);

		/*
		 * Retrieve the shared offset values.
		 */
		pOv->overlapped.OffsetHigh = *(fdTable[fd].offsetHighPtr);
		pOv->overlapped.Offset = *(fdTable[fd].offsetLowPtr);

		/*
		 * Update the shared offset values for the next write
		 */
		*(fdTable[fd].offsetHighPtr) += 0;	/* XXX How do I handle overflow */
		*(fdTable[fd].offsetLowPtr) += len;

		ReleaseMutex(fdTable[fd].hMapMutex);
	    } else
	        pOv->overlapped.Offset = fdTable[fd].offset;
	else
	    pOv->overlapped.Offset = offset;
    pOv->instance = fdTable[fd].instance;
    pOv->procPtr = procPtr;
    pOv->clientData = clientData;
    bytesWritten = fd;
    /*
     * WriteFile returns: TRUE success, FALSE failure
     */
    if (!WriteFile(fdTable[fd].fid.fileHandle, buf, len, &bytesWritten,
	(LPOVERLAPPED)pOv)) {
	fdTable[fd].Errno = GetLastError();
	if(fdTable[fd].Errno != ERROR_IO_PENDING) {
	    PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv);
	    return -1;
	}
	fdTable[fd].Errno = 0;
    }
    if (fdTable[fd].offset >= 0)
	fdTable[fd].offset += len;
    return 0;
}

/*
 *--------------------------------------------------------------
 *
 * OS_Close --
 *
 *	Closes the descriptor with routine appropriate for
 *      descriptor's type.
 *
 * Results:
 *	Socket or file is closed. Return values mimic Unix close:
 *		0 success, -1 failure
 *
 * Side effects:
 *	Entry in fdTable is marked as free.
 *
 *--------------------------------------------------------------
 */
int OS_Close(int fd)
{
    int ret = 0;

    /*
     * Catch it if fd is a bogus value
     */
    ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
    ASSERT(fdTable[fd].type != FD_UNUSED);

    switch (fdTable[fd].type) {
	case FD_PIPE_SYNC:
	case FD_PIPE_ASYNC:
	case FD_FILE_SYNC:
	case FD_FILE_ASYNC:
	    
        break;

    case FD_SOCKET_SYNC:
	case FD_SOCKET_ASYNC:

        /*
         * shutdown() the send side and then read() from client until EOF
         * or a timeout expires.  This is done to minimize the potential
         * that a TCP RST will be sent by our TCP stack in response to 
         * receipt of additional data from the client.  The RST would
         * cause the client to discard potentially useful response data.
         */

        if (shutdown(fdTable[fd].fid.sock, SD_SEND) == 0)
        {
            struct timeval tv;
            fd_set rfds;
            SOCKET sock = fdTable[fd].fid.sock;
            int rv;
            char trash[1024];
   
            FD_ZERO(&rfds);

            do 
            {
#pragma warning( disable : 4127 )
	            FD_SET(sock, &rfds);
#pragma warning( default : 4127 )
	            tv.tv_sec = 2;
	            tv.tv_usec = 0;
                rv = select(0, &rfds, NULL, NULL, &tv);
            }
            while (rv > 0 && recv(sock, trash, sizeof(trash), 0) > 0);
        }
        
        closesocket(fdTable[fd].fid.sock);

        break;

	default:

	    ret = -1;		/* fake failure */
    }

    Win32FreeDescriptor(fd);
    return ret;
}

/*
 *--------------------------------------------------------------
 *
 * OS_CloseRead --
 *
 *	Cancel outstanding asynchronous reads and prevent subsequent
 *      reads from completing.
 *
 * Results:
 *	Socket or file is shutdown. Return values mimic Unix shutdown:
 *		0 success, -1 failure
 *
 *--------------------------------------------------------------
 */
int OS_CloseRead(int fd)
{
    int ret = 0;

    /*
     * Catch it if fd is a bogus value
     */
    ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
    ASSERT(fdTable[fd].type == FD_SOCKET_ASYNC
	|| fdTable[fd].type == FD_SOCKET_SYNC);

    if (shutdown(fdTable[fd].fid.sock,0) == SOCKET_ERROR)
	ret = -1;
    return ret;
}

/*
 *--------------------------------------------------------------
 *
 * OS_DoIo --
 *
 *	This function was formerly OS_Select.  It's purpose is
 *      to pull I/O completion events off the queue and dispatch
 *      them to the appropriate place.
 *
 * Results:
 *	Returns 0.
 *
 * Side effects:
 *	Handlers are called.
 *
 *--------------------------------------------------------------
 */
int OS_DoIo(struct timeval *tmo)
{
    ULONG_PTR fd;
    unsigned long bytes;
    POVERLAPPED_REQUEST pOv;
    struct timeb tb;
    time_t ms;
    time_t ms_last;
    int err;

    /* XXX
     * We can loop in here, but not too long, as wait handlers
     * must run.
     * For cgi stdin, apparently select returns when io completion
     * ports don't, so don't wait the full timeout.
     */
    if(tmo)
	ms = (tmo->tv_sec*1000 + tmo->tv_usec/1000) / 2;
    else
	ms = 1000;
    ftime(&tb);
    ms_last = tb.time*1000 + tb.millitm;
    while (ms >= 0) {
	if(tmo && (ms = tmo->tv_sec*1000 + tmo->tv_usec/1000)> 100)
	    ms = 100;
        ASSERT(0 <= ms && ms < 0xFFFFFFFF);
	if (!GetQueuedCompletionStatus(hIoCompPort, &bytes, &fd,
           (LPOVERLAPPED *)&pOv, (DWORD)ms) && !pOv) {
	    err = WSAGetLastError();
	    return 0; /* timeout */
        }

	ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
	/* call callback if descriptor still valid */
	ASSERT(pOv);
	if(pOv->instance == fdTable[fd].instance)
	  (*pOv->procPtr)(pOv->clientData, bytes);
	free(pOv);

	ftime(&tb);
	ms -= (tb.time*1000 + tb.millitm - ms_last);
	ms_last = tb.time*1000 + tb.millitm;
    }
    return 0;
}

static int isAddrOK(struct sockaddr_in * inet_sockaddr, const char * okAddrs)
{
    static const char *token = " ,;:\t";
    char *ipaddr;
    char *p;

    if (okAddrs == NULL) return TRUE;

    ipaddr = inet_ntoa(inet_sockaddr->sin_addr);
    p = strstr(okAddrs, ipaddr);

    if (p == NULL) return FALSE;

    if (p == okAddrs)
    {
        p += strlen(ipaddr);
        return (strchr(token, *p) != NULL);
    }

    if (strchr(token, *--p) != NULL)
    {
        p += strlen(ipaddr) + 1;
        return (strchr(token, *p) != NULL);
    }

    return FALSE;
}

#ifndef NO_WSAACEPT
static int CALLBACK isAddrOKCallback(LPWSABUF  lpCallerId,
                                     LPWSABUF  dc0,
                                     LPQOS     dc1,
                                     LPQOS     dc2,
                                     LPWSABUF  dc3,
                                     LPWSABUF  dc4,
                                     GROUP     *dc5,
                                     DWORD_PTR data)
{
    struct sockaddr_in *sockaddr = (struct sockaddr_in *) lpCallerId->buf;

    // Touch the args to avoid warnings
    dc0 = NULL; dc1 = NULL; dc2 = NULL; dc3 = NULL; dc4 = NULL; dc5 = NULL;

    if ((void *) data == NULL) return CF_ACCEPT;

    if (sockaddr->sin_family != AF_INET) return CF_ACCEPT;

    return isAddrOK(sockaddr, (const char *) data) ? CF_ACCEPT : CF_REJECT;
}
#endif

static void printLastError(const char * text)
{
    LPVOID buf;

    FormatMessage( 
        FORMAT_MESSAGE_ALLOCATE_BUFFER | 
        FORMAT_MESSAGE_FROM_SYSTEM | 
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        GetLastError(),
        0,
        (LPTSTR) &buf,
        0,
        NULL 
    );
    
    fprintf(stderr, "%s: %s\n", text, (LPCTSTR) buf);
    LocalFree(buf);
}

static int acceptNamedPipe()
{
    int ipcFd = -1;

    if (! ConnectNamedPipe(hListen, NULL))
    {
        switch (GetLastError())
        {
            case ERROR_PIPE_CONNECTED:

                // A client connected after CreateNamedPipe but
                // before ConnectNamedPipe. Its a good connection.

                break;
        
            case ERROR_IO_PENDING:

                // The NamedPipe was opened with an Overlapped structure
                // and there is a pending io operation.  mod_fastcgi 
                // did this in 2.2.12 (fcgi_pm.c v1.52).

            case ERROR_PIPE_LISTENING:

                // The pipe handle is in nonblocking mode.

            case ERROR_NO_DATA:

                // The previous client closed its handle (and we failed
                // to call DisconnectNamedPipe)

            default:

                printLastError("unexpected ConnectNamedPipe() error");
        }
    }

    ASSERT(INT_MIN <= (intptr_t)hListen && (intptr_t)hListen <= INT_MAX);
    ipcFd = Win32NewDescriptor(FD_PIPE_SYNC, (int)(intptr_t)hListen, -1);
	if (ipcFd == -1) 
    {
        DisconnectNamedPipe(hListen);
    }

    return ipcFd;
}

static int acceptSocket(const char *webServerAddrs)
{
    SOCKET hSock;
    int ipcFd = -1;

    for (;;)
    {
        struct sockaddr sockaddr;
        int sockaddrLen = sizeof(sockaddr);

        for (;;)
        {
            const struct timeval timeout = {1, 0};
            fd_set readfds;

            FD_ZERO(&readfds);

#pragma warning( disable : 4127 ) 
            FD_SET((SOCKET)hListen, &readfds);
#pragma warning( default : 4127 ) 

            if (select(0, &readfds, NULL, NULL, &timeout) == 0)
            {
                if (shutdownPending) 
                {
                    OS_LibShutdown();
                    return -1;
                }
            }
            else 
            {
                break;
            }
        }
    
#if NO_WSAACEPT
        hSock = accept((SOCKET) hListen, &sockaddr, &sockaddrLen);

        if (hSock == INVALID_SOCKET)
        {
            break;
        }

        if (isAddrOK((struct sockaddr_in *) &sockaddr, webServerAddrs))
        {
            break;
        }

        closesocket(hSock);
#else
        hSock = WSAAccept((SOCKET)hListen,
                          &sockaddr,  
                          &sockaddrLen,               
                          isAddrOKCallback,  
                          (DWORD_PTR) webServerAddrs);

        if (hSock != INVALID_SOCKET)
        {
            break;
        }
        
        if (WSAGetLastError() != WSAECONNREFUSED)
        {
            break;
        }
#endif
    }

    if (hSock == INVALID_SOCKET) 
    {
        /* Use FormatMessage() */
        fprintf(stderr, "accept()/WSAAccept() failed: %d", WSAGetLastError());
        return -1;
    }
    
    ASSERT(INT_MIN <= (intptr_t)hSock && (intptr_t)hSock <= INT_MAX);
    ipcFd = Win32NewDescriptor(FD_SOCKET_SYNC, (int)(intptr_t)hSock, -1);
	if (ipcFd == -1) 
    {
	    closesocket(hSock);
	}

    return ipcFd;
}

/*
 *----------------------------------------------------------------------
 *
 * OS_Accept --
 *
 *  Accepts a new FastCGI connection.  This routine knows whether
 *  we're dealing with TCP based sockets or NT Named Pipes for IPC.
 *
 *  fail_on_intr is ignored in the Win lib.
 *
 * Results:
 *      -1 if the operation fails, otherwise this is a valid IPC fd.
 *
 *----------------------------------------------------------------------
 */
int OS_Accept(int listen_sock, int fail_on_intr, const char *webServerAddrs)
{
    int ipcFd = -1;

    // Touch args to prevent warnings
    listen_sock = 0; fail_on_intr = 0;

    // @todo Muliple listen sockets and sockets other than 0 are not
    // supported due to the use of globals.

    if (shutdownPending) 
    {
        OS_LibShutdown();
        return -1;
    }

    // The mutex is to keep other processes (and threads, when supported)
    // from going into the accept cycle.  The accept cycle needs to
    // periodically break out to check the state of the shutdown flag
    // and there's no point to having more than one thread do that.
    
    if (acceptMutex != INVALID_HANDLE_VALUE) 
    {
        if (WaitForSingleObject(acceptMutex, INFINITE) == WAIT_FAILED) 
        {
            printLastError("WaitForSingleObject() failed");
            return -1;
        }
    }
    
    if (shutdownPending) 
    {
        OS_LibShutdown();
    }
    else if (listenType == FD_PIPE_SYNC) 
    {
        ipcFd = acceptNamedPipe();
    }
    else if (listenType == FD_SOCKET_SYNC)
    {
        ipcFd = acceptSocket(webServerAddrs);
    }
    else
    {
        fprintf(stderr, "unknown listenType (%d)\n", listenType);
    }
	    
    if (acceptMutex != INVALID_HANDLE_VALUE) 
    {
        ReleaseMutex(acceptMutex);
    }

    return ipcFd;
}

/*
 *----------------------------------------------------------------------
 *
 * OS_IpcClose
 *
 *	OS IPC routine to close an IPC connection.
 *
 * Results:
 *
 *
 * Side effects:
 *      IPC connection is closed.
 *
 *----------------------------------------------------------------------
 */
int OS_IpcClose(int ipcFd)
{
    if (ipcFd == -1) return 0;

    /*
     * Catch it if fd is a bogus value
     */
    ASSERT((ipcFd >= 0) && (ipcFd < WIN32_OPEN_MAX));
    ASSERT(fdTable[ipcFd].type != FD_UNUSED);

    switch (listenType) 
    {
    case FD_PIPE_SYNC:
	    /*
	     * Make sure that the client (ie. a Web Server in this case) has
	     * read all data from the pipe before we disconnect.
	     */
	    if (! FlushFileBuffers(fdTable[ipcFd].fid.fileHandle)) return -1;

	    if (! DisconnectNamedPipe(fdTable[ipcFd].fid.fileHandle)) return -1;

        /* fall through */

    case FD_SOCKET_SYNC:

	    OS_Close(ipcFd);
	    break;

    case FD_UNUSED:
    default:

	    exit(106);
	    break;
    }

    return 0; 
}

/*
 *----------------------------------------------------------------------
 *
 * OS_IsFcgi --
 *
 *	Determines whether this process is a FastCGI process or not.
 *
 * Results:
 *      Returns 1 if FastCGI, 0 if not.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */
int OS_IsFcgi(int sock)
{
    // Touch args to prevent warnings
    sock = 0;

    /* XXX This is broken for sock */

	return (listenType != FD_UNUSED); 
}

/*
 *----------------------------------------------------------------------
 *
 * OS_SetFlags --
 *
 *      Sets selected flag bits in an open file descriptor.  Currently
 *      this is only to put a SOCKET into non-blocking mode.
 *
 *----------------------------------------------------------------------
 */
void OS_SetFlags(int fd, int flags)
{
    unsigned long pLong = 1L;
    int err;

    if (fdTable[fd].type == FD_SOCKET_SYNC && flags == O_NONBLOCK) {
        if (ioctlsocket(fdTable[fd].fid.sock, FIONBIO, &pLong) ==
	    SOCKET_ERROR) {
	    exit(WSAGetLastError());
        }
        if (!CreateIoCompletionPort((HANDLE)fdTable[fd].fid.sock,
				    hIoCompPort, fd, 1)) {
	    err = GetLastError();
	    exit(err);
	}

        fdTable[fd].type = FD_SOCKET_ASYNC;
    }
    return;
}

