Added tlspool_configvar() to libtlspool
[tlspool] / lib / libtlspool.c
index e6b8722..a57949d 100644 (file)
@@ -1,29 +1,46 @@
 /* tlspool/libtlspool.c -- Library function for starttls go-get-it */
 
+#include "whoami.h"
 
+#include <stdio.h>
 #include <stdlib.h>
 #include <errno.h>
 #include <string.h>
-#include <syslog.h>
 #include <assert.h>
 #include <stdint.h>
-#include <fcntl.h>
+#include <limits.h>
+#include <ctype.h>
 
 #include <unistd.h>
 #include <pthread.h>
-#include <io.h>
+#include <fcntl.h>
+#include <syslog.h>
 
+#include <tlspool/starttls.h>
+#include <tlspool/commands.h>
+
+#ifdef WINDOWS_PORT
+#include <winsock2.h>
+#else
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/un.h>
 #include <sys/select.h>
 #include <sys/resource.h>
+#endif
 
-#include <tlspool/starttls.h>
-#include <tlspool/commands.h>
+#if !defined(WINDOWS_PORT)
+#define closesocket(s) close(s)
+#endif
 
-#include <sys/cygwin.h>
+#ifdef WINDOWS_PORT
+#define PIPE_TIMEOUT 5000
+#define BUFSIZE 4096
+#define random rand
+#define srandom srand
 
+#define _tprintf printf
+#endif /* WINDOWS_PORT */
 
 /* The master thread will run the receiving side of the socket that connects
  * to the TLS Pool.  The have_master_lock is used with _trylock() and will
@@ -34,20 +51,43 @@ static pthread_mutex_t have_master_lock = PTHREAD_MUTEX_INITIALIZER;
 
 static void *master_thread (void *path);
 
-static int poolfd = -1;                /* Blocked retrieval with tlspool_socket() */
+static pool_handle_t poolfd = INVALID_POOL_HANDLE;             /* Blocked retrieval with tlspool_socket() */
 
 static pthread_cond_t updated_poolfd = PTHREAD_COND_INITIALIZER;
 
 static pthread_mutex_t prng_lock = PTHREAD_MUTEX_INITIALIZER;
 
-int tlspool_getpid () {
-       int fd;
 
-       if ((fd = open("/var/run/tlspool.pid", O_RDONLY)) != -1) {
-               char str_pid[256];
+/* Retrieve the process identity of the TLS Pool from the named file, or fall
+ * back on the default file if the name is set to NULL.  Returns -1 on failure.
+ */
+int tlspool_pid (char *opt_pidfile) {
+       int fd;
+       char str_pid [256];
+       char *endptr;
+       size_t len;
+       unsigned long pid;
 
-               if (read(fd, str_pid, sizeof(str_pid)) != -1) {
-                       return atoi(str_pid);
+       if (opt_pidfile == NULL) {
+               opt_pidfile = tlspool_configvar (NULL, "daemon_pidfile");
+       }
+       if (opt_pidfile == NULL) {
+               opt_pidfile = TLSPOOL_DEFAULT_PIDFILE_PATH;
+       }
+       assert (opt_pidfile != NULL);
+       fd = open (opt_pidfile, O_RDONLY);
+       if (fd != -1) {
+               len = read (fd, str_pid, sizeof (str_pid) -1);
+               close (fd);
+               if ((len > 0) && (len < sizeof (str_pid))) {
+                       str_pid [len] = '\0';
+                       pid = strtoul (str_pid, &endptr, 10);
+                       while ((endptr != NULL) && (isspace (*endptr))) {
+                               endptr++;
+                       }
+                       if ((pid >= 0) && (pid <= INT_MAX) && (!*endptr)) {
+                               return (int) pid;
+                       }
                }
        }
        return -1;
@@ -60,9 +100,9 @@ int tlspool_getpid () {
  * on the poolfd.  This is the process that actually contacts the TLS Pool
  * and sets up the poolfd socket.
  */
-int tlspool_socket (char *path) {
-       int poolfdsample = poolfd;
-       if (poolfdsample < 0) {
+pool_handle_t tlspool_open_poolhandle (char *path) {
+       pool_handle_t poolfdsample = poolfd;
+       if (poolfdsample == INVALID_POOL_HANDLE) {
                pthread_mutex_t local_cond_wait = PTHREAD_MUTEX_INITIALIZER;
                //
                // Now that we have established a (first) poolfd, start up
@@ -72,18 +112,25 @@ int tlspool_socket (char *path) {
                        unsigned int seed;
                        pid_t me;
                        if (!path) {
+                               path = tlspool_configvar (NULL, "socket_name");
+                       }
+                       if (!path) {
                                path = TLSPOOL_DEFAULT_SOCKET_PATH;
                        }
-                       if (strlen (path) + 1 > sizeof (((struct sockaddr_un *) NULL)->sun_path)) {
-                               syslog (LOG_ERR, "TLS Pool path name too long for UNIX domain socket");
-                               exit (1);
+                       assert (path != NULL);
+                       fprintf (stderr, "DEBUG: Opening TLS Pool on socket path %s\n", path);
+#ifndef WINDOWS_PORT
+                       if (strlen(path) + 1 > sizeof(((struct sockaddr_un *) NULL)->sun_path)) {
+                               syslog(LOG_ERR, "TLS Pool path name too long for UNIX domain socket");
+                               exit(1);
                        }
-                       if (pthread_create (&thr, NULL, master_thread, (void *) path) != 0) {
-                               syslog (LOG_NOTICE, "Failed to create TLS Pool client master thread");
-                               pthread_mutex_unlock (&have_master_lock);
-                               close (poolfd);
-                               poolfd = -1;
-                               return -1;
+#endif
+                       if (pthread_create(&thr, NULL, master_thread, (void *)path) != 0) {
+                               syslog(LOG_NOTICE, "Failed to create TLS Pool client master thread");
+                               pthread_mutex_unlock(&have_master_lock);
+                               tlspool_close_poolhandle(poolfd);
+                               poolfd = INVALID_POOL_HANDLE;
+                               return INVALID_POOL_HANDLE;
                        }
                        pthread_detach (thr);
                        //
@@ -103,7 +150,7 @@ int tlspool_socket (char *path) {
                // shared mutex -- which is why we use a local mutex per
                // thread: we don't need to wait for unique access.
                assert (pthread_mutex_lock (&local_cond_wait) == 0);
-               while (poolfdsample = poolfd, poolfdsample < 0) {
+               while (poolfdsample = poolfd, poolfdsample == INVALID_POOL_HANDLE) {
                        pthread_cond_wait (&updated_poolfd, &local_cond_wait);
                }
                pthread_mutex_unlock (&local_cond_wait);
@@ -116,7 +163,10 @@ int tlspool_socket (char *path) {
  * number of available file descriptors.  Note: The result is cached, so
  * don't use root to increase beyond max in setrlimit() after calling this.
  */
-int tlspool_simultaneous_starttls (void) {
+int tlspool_simultaneous_starttls(void) {
+#ifdef WINDOWS_PORT
+       return 512;
+#else /* WINDOWS_PORT */
        static int simu = -1;
        if (simu < 0) {
                struct rlimit rlimit_nofile;
@@ -127,6 +177,7 @@ int tlspool_simultaneous_starttls (void) {
                simu = rlimit_nofile.rlim_max / 2;  // 2 FDs per STARTTLS
        }
        return simu;
+#endif /* WINDOWS_PORT */
 }
 
 
@@ -139,7 +190,7 @@ int tlspool_simultaneous_starttls (void) {
 struct registry_entry {
        pthread_mutex_t *sig;           /* Wait for master thread's recvmsg() */
        struct tlspool_command *buf;    /* Buffer to hold received command */
-       int pfd;                        /* Client thread's assumed poolfd */
+       pool_handle_t pfd;                      /* Client thread's assumed poolfd */
 };
 
 static struct registry_entry **registry;
@@ -154,7 +205,7 @@ static pthread_mutex_t registry_lock = PTHREAD_MUTEX_INITIALIZER;
  * is reset to -1.
  *
  * The return value is 0 on success, or -1 on failure; the most probable
- * cause for failure is 
+ * cause for failure is
  */
 static int registry_update (int *reqid, struct registry_entry *entry) {
        static int simu = -1;
@@ -209,7 +260,7 @@ static int registry_update (int *reqid, struct registry_entry *entry) {
  * Any outstanding registry entries will be sent an ERROR value at this
  * time.
  */
-static void registry_flush (int poolfd) {
+static void registry_flush (pool_handle_t poolfd) {
        int regid = tlspool_simultaneous_starttls ();
        assert (pthread_mutex_lock (&registry_lock) == 0);
        while (regid-- > 0) {
@@ -229,6 +280,110 @@ static void registry_flush (int poolfd) {
        pthread_mutex_unlock (&registry_lock);
 }
 
+#ifdef WINDOWS_PORT
+static pool_handle_t open_named_pipe (LPCTSTR lpszPipename)
+{
+       HANDLE hPipe;
+       //struct tlspool_command chBuf;
+       BOOL   fSuccess = FALSE;
+       DWORD  dwMode;
+
+       // Try to open a named pipe; wait for it, if necessary.
+
+       while (1)
+       {
+               hPipe = CreateFile(
+                       lpszPipename,   // pipe name
+                       GENERIC_READ |  // read and write access
+                       GENERIC_WRITE,
+                       0,              // no sharing
+                       NULL,           // default security attributes
+                       OPEN_EXISTING,  // opens existing pipe
+                       FILE_FLAG_OVERLAPPED, // overlapped
+                       NULL);          // no template file
+
+               // Break if the pipe handle is valid.
+               if (hPipe != INVALID_POOL_HANDLE)
+                       break;
+
+               // Exit if an error other than ERROR_PIPE_BUSY occurs.
+               if (GetLastError() != ERROR_PIPE_BUSY)
+               {
+                       _tprintf(TEXT("Could not open pipe. GLE=%d\n"), GetLastError());
+                       return INVALID_POOL_HANDLE;
+               }
+
+               // All pipe instances are busy, so wait for 20 seconds.
+               if (!WaitNamedPipe(lpszPipename, 20000))
+               {
+                       printf("Could not open pipe: 20 second wait timed out.");
+                       return INVALID_POOL_HANDLE;
+               }
+       }
+       // The pipe connected; change to message-read mode.
+       dwMode = PIPE_READMODE_MESSAGE;
+       fSuccess = SetNamedPipeHandleState(
+               hPipe,    // pipe handle
+               &dwMode,  // new pipe mode
+               NULL,     // don't set maximum bytes
+               NULL);    // don't set maximum time
+       if (!fSuccess)
+       {
+               _tprintf(TEXT("SetNamedPipeHandleState failed. GLE=%d\n"), GetLastError());
+               return INVALID_POOL_HANDLE;
+       }
+       ULONG ServerProcessId;
+       if (GetNamedPipeServerProcessId(hPipe, &ServerProcessId)) {
+               printf("GetNamedPipeServerProcessId: ServerProcessId = %ld\n", ServerProcessId);
+       } else {
+               _tprintf(TEXT("GetNamedPipeServerProcessId failed. GLE=%d\n"), GetLastError());
+       }
+       return hPipe;
+}
+
+static int np_send_command(struct tlspool_command *cmd) {
+       DWORD  cbToWrite, cbWritten;
+       OVERLAPPED overlapped;
+       BOOL fSuccess;
+
+       /* Send the request */
+       // Send a message to the pipe server.
+
+       cbToWrite = sizeof (struct tlspool_command);
+       _tprintf(TEXT("Sending %d byte cmd\n"), cbToWrite);
+
+       memset(&overlapped, 0, sizeof(overlapped));
+       overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+       fSuccess = WriteFile(
+               poolfd,                  // pipe handle
+               cmd,                    // cmd message
+               cbToWrite,              // cmd message length
+               NULL,                  // bytes written
+               &overlapped);            // overlapped
+
+       if (!fSuccess && GetLastError() == ERROR_IO_PENDING )
+       {
+// printf ("DEBUG: Write I/O pending\n");
+               fSuccess = WaitForSingleObject(overlapped.hEvent, INFINITE) == WAIT_OBJECT_0;
+       }
+
+       if (fSuccess) {
+               fSuccess = GetOverlappedResult(poolfd, &overlapped, &cbWritten, TRUE);
+       }
+
+       if (!fSuccess)
+       {
+               _tprintf(TEXT("WriteFile to pipe failed. GLE=%d\n"), GetLastError());
+               errno = EPIPE;
+               return -1;
+       } else {
+// printf ("DEBUG: Wrote %ld bytes to pipe\n", cbWritten);
+       }
+// printf("DEBUG: Message sent to server, receiving reply as follows:\n");
+       return 0;
+}
+#endif /* WINDOWS_PORT */
 
 /* The master thread issues the recv() commands on the TLS Pool socket, and
  * redistributes the result to the registry entries that are waiting for
@@ -247,49 +402,70 @@ static void registry_flush (int poolfd) {
  * TLS Pool anew, using exponential back-off.
  */
 static void *master_thread (void *path) {
+#ifndef WINDOWS_PORT
        useconds_t usec;
        struct sockaddr_un sun;
-       struct tlspool_command cmd;
        //NOT-USED// char anc [CMSG_SPACE(sizeof (int))];
        struct iovec iov;
        struct cmsghdr *cmsg;
        struct msghdr mh = { 0 };
+#else
+       DWORD usec;
+#endif
+       struct tlspool_command cmd;
+       //NOT-USED// char anc [CMSG_SPACE(sizeof (int))];
        struct registry_entry *entry;
+#ifdef WINDOWS_PORT
+       BOOL   fSuccess = FALSE;
+       DWORD  cbRead;
+#endif
 
+#ifndef WINDOWS_PORT
        //
        // Setup path information -- value and size were checked
-       bzero (&sun, sizeof (sun));
+       memset (&sun, 0, sizeof (sun));
        strcpy (sun.sun_path, (char *) path);
        sun.sun_family = AF_UNIX;
+#endif
+
        //
        // Service forever
        while (1) {
                //
                // If any old socket clients persist, tell them that the
                // TLS Pool has been disconnected.
-               if (poolfd >= 0) {
-                       int poolfdcopy = poolfd;
+               if (poolfd != INVALID_POOL_HANDLE) {
+                       pool_handle_t poolfdcopy = poolfd;
 // printf ("DEBUG: Removing old poolfd %d\n", poolfd);
-                       poolfd = -1;
-                       registry_flush (-1);
-                       close (poolfdcopy);
+                       poolfd = INVALID_POOL_HANDLE;
+                       registry_flush (INVALID_POOL_HANDLE);
+                       tlspool_close_poolhandle (poolfdcopy);
                }
                //
                // First, connect to the TLS Pool; upon failure, retry
                // with 1s, 2s, 4s, 8s, 16s, 32s, 32s, 32s, ... intervals.
                usec = 1000000;
-               while (poolfd < 0) {
-                       int newpoolfd = socket (AF_UNIX, SOCK_STREAM, 0);
-                       if (newpoolfd != -1) {
+               while (poolfd == INVALID_POOL_HANDLE) {
+#ifdef WINDOWS_PORT
+// printf ("DEBUG: path = %s\n", (char *) path);
+                       pool_handle_t newpoolfd = open_named_pipe ((LPCTSTR) path);
+// printf ("DEBUG: newpoolfd = %d\n", newpoolfd);
+                       if (newpoolfd != INVALID_POOL_HANDLE) {
+                               poolfd = newpoolfd;
+                       }
+#else
+                       pool_handle_t newpoolfd = socket (AF_UNIX, SOCK_STREAM, 0);
+                       if (newpoolfd != INVALID_POOL_HANDLE) {
                                if (connect (newpoolfd, (struct sockaddr *) &sun, SUN_LEN (&sun)) == 0) {
 // printf ("DEBUG: Succeeded connect() to TLS Pool\n");
                                        poolfd = newpoolfd;
                                } else {
-                                       close (newpoolfd);
-                                       newpoolfd = -1;
+                                       tlspool_close_poolhandle (newpoolfd);
+                                       newpoolfd = INVALID_POOL_HANDLE;
                                }
                        }
 // printf ("DEBUG: Trying new poolfd %d for path %s\n", poolfd, sun.sun_path);
+#endif
                        //
                        // Signal a newly set poolfd value to all waiting.
                        // Note that we do not need to claim a mutex first;
@@ -303,8 +479,12 @@ static void *master_thread (void *path) {
 // printf ("DEBUG: Signalled slave threads with poolfd %d\n", poolfd);
                        //
                        // Wait before repeating, with exponential back-off
-                       if (poolfd < 0) {
-                               usleep (usec);
+                       if (poolfd == INVALID_POOL_HANDLE) {
+#ifdef WINDOWS_PORT
+                               Sleep(usec / 1000);
+#else
+                               usleep(usec);
+#endif
                                usec <<= 1;
                                if (usec > 32000000) {
                                        usec = 32000000;
@@ -318,6 +498,38 @@ static void *master_thread (void *path) {
                // back up to the re-connection logic.
                while (1) {
                        int retval;
+#ifdef WINDOWS_PORT
+                       OVERLAPPED overlapped;
+
+                       memset(&overlapped, 0, sizeof(overlapped));
+                       overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+                       // Read from the pipe.
+                       fSuccess = ReadFile(
+                               poolfd,       // pipe handle
+                               &cmd,         // buffer to receive reply
+                               sizeof (cmd), // size of buffer
+                               NULL,         // number of bytes read
+                               &overlapped); // not overlapped
+
+                       if (!fSuccess && GetLastError() == ERROR_IO_PENDING )
+                       {
+// printf ("DEBUG: Read I/O pending\n");
+                               fSuccess = WaitForSingleObject(overlapped.hEvent, INFINITE) == WAIT_OBJECT_0;
+                       }
+
+                       if (fSuccess) {
+                               fSuccess = GetOverlappedResult(poolfd, &overlapped, &cbRead, TRUE);
+                       }
+
+                       if (!fSuccess)
+                       {
+                               _tprintf(TEXT("ReadFile from pipe failed. GLE=%d\n"), GetLastError());
+                               retval = -1;
+                       } else {
+// printf ("DEBUG: Read %ld bytes from pipe\n", cbRead);
+                       }
+#else
                        iov.iov_base = &cmd;
                        iov.iov_len = sizeof (cmd);
                        mh.msg_iov = &iov;
@@ -338,6 +550,7 @@ static void *master_thread (void *path) {
 // printf ("DEBUG: recvmsg() returned -1 due to: %s\n", strerror (errno));
                                break;
                        }
+#endif /* WINDOWS_PORT */
                        //
                        // Determine where to post the received message
                        entry = registry [cmd.pio_reqid];
@@ -351,9 +564,13 @@ static void *master_thread (void *path) {
                                        cmd.pio_cmd = PIOC_ERROR_V2;
                                        cmd.pio_data.pioc_error.tlserrno = EPIPE;
                                        strncpy (cmd.pio_data.pioc_error.message, "Client prematurely left TLS Pool negotiations", sizeof (cmd.pio_data.pioc_error.message));
+#ifdef WINDOWS_PORT
+                                       np_send_command (&cmd);
+#else
                                        sendmsg (poolfd, &mh, MSG_NOSIGNAL);
+#endif
                                        // Ignore errors
-printf ("DEBUG: Sent      PIOC_ERROR_V2 as callback to TLS Pool\n");
+// printf ("DEBUG: Sent      PIOC_ERROR_V2 as callback to TLS Pool\n");
                                }
                                // Do not attempt delivery
                                continue;
@@ -375,12 +592,12 @@ printf ("DEBUG: Sent      PIOC_ERROR_V2 as callback to TLS Pool\n");
  * doing that yet.  Then, wait until a message has been received.
  */
 static void registry_recvmsg (struct registry_entry *entry) {
-       static int lastpoolfd = -1;
+       static pool_handle_t lastpoolfd = INVALID_POOL_HANDLE;
        //
        // Detect poolfd socket change for potential dangling recipients
        if (entry->pfd != lastpoolfd) {
-               lastpoolfd = tlspool_socket (NULL);
-               if ((entry->pfd != lastpoolfd) && (lastpoolfd != -1)) {
+               lastpoolfd = tlspool_open_poolhandle (NULL);
+               if ((entry->pfd != lastpoolfd) && (lastpoolfd != INVALID_POOL_HANDLE)) {
                        // Signal PIOC_ERROR to outdated recipients.
                        // (That will include the current recipient.)
                        registry_flush (lastpoolfd);
@@ -388,7 +605,7 @@ static void registry_recvmsg (struct registry_entry *entry) {
        }
        //
        // Now wait for the registered command structure to be filled
-       // by the master thread.  Note that the call to tlspool_socket()
+       // by the master thread.  Note that the call to tlspool_open_poolhandle()
        // above is made when this function is first called, and that
        // routine ensures running of the master thread.
        assert (pthread_mutex_lock (entry->sig) == 0);
@@ -417,13 +634,12 @@ int tlspool_ping (pingpool_t *pingdata) {
        pthread_mutex_t recvwait = PTHREAD_MUTEX_INITIALIZER;
        struct registry_entry regent = { .sig = &recvwait, .buf = &cmd };
        int entry_reqid = -1;
-       int poolfd = -1;
-       struct iovec iov;
-       struct msghdr mh = { 0 };
+       pool_handle_t poolfd = INVALID_POOL_HANDLE;
 
        /* Prepare command structure */
-       poolfd = tlspool_socket (NULL);
-       if (poolfd == -1) {
+       poolfd = tlspool_open_poolhandle (NULL);
+// printf ("DEBUG: poolfd = %d\n", poolfd);
+       if (poolfd == INVALID_POOL_HANDLE) {
                errno = ENODEV;
                return -1;
        }
@@ -435,24 +651,26 @@ int tlspool_ping (pingpool_t *pingdata) {
                errno = EBUSY;
                return -1;
        }
-       bzero (&cmd, sizeof (cmd));     /* Do not leak old stack info */
+       memset (&cmd, 0, sizeof (cmd)); /* Do not leak old stack info */
        cmd.pio_reqid = entry_reqid;
        cmd.pio_cbid = 0;
        cmd.pio_cmd = PIOC_PING_V2;
        memcpy (&cmd.pio_data.pioc_ping, pingdata, sizeof (struct pioc_ping));
-
+#ifdef WINDOWS_PORT
+       if (np_send_command (&cmd) == -1) {
+               // errno inherited from np_send_command ()
+               registry_update (&entry_reqid, NULL);
+               return -1;
+       }
+#else
        /* Send the request */
-       iov.iov_base = &cmd;
-       iov.iov_len = sizeof (cmd);
-       mh.msg_iov = &iov;
-       mh.msg_iovlen = 1;
-       if (sendmsg (poolfd, &mh, MSG_NOSIGNAL) == -1) {
+       if (send (poolfd, &cmd, sizeof (cmd), MSG_NOSIGNAL) == -1) {
                // Let SIGPIPE be reported as EPIPE
                registry_update (&entry_reqid, NULL);
                // errno inherited from sendmsg()
                return -1;
        }
-
+#endif
        /* Await response and process it */
        registry_recvmsg (&regent);
        registry_update (&entry_reqid, NULL);
@@ -473,6 +691,18 @@ int tlspool_ping (pingpool_t *pingdata) {
        }
 }
 
+#if defined(WINDOWS_PORT)
+static int socket_dup_protocol_info(int fd, int pid, LPWSAPROTOCOL_INFOW lpProtocolInfo)
+{
+       if (WSADuplicateSocketW((SOCKET)fd, pid, lpProtocolInfo) == SOCKET_ERROR) {
+               errno = EPIPE;
+               return -1;
+       } else {
+               return 0;
+       }       
+}
+#endif
+
 /* The library function for starttls, which is normally called through one
  * of the two inline variations below, which start client and server sides.
  *
@@ -522,20 +752,22 @@ int tlspool_starttls (int cryptfd, starttls_t *tlsdata,
        pthread_mutex_t recvwait = PTHREAD_MUTEX_INITIALIZER;
        struct registry_entry regent = { .sig = &recvwait, .buf = &cmd };
        int entry_reqid = -1;
-       int poolfd = -1;
+       pool_handle_t poolfd = INVALID_POOL_HANDLE;
        int plainfd = -1;
        int sentfd = -1;
-       char anc [CMSG_SPACE(sizeof (int))];
+#ifndef WINDOWS_PORT
        struct iovec iov;
        struct cmsghdr *cmsg;
        struct msghdr mh = { 0 };
+       char anc[CMSG_SPACE(sizeof(int))];
+#endif
        int processing;
        int renegotiate = 0 != (tlsdata->flags & PIOF_STARTTLS_RENEGOTIATE);
 
        /* Prepare command structure */
-       poolfd = tlspool_socket (NULL);
-       if (poolfd == -1) {
-               close (cryptfd);
+       poolfd = tlspool_open_poolhandle (NULL);
+       if (poolfd == INVALID_POOL_HANDLE) {
+               closesocket(cryptfd);
                errno = ENODEV;
                return -1;
        }
@@ -544,26 +776,28 @@ int tlspool_starttls (int cryptfd, starttls_t *tlsdata,
        pthread_mutex_lock (&recvwait);         // Will await unlock by master
        /* Determine the request ID */
        if (registry_update (&entry_reqid, &regent) != 0) {
-               close (cryptfd);
+               closesocket(cryptfd);
                errno = EBUSY;
                return -1;
        }
-       bzero (&cmd, sizeof (cmd));     /* Do not leak old stack info */
+       memset (&cmd, 0, sizeof (cmd)); /* Do not leak old stack info */
        cmd.pio_reqid = entry_reqid;
        cmd.pio_cbid = 0;
        cmd.pio_cmd = PIOC_STARTTLS_V2;
        memcpy (&cmd.pio_data.pioc_starttls, tlsdata, sizeof (struct pioc_starttls));
 
-#if RAND_MAX < 0xfffff
-#  error "Failure on assumption of 16 bits of random material per random() call"
-#endif
-
 #if TLSPOOL_CTLKEYLEN != 16
 #  error "Failure on assumption of 16 bytes per ctlkey"
 #endif
 
        if (!renegotiate) {
                assert (pthread_mutex_lock (&prng_lock) == 0);
+#if RAND_MAX >= 0xffffffff
+               * (uint32_t *) &cmd.pio_data.pioc_starttls.ctlkey [ 0] = random ();
+               * (uint32_t *) &cmd.pio_data.pioc_starttls.ctlkey [ 4] = random ();
+               * (uint32_t *) &cmd.pio_data.pioc_starttls.ctlkey [ 8] = random ();
+               * (uint32_t *) &cmd.pio_data.pioc_starttls.ctlkey [12] = random ();
+#elif RAND_MAX >= 0xffff
                * (uint16_t *) &cmd.pio_data.pioc_starttls.ctlkey [ 0] = random ();
                * (uint16_t *) &cmd.pio_data.pioc_starttls.ctlkey [ 2] = random ();
                * (uint16_t *) &cmd.pio_data.pioc_starttls.ctlkey [ 4] = random ();
@@ -572,6 +806,26 @@ int tlspool_starttls (int cryptfd, starttls_t *tlsdata,
                * (uint16_t *) &cmd.pio_data.pioc_starttls.ctlkey [10] = random ();
                * (uint16_t *) &cmd.pio_data.pioc_starttls.ctlkey [12] = random ();
                * (uint16_t *) &cmd.pio_data.pioc_starttls.ctlkey [14] = random ();
+#elif RAND_MAX >= 0xff
+               * (uint8_t *) &cmd.pio_data.pioc_starttls.ctlkey [ 0] = random ();
+               * (uint8_t *) &cmd.pio_data.pioc_starttls.ctlkey [ 1] = random ();
+               * (uint8_t *) &cmd.pio_data.pioc_starttls.ctlkey [ 2] = random ();
+               * (uint8_t *) &cmd.pio_data.pioc_starttls.ctlkey [ 3] = random ();
+               * (uint8_t *) &cmd.pio_data.pioc_starttls.ctlkey [ 4] = random ();
+               * (uint8_t *) &cmd.pio_data.pioc_starttls.ctlkey [ 5] = random ();
+               * (uint8_t *) &cmd.pio_data.pioc_starttls.ctlkey [ 6] = random ();
+               * (uint8_t *) &cmd.pio_data.pioc_starttls.ctlkey [ 7] = random ();
+               * (uint8_t *) &cmd.pio_data.pioc_starttls.ctlkey [ 8] = random ();
+               * (uint8_t *) &cmd.pio_data.pioc_starttls.ctlkey [ 9] = random ();
+               * (uint8_t *) &cmd.pio_data.pioc_starttls.ctlkey [10] = random ();
+               * (uint8_t *) &cmd.pio_data.pioc_starttls.ctlkey [11] = random ();
+               * (uint8_t *) &cmd.pio_data.pioc_starttls.ctlkey [12] = random ();
+               * (uint8_t *) &cmd.pio_data.pioc_starttls.ctlkey [13] = random ();
+               * (uint8_t *) &cmd.pio_data.pioc_starttls.ctlkey [14] = random ();
+               * (uint8_t *) &cmd.pio_data.pioc_starttls.ctlkey [15] = random ();
+#else
+#  error "Unsuitable random() function due to RAND_MAX value < 0xff"
+#endif
                pthread_mutex_unlock (&prng_lock);
        }
 // printf ("DEBUG: ctlkey =");
@@ -579,32 +833,36 @@ int tlspool_starttls (int cryptfd, starttls_t *tlsdata,
 // printf ("\n");
 
        /* Send the request */
+#ifndef WINDOWS_PORT
        iov.iov_base = &cmd;
-       iov.iov_len = sizeof (cmd);
+       iov.iov_len = sizeof(cmd);
        mh.msg_iov = &iov;
        mh.msg_iovlen = 1;
+#endif
        if (!renegotiate) {
-#ifndef __CYGWIN__
+#ifndef WINDOWS_PORT
                mh.msg_control = anc;
                mh.msg_controllen = sizeof (anc);
                cmsg = CMSG_FIRSTHDR (&mh);
                cmsg->cmsg_level = SOL_SOCKET;
                cmsg->cmsg_type = SCM_RIGHTS;
-               * (int *) CMSG_DATA (cmsg) = cryptfd;   /* cannot close it yet */
-               cmsg->cmsg_len = CMSG_LEN (sizeof (int));
-#else /* ifdef __CYGWIN__ */
+               *(int *)CMSG_DATA(cmsg) = cryptfd;      /* cannot close it yet */
+               cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+#else /* WINDOWS_PORT */
                // cmd was already set to 0, including ancilary data simulation
                if (1 /*is_sock(wsock)*/) {
                        // Send a socket
-                       int pid = tlspool_getpid();
+                       LONG pid;
+
+                       GetNamedPipeServerProcessId(poolfd, &pid);
                        cmd.pio_ancil_type = ANCIL_TYPE_SOCKET;
-                       printf("pid = %d, cryptfd = %d\n", pid, cryptfd);
-                       if (cygwin_socket_dup_protocol_info (cryptfd, pid, &cmd.pio_ancil_data.pioa_socket) == -1) {
-                               printf("cygwin_socket_dup_protocol_info error\n");
+                       printf("DEBUG: pid = %d, cryptfd = %d\n", pid, cryptfd);
+                       if (socket_dup_protocol_info(cryptfd, pid, &cmd.pio_ancil_data.pioa_socket) == -1) {
+                               // printf("DEBUG: cygwin_socket_dup_protocol_info error\n");
                                // Let SIGPIPE be reported as EPIPE
-                               close (cryptfd);
+                               closesocket(cryptfd);
                                registry_update (&entry_reqid, NULL);
-                               // errno inherited from sendmsg()
+                               // errno inherited from socket_dup_protocol_info()
                                return -1;
                        }
                        //... (..., &cmd.pio_ancil_data.pioa_socket, ...);
@@ -613,8 +871,16 @@ int tlspool_starttls (int cryptfd, starttls_t *tlsdata,
                        cmd.pio_ancil_type = ANCIL_TYPE_FILEHANDLE;
                        //... (..., &cmd.pio_ancil_data.pioa_filehandle, ...);
                }
-#endif /* __CYGWIN__ */
+#endif /* WINDOWS_PORT */
+       }
+#ifdef WINDOWS_PORT
+       if (np_send_command (&cmd) == -1) {
+               close (cryptfd);
+               registry_update (&entry_reqid, NULL);
+               // errno inherited from np_send_command ()
+               return -1;
        }
+#else
        if (sendmsg (poolfd, &mh, MSG_NOSIGNAL) == -1) {
                // Let SIGPIPE be reported as EPIPE
                close (cryptfd);
@@ -622,6 +888,7 @@ int tlspool_starttls (int cryptfd, starttls_t *tlsdata,
                // errno inherited from sendmsg()
                return -1;
        }
+#endif /* WINDOWS_PORT */
        sentfd = cryptfd;  /* Close anytime after response and before fn end */
 
        /* Handle responses until success or error */
@@ -631,7 +898,7 @@ int tlspool_starttls (int cryptfd, starttls_t *tlsdata,
                //NOTUSED// mh.msg_controllen = sizeof (anc);
                registry_recvmsg (&regent);
                if (sentfd >= 0) {
-                       close (sentfd);
+                       closesocket(sentfd);
                }
                sentfd = -1;
                switch (cmd.pio_cmd) {
@@ -649,16 +916,26 @@ int tlspool_starttls (int cryptfd, starttls_t *tlsdata,
                                /* default namedconnect() implementation */
                                plainfd = * (int *) privdata;
                                if ((plainfd < 0) && (cmd.pio_cmd == PIOC_PLAINTEXT_CONNECT_V2)) {
-                                       int soxx [2];
+#if !defined(WINDOWS_PORT)
+                                       int soxx[2];
+#else
+                                       // https://github.com/ncm/selectable-socketpair
+                                       extern int dumb_socketpair(SOCKET socks[2], int make_overlapped);
+                                       SOCKET soxx[2];
+#endif
                                        //TODO// Setup for TCP, UDP, SCTP
-                                       if (socketpair (AF_UNIX, SOCK_STREAM, 0, soxx) == 0) {
-                                               printf("HFM: socketpair succeeded\n");
+#ifndef WINDOWS_PORT
+                                       if (socketpair (AF_UNIX, SOCK_SEQPACKET, 0, soxx) == 0) {
+#else /* WINDOWS_PORT */
+                                       if (dumb_socketpair(soxx, 1) == 0) {
+#endif /* WINDOWS_PORT */
+                                               // printf("DEBUG: socketpair succeeded\n");
                                                /* Socketpair created */
                                                plainfd = soxx [0];
                                                * (int *) privdata = soxx [1];
                                        } else {
                                                /* Socketpair failed */
-                                               printf("HFM: socketpair failed\n");
+                                               // printf("DEBUG: socketpair failed\n");
                                                cmd.pio_cmd = PIOC_ERROR_V2;
                                                cmd.pio_data.pioc_error.tlserrno = errno;
                                                plainfd = -1;
@@ -667,7 +944,7 @@ int tlspool_starttls (int cryptfd, starttls_t *tlsdata,
                        }
                        /* We may now have a value to send in plainfd */
                        if (plainfd >= 0) {
-#ifndef __CYGWIN__
+#ifndef WINDOWS_PORT
                                mh.msg_control = anc;
                                mh.msg_controllen = sizeof (anc);
                                cmsg = CMSG_FIRSTHDR (&mh);
@@ -675,21 +952,20 @@ int tlspool_starttls (int cryptfd, starttls_t *tlsdata,
                                cmsg->cmsg_type = SCM_RIGHTS;
                                * (int *) CMSG_DATA (cmsg) = plainfd;
                                cmsg->cmsg_len = CMSG_LEN (sizeof (int));
-#else /* ifdef __CYGWIN__ */
-                               SOCKET wsock = (SOCKET) get_osfhandle (plainfd);
-                               
+#else /* ifdef WINDOWS_PORT */
                                // cmd was already set to 0, including ancilary data simulation
                                if (1 /*is_sock(wsock)*/) {
                                        // Send a socket
-                                       int pid = tlspool_getpid();
+                                       ULONG pid;
+                                       GetNamedPipeServerProcessId(poolfd, &pid);
                                        cmd.pio_ancil_type = ANCIL_TYPE_SOCKET;
-                                       printf("pid = %d, plainfd = %d\n", pid, plainfd);
-                                       if (cygwin_socket_dup_protocol_info (plainfd, pid, &cmd.pio_ancil_data.pioa_socket) == -1) {
-                                               printf("cygwin_socket_dup_protocol_info error\n");
+                                       // printf("DEBUG: pid = %d, plainfd = %d\n", pid, plainfd);
+                                       if (socket_dup_protocol_info(plainfd, pid, &cmd.pio_ancil_data.pioa_socket) == -1) {
+                                               // printf("DEBUG: cygwin_socket_dup_protocol_info error\n");
                                                // Let SIGPIPE be reported as EPIPE
-                                               close (plainfd);
+                                               closesocket(plainfd);
                                                registry_update (&entry_reqid, NULL);
-                                               // errno inherited from sendmsg()
+                                               // errno inherited from socket_dup_protocol_info()
                                                return -1;
                                        }
                                        //... (..., &cmd.pio_ancil_data.pioa_socket, ...);
@@ -698,10 +974,22 @@ int tlspool_starttls (int cryptfd, starttls_t *tlsdata,
                                        cmd.pio_ancil_type = ANCIL_TYPE_FILEHANDLE;
                                        //... (..., &cmd.pio_ancil_data.pioa_filehandle, ...);
                                }
-#endif /* __CYGWIN__ */
+#endif /* WINDOWS_PORT */
                        }
+
                        /* Now supply plainfd in the callback response */
                        sentfd = plainfd;
+#ifdef WINDOWS_PORT
+                       if (np_send_command (&cmd) == -1) {
+                               if (sentfd >= 0) {
+                                       closesocket(sentfd);
+                                       sentfd = -1;
+                               }
+                               registry_update (&entry_reqid, NULL);
+                               // errno inherited from np_send_command()
+                               return -1;
+                       }
+#else
                        if (sendmsg (poolfd, &mh, MSG_NOSIGNAL) == -1) {
                                // Let SIGPIPE be reported as EPIPE
                                if (sentfd >= 0) {
@@ -712,6 +1000,7 @@ int tlspool_starttls (int cryptfd, starttls_t *tlsdata,
                                // errno inherited from sendmsg()
                                return -1;
                        }
+#endif /* WINDOWS_PORT */
                        break;  // Loop around and try again
                case PIOC_STARTTLS_V2:
                        /* Wheee!!! we're done */
@@ -728,7 +1017,7 @@ int tlspool_starttls (int cryptfd, starttls_t *tlsdata,
        /* Close the now-duplicated or now-erradicated plaintext fd */
 
        memcpy (tlsdata, &cmd.pio_data.pioc_starttls, sizeof (struct pioc_starttls));
-printf ("DEBUG: Returning control key %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", tlsdata->ctlkey [0], tlsdata->ctlkey [1], tlsdata->ctlkey [2], tlsdata->ctlkey [3], tlsdata->ctlkey [4], tlsdata->ctlkey [5], tlsdata->ctlkey [6], tlsdata->ctlkey [7], tlsdata->ctlkey [8], tlsdata->ctlkey [9], tlsdata->ctlkey [10], tlsdata->ctlkey [11], tlsdata->ctlkey [12], tlsdata->ctlkey [13], tlsdata->ctlkey [14], tlsdata->ctlkey [15]);
+// printf ("DEBUG: Returning control key %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", tlsdata->ctlkey [0], tlsdata->ctlkey [1], tlsdata->ctlkey [2], tlsdata->ctlkey [3], tlsdata->ctlkey [4], tlsdata->ctlkey [5], tlsdata->ctlkey [6], tlsdata->ctlkey [7], tlsdata->ctlkey [8], tlsdata->ctlkey [9], tlsdata->ctlkey [10], tlsdata->ctlkey [11], tlsdata->ctlkey [12], tlsdata->ctlkey [13], tlsdata->ctlkey [14], tlsdata->ctlkey [15]);
        registry_update (&entry_reqid, NULL);
        return 0;
 }
@@ -748,8 +1037,8 @@ int _tlspool_control_command (int cmdcode, uint8_t *ctlkey) {
        int retval;
 
        /* Prepare command structure */
-       poolfd = tlspool_socket (NULL);
-       if (poolfd == -1) {
+       poolfd = tlspool_open_poolhandle (NULL);
+       if (poolfd == INVALID_POOL_HANDLE) {
                errno = ENODEV;
                return -1;
        }
@@ -761,13 +1050,20 @@ int _tlspool_control_command (int cmdcode, uint8_t *ctlkey) {
                errno = EBUSY;
                return -1;
        }
-       bzero (&cmd, sizeof (cmd));     /* Do not leak old stack info */
+       memset (&cmd, 0, sizeof (cmd)); /* Do not leak old stack info */
        cmd.pio_reqid = entry_reqid;
        cmd.pio_cbid = 0;
        cmd.pio_cmd = cmdcode;
        memcpy (&cmd.pio_data.pioc_control.ctlkey, ctlkey, TLSPOOL_CTLKEYLEN);
 // printf ("DEBUG: Using control key %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", cmd.pio_data.pioc_control.ctlkey [0], cmd.pio_data.pioc_control.ctlkey [1], cmd.pio_data.pioc_control.ctlkey [2], cmd.pio_data.pioc_control.ctlkey [3], cmd.pio_data.pioc_control.ctlkey [4], cmd.pio_data.pioc_control.ctlkey [5], cmd.pio_data.pioc_control.ctlkey [6], cmd.pio_data.pioc_control.ctlkey [7], cmd.pio_data.pioc_control.ctlkey [8], cmd.pio_data.pioc_control.ctlkey [9], cmd.pio_data.pioc_control.ctlkey [10], cmd.pio_data.pioc_control.ctlkey [11], cmd.pio_data.pioc_control.ctlkey [12], cmd.pio_data.pioc_control.ctlkey [13], cmd.pio_data.pioc_control.ctlkey [14], cmd.pio_data.pioc_control.ctlkey [15]);
 
+#ifdef WINDOWS_PORT
+       if (np_send_command (&cmd) == -1) {
+               registry_update (&entry_reqid, NULL);
+               // errno inherited from np_send_command ()
+               return -1;
+       }
+#else
        /* Send the request */
        if (send (poolfd, &cmd, sizeof (cmd), MSG_NOSIGNAL) == -1) {
                // Let SIGPIPE be reported as EPIPE
@@ -775,7 +1071,7 @@ int _tlspool_control_command (int cmdcode, uint8_t *ctlkey) {
                // errno inherited from send()
                return -1;
        }
-
+#endif
        /* Receive the response */
        registry_recvmsg (&regent);
        switch (cmd.pio_cmd) {
@@ -805,7 +1101,7 @@ int _tlspool_control_command (int cmdcode, uint8_t *ctlkey) {
  * purposes, such as finding the same session key for both sides deriving from
  * prior key negotiation; the protection of a ctlkey for such applications is
  * important.
- * 
+ *
  * The inputs to this function must adhere to the following restrictions:
  *  - label must not be a NULL pointer, but opt_ctxvalue may be set to NULL
  *    to bypass the use of a context value.  Note that passing an empty string
@@ -845,11 +1141,8 @@ int tlspool_prng (char *label, char *opt_ctxvalue,
        pthread_mutex_t recvwait = PTHREAD_MUTEX_INITIALIZER;
        struct registry_entry regent = { .sig = &recvwait, .buf = &cmd };
        int entry_reqid = -1;
-       int poolfd = -1;
-       struct iovec iov;
-       struct msghdr mh = { 0 };
-
-       bzero (prng_buf, prng_len);
+       pool_handle_t poolfd = INVALID_POOL_HANDLE;
+       memset (prng_buf, 0, prng_len);
 
        /* Sanity checks */
        if ((prng_len > TLSPOOL_PRNGBUFLEN) ||
@@ -860,11 +1153,11 @@ int tlspool_prng (char *label, char *opt_ctxvalue,
                errno = EINVAL;
                return -1;
        }
-       
+
 
        /* Prepare command structure */
-       poolfd = tlspool_socket (NULL);
-       if (poolfd == -1) {
+       poolfd = tlspool_open_poolhandle (NULL);
+       if (poolfd == INVALID_POOL_HANDLE) {
                errno = ENODEV;
                return -1;
        }
@@ -876,7 +1169,7 @@ int tlspool_prng (char *label, char *opt_ctxvalue,
                errno = EBUSY;
                return -1;
        }
-       bzero (&cmd, sizeof (cmd));     /* Do not leak old stack info */
+       memset (&cmd, 0, sizeof (cmd)); /* Do not leak old stack info */
        cmd.pio_reqid = entry_reqid;
        cmd.pio_cbid = 0;
        cmd.pio_cmd = PIOC_PRNG_V2;
@@ -891,18 +1184,21 @@ int tlspool_prng (char *label, char *opt_ctxvalue,
                cmd.pio_data.pioc_prng.in2_len = -1;
        }
 
+#ifdef WINDOWS_PORT
+if (np_send_command (&cmd) == -1) {
+       // errno inherited from np_send_command ()
+       registry_update (&entry_reqid, NULL);
+       return -1;
+}
+#else
        /* Send the request */
-       iov.iov_base = &cmd;
-       iov.iov_len = sizeof (cmd);
-       mh.msg_iov = &iov;
-       mh.msg_iovlen = 1;
-       if (sendmsg (poolfd, &mh, MSG_NOSIGNAL) == -1) {
+       if (send (poolfd, &cmd, sizeof (cmd), MSG_NOSIGNAL) == -1) {
                // Let SIGPIPE be reported as EPIPE
                registry_update (&entry_reqid, NULL);
                // errno inherited from sendmsg()
                return -1;
        }
-
+#endif
        /* Await response and process it */
        registry_recvmsg (&regent);
        registry_update (&entry_reqid, NULL);
@@ -923,3 +1219,106 @@ int tlspool_prng (char *label, char *opt_ctxvalue,
        }
 }
 
+
+/* Fetch a configuration variable value from the configuration file.  This is not
+ * an efficient procedure, at best suited for startup of tools or daemons; it
+ * will iterate over the config file until it reads the desired value.  The value
+ * returned is allocated and should be freed by the caller using free().
+ *
+ * When cfgfile is NULL, the environment variable TLSPOOL_CONFIGFILE is
+ * tried first, followed by the default setting from the macro 
+ * TLSPOOL_DEFAULT_CONFIG_PATH as defined in <tlspool/starttls.h>.
+ *
+ * The value returned is NULL when the variable is not found, including when this
+ * is due to errors such as not being able to open the file.
+ */
+char *tlspool_configvar (char *cfgfile, char *varname) {
+       FILE *cf;
+       char line [514];
+       int linelen;
+       int eof = 0;
+       char *here;
+       struct cfgopt *curopt;
+       int found;
+       char *retval = NULL;
+
+       if (cfgfile == NULL) {
+               cfgfile = getenv ("TLSPOOL_CFGFILE");
+       }
+       if (cfgfile == NULL) {
+               cfgfile = TLSPOOL_DEFAULT_CONFIG_PATH;
+       }
+
+       assert (cfgfile != NULL);
+       assert (varname != NULL);
+
+       cf = fopen (cfgfile, "r");
+       if (cf == NULL) {
+               perror ("Failed to open configuration file");
+               goto cleanup;
+       }
+
+       while (!eof) {
+               if (!fgets (line, sizeof (line)-1, cf)) {
+                       if (feof (cf)) {
+                               eof = 1;
+                               continue;
+                       } else {
+                               perror ("Error while reading configuration file");
+                               exit (1);
+                       }
+               }
+               linelen = strlen (line);
+               if (linelen == 0) {
+                       eof = 1;
+                       continue;
+               }
+               if (line [linelen-1] == (char) EOF) {
+                       linelen--;
+                       eof = 1;
+               }
+               if (line [linelen-1] != '\n') {
+                       fprintf (stderr, "Configuration line too long\n");
+                       goto cleanup;
+               }
+               line [--linelen] = 0;
+               if (linelen == 0) {
+                       continue;
+               }
+               if (line [0] == '#') {
+                       continue;
+               }
+               here = line;
+               while ((*here) && isspace (*here)) {
+                       here++;
+               }
+               if (!*here) {
+                       continue;
+               }
+               if (here != line) {
+                       fprintf (stderr, "Configuration line starts with whitespace:\n%s\n", line);
+                       goto cleanup;
+               }
+               while ((*here) && (*here != ' ')) {
+                       here++;
+               }
+               if (!*here) {
+                       fprintf (stderr, "Configuration line misses space after keyword:\n%s\n", line);
+                       goto cleanup;
+               }
+               *here++ = '\0';
+               if (strcmp (varname, line) == 0) {
+                       // Success!  We set the return value and end the loop
+                       retval = here;
+                       goto cleanup;
+               }
+       }
+
+cleanup:
+       if (cf != NULL) {
+               fclose (cf);
+               cf = NULL;
+       }
+       return retval;
+}
+