quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

sockfilt.c (42093B)


      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
      9  *
     10  * This software is licensed as described in the file COPYING, which
     11  * you should have received as part of this distribution. The terms
     12  * are also available at https://curl.se/docs/copyright.html.
     13  *
     14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
     15  * copies of the Software, and permit persons to whom the Software is
     16  * furnished to do so, under the terms of the COPYING file.
     17  *
     18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
     19  * KIND, either express or implied.
     20  *
     21  * SPDX-License-Identifier: curl
     22  *
     23  ***************************************************************************/
     24 #include "first.h"
     25 
     26 /* Purpose
     27  *
     28  * 1. Accept a TCP connection on a custom port (IPv4 or IPv6), or connect
     29  *    to a given (localhost) port.
     30  *
     31  * 2. Get commands on STDIN. Pass data on to the TCP stream.
     32  *    Get data from TCP stream and pass on to STDOUT.
     33  *
     34  * This program is made to perform all the socket/stream/connection stuff for
     35  * the test suite's (perl) FTP server. Previously the perl code did all of
     36  * this by its own, but I decided to let this program do the socket layer
     37  * because of several things:
     38  *
     39  * o We want the perl code to work with rather old perl installations, thus
     40  *   we cannot use recent perl modules or features.
     41  *
     42  * o We want IPv6 support for systems that provide it, and doing optional IPv6
     43  *   support in perl seems if not impossible so at least awkward.
     44  *
     45  * o We want FTP-SSL support, which means that a connection that starts with
     46  *   plain sockets needs to be able to "go SSL" in the midst. This would also
     47  *   require some nasty perl stuff I'd rather avoid.
     48  *
     49  * (Source originally based on sws.c)
     50  */
     51 
     52 /*
     53  * Signal handling notes for sockfilt
     54  * ----------------------------------
     55  *
     56  * This program is a single-threaded process.
     57  *
     58  * This program is intended to be highly portable and as such it must be kept
     59  * as simple as possible, due to this the only signal handling mechanisms used
     60  * will be those of ANSI C, and used only in the most basic form which is good
     61  * enough for the purpose of this program.
     62  *
     63  * For the above reason and the specific needs of this program signals SIGHUP,
     64  * SIGPIPE and SIGALRM will be simply ignored on systems where this can be
     65  * done.  If possible, signals SIGINT and SIGTERM will be handled by this
     66  * program as an indication to cleanup and finish execution as soon as
     67  * possible.  This will be achieved with a single signal handler
     68  * 'exit_signal_handler' for both signals.
     69  *
     70  * The 'exit_signal_handler' upon the first SIGINT or SIGTERM received signal
     71  * will just set to one the global var 'got_exit_signal' storing in global var
     72  * 'exit_signal' the signal that triggered this change.
     73  *
     74  * Nothing fancy that could introduce problems is used, the program at certain
     75  * points in its normal flow checks if var 'got_exit_signal' is set and in
     76  * case this is true it just makes its way out of loops and functions in
     77  * structured and well behaved manner to achieve proper program cleanup and
     78  * termination.
     79  *
     80  * Even with the above mechanism implemented it is worthwhile to note that
     81  * other signals might still be received, or that there might be systems on
     82  * which it is not possible to trap and ignore some of the above signals.
     83  * This implies that for increased portability and reliability the program
     84  * must be coded as if no signal was being ignored or handled at all.  Enjoy
     85  * it!
     86  */
     87 
     88 /* buffer is this excessively large only to be able to support things like
     89    test 1003 which tests exceedingly large server response lines */
     90 #define BUFFER_SIZE 17010
     91 
     92 static bool verbose = FALSE;
     93 static bool s_bind_only = FALSE;
     94 static unsigned short server_connectport = 0; /* if non-zero,
     95                                                  we activate this mode */
     96 
     97 enum sockmode {
     98   PASSIVE_LISTEN,    /* as a server waiting for connections */
     99   PASSIVE_CONNECT,   /* as a server, connected to a client */
    100   ACTIVE,            /* as a client, connected to a server */
    101   ACTIVE_DISCONNECT  /* as a client, disconnected from server */
    102 };
    103 
    104 #if defined(_WIN32) && !defined(CURL_WINDOWS_UWP) && !defined(UNDER_CE)
    105 /*
    106  * read-wrapper to support reading from stdin on Windows.
    107  */
    108 static ssize_t read_wincon(int fd, void *buf, size_t count)
    109 {
    110   HANDLE handle = NULL;
    111   DWORD mode, rcount = 0;
    112   BOOL success;
    113 
    114   if(fd == fileno(stdin)) {
    115     handle = GetStdHandle(STD_INPUT_HANDLE);
    116   }
    117   else {
    118     return read(fd, buf, count);
    119   }
    120 
    121   if(GetConsoleMode(handle, &mode)) {
    122     success = ReadConsole(handle, buf, curlx_uztoul(count), &rcount, NULL);
    123   }
    124   else {
    125     success = ReadFile(handle, buf, curlx_uztoul(count), &rcount, NULL);
    126   }
    127   if(success) {
    128     return rcount;
    129   }
    130 
    131   CURL_SETERRNO((int)GetLastError());
    132   return -1;
    133 }
    134 
    135 /*
    136  * write-wrapper to support writing to stdout and stderr on Windows.
    137  */
    138 static ssize_t write_wincon(int fd, const void *buf, size_t count)
    139 {
    140   HANDLE handle = NULL;
    141   DWORD mode, wcount = 0;
    142   BOOL success;
    143 
    144   if(fd == fileno(stdout)) {
    145     handle = GetStdHandle(STD_OUTPUT_HANDLE);
    146   }
    147   else if(fd == fileno(stderr)) {
    148     handle = GetStdHandle(STD_ERROR_HANDLE);
    149   }
    150   else {
    151     return write(fd, buf, count);
    152   }
    153 
    154   if(GetConsoleMode(handle, &mode)) {
    155     success = WriteConsole(handle, buf, curlx_uztoul(count), &wcount, NULL);
    156   }
    157   else {
    158     success = WriteFile(handle, buf, curlx_uztoul(count), &wcount, NULL);
    159   }
    160   if(success) {
    161     return wcount;
    162   }
    163 
    164   CURL_SETERRNO((int)GetLastError());
    165   return -1;
    166 }
    167 #define SOCKFILT_read  read_wincon
    168 #define SOCKFILT_write write_wincon
    169 #else
    170 #define SOCKFILT_read  read
    171 #define SOCKFILT_write write
    172 #endif
    173 
    174 #ifndef UNDER_CE
    175 
    176 /* On Windows, we sometimes get this for a broken pipe, seemingly
    177  * when the client just closed stdin? */
    178 #define CURL_WIN32_EPIPE      109
    179 
    180 /*
    181  * fullread is a wrapper around the read() function. This will repeat the call
    182  * to read() until it actually has read the complete number of bytes indicated
    183  * in nbytes or it fails with a condition that cannot be handled with a simple
    184  * retry of the read call.
    185  */
    186 
    187 static ssize_t fullread(int filedes, void *buffer, size_t nbytes)
    188 {
    189   int error;
    190   ssize_t nread = 0;
    191 
    192   do {
    193     ssize_t rc = SOCKFILT_read(filedes,
    194                                (unsigned char *)buffer + nread,
    195                                nbytes - nread);
    196 
    197     if(got_exit_signal) {
    198       logmsg("signalled to die");
    199       return -1;
    200     }
    201 
    202     if(rc < 0) {
    203       error = errno;
    204       /* !checksrc! disable ERRNOVAR 1 */
    205       if((error == EINTR) || (error == EAGAIN))
    206         continue;
    207       if(error == CURL_WIN32_EPIPE) {
    208         logmsg("got Windows ERROR_BROKEN_PIPE on fd=%d, treating as close",
    209                filedes);
    210         return 0;
    211       }
    212       logmsg("reading from file descriptor: %d,", filedes);
    213       logmsg("unrecoverable read() failure (%d) %s",
    214              error, strerror(error));
    215       return -1;
    216     }
    217 
    218     if(rc == 0) {
    219       logmsg("got 0 reading from stdin");
    220       return 0;
    221     }
    222 
    223     nread += rc;
    224 
    225   } while((size_t)nread < nbytes);
    226 
    227   if(verbose)
    228     logmsg("read %zd bytes", nread);
    229 
    230   return nread;
    231 }
    232 
    233 /*
    234  * fullwrite is a wrapper around the write() function. This will repeat the
    235  * call to write() until it actually has written the complete number of bytes
    236  * indicated in nbytes or it fails with a condition that cannot be handled
    237  * with a simple retry of the write call.
    238  */
    239 
    240 static ssize_t fullwrite(int filedes, const void *buffer, size_t nbytes)
    241 {
    242   int error;
    243   ssize_t nwrite = 0;
    244 
    245   do {
    246     ssize_t wc = SOCKFILT_write(filedes,
    247                                 (const unsigned char *)buffer + nwrite,
    248                                 nbytes - nwrite);
    249 
    250     if(got_exit_signal) {
    251       logmsg("signalled to die");
    252       return -1;
    253     }
    254 
    255     if(wc < 0) {
    256       error = errno;
    257       /* !checksrc! disable ERRNOVAR 1 */
    258       if((error == EINTR) || (error == EAGAIN))
    259         continue;
    260       logmsg("writing to file descriptor: %d,", filedes);
    261       logmsg("unrecoverable write() failure (%d) %s",
    262              error, strerror(error));
    263       return -1;
    264     }
    265 
    266     if(wc == 0) {
    267       logmsg("put 0 writing to stdout");
    268       return 0;
    269     }
    270 
    271     nwrite += wc;
    272 
    273   } while((size_t)nwrite < nbytes);
    274 
    275   if(verbose)
    276     logmsg("wrote %zd bytes", nwrite);
    277 
    278   return nwrite;
    279 }
    280 
    281 /*
    282  * read_stdin tries to read from stdin nbytes into the given buffer. This is a
    283  * blocking function that will only return TRUE when nbytes have actually been
    284  * read or FALSE when an unrecoverable error has been detected. Failure of this
    285  * function is an indication that the sockfilt process should terminate.
    286  */
    287 
    288 static bool read_stdin(void *buffer, size_t nbytes)
    289 {
    290   ssize_t nread = fullread(fileno(stdin), buffer, nbytes);
    291   if(nread != (ssize_t)nbytes) {
    292     logmsg("exiting...");
    293     return FALSE;
    294   }
    295   return TRUE;
    296 }
    297 #endif
    298 
    299 /*
    300  * write_stdout tries to write to stdio nbytes from the given buffer. This is a
    301  * blocking function that will only return TRUE when nbytes have actually been
    302  * written or FALSE when an unrecoverable error has been detected. Failure of
    303  * this function is an indication that the sockfilt process should terminate.
    304  */
    305 
    306 static bool write_stdout(const void *buffer, size_t nbytes)
    307 {
    308   ssize_t nwrite;
    309 #ifdef UNDER_CE
    310   puts(buffer);
    311   nwrite = nbytes;
    312 #else
    313   nwrite = fullwrite(fileno(stdout), buffer, nbytes);
    314 #endif
    315   if(nwrite != (ssize_t)nbytes) {
    316     logmsg("exiting...");
    317     return FALSE;
    318   }
    319   return TRUE;
    320 }
    321 
    322 #ifndef UNDER_CE
    323 static void lograw(unsigned char *buffer, ssize_t len)
    324 {
    325   char data[120];
    326   ssize_t i;
    327   unsigned char *ptr = buffer;
    328   char *optr = data;
    329   ssize_t width = 0;
    330   int left = sizeof(data);
    331 
    332   for(i = 0; i < len; i++) {
    333     switch(ptr[i]) {
    334     case '\n':
    335       snprintf(optr, left, "\\n");
    336       width += 2;
    337       optr += 2;
    338       left -= 2;
    339       break;
    340     case '\r':
    341       snprintf(optr, left, "\\r");
    342       width += 2;
    343       optr += 2;
    344       left -= 2;
    345       break;
    346     default:
    347       snprintf(optr, left, "%c", (ISGRAPH(ptr[i]) ||
    348                                   ptr[i] == 0x20) ? ptr[i] : '.');
    349       width++;
    350       optr++;
    351       left--;
    352       break;
    353     }
    354 
    355     if(width > 60) {
    356       logmsg("'%s'", data);
    357       width = 0;
    358       optr = data;
    359       left = sizeof(data);
    360     }
    361   }
    362   if(width)
    363     logmsg("'%s'", data);
    364 }
    365 
    366 /*
    367  * handle the DATA command
    368  * maxlen is the available space in buffer (input)
    369  * *buffer_len is the amount of data in the buffer (output)
    370  */
    371 static bool read_data_block(unsigned char *buffer, ssize_t maxlen,
    372                             ssize_t *buffer_len)
    373 {
    374   if(!read_stdin(buffer, 5))
    375     return FALSE;
    376 
    377   buffer[5] = '\0';
    378 
    379   *buffer_len = (ssize_t)strtol((char *)buffer, NULL, 16);
    380   if(*buffer_len > maxlen) {
    381     logmsg("Buffer size (%zd bytes) too small for data size error "
    382            "(%zd bytes)", maxlen, *buffer_len);
    383     return FALSE;
    384   }
    385   logmsg("> %zd bytes data, server => client", *buffer_len);
    386 
    387   if(!read_stdin(buffer, *buffer_len))
    388     return FALSE;
    389 
    390   lograw(buffer, *buffer_len);
    391 
    392   return TRUE;
    393 }
    394 #endif
    395 
    396 
    397 #if defined(USE_WINSOCK) && !defined(CURL_WINDOWS_UWP) && !defined(UNDER_CE)
    398 /*
    399  * Winsock select() does not support standard file descriptors,
    400  * it can only check SOCKETs. The following function is an attempt
    401  * to re-create a select() function with support for other handle types.
    402  *
    403  * select() function with support for Winsock2 sockets and all
    404  * other handle types supported by WaitForMultipleObjectsEx() as
    405  * well as disk files, anonymous and names pipes, and character input.
    406  *
    407  * https://msdn.microsoft.com/en-us/library/windows/desktop/ms687028.aspx
    408  * https://msdn.microsoft.com/en-us/library/windows/desktop/ms741572.aspx
    409  */
    410 struct select_ws_wait_data {
    411   HANDLE handle; /* actual handle to wait for during select */
    412   HANDLE signal; /* internal event to signal handle trigger */
    413   HANDLE abort;  /* internal event to abort waiting threads */
    414 };
    415 #include <process.h>
    416 static unsigned int WINAPI select_ws_wait_thread(void *lpParameter)
    417 {
    418   struct select_ws_wait_data *data;
    419   HANDLE signal, handle, handles[2];
    420   INPUT_RECORD inputrecord;
    421   LARGE_INTEGER size, pos;
    422   DWORD type, length, ret;
    423 
    424   /* retrieve handles from internal structure */
    425   data = (struct select_ws_wait_data *) lpParameter;
    426   if(data) {
    427     handle = data->handle;
    428     handles[0] = data->abort;
    429     handles[1] = handle;
    430     signal = data->signal;
    431     free(data);
    432   }
    433   else
    434     return (DWORD)-1;
    435 
    436   /* retrieve the type of file to wait on */
    437   type = GetFileType(handle);
    438   switch(type) {
    439     case FILE_TYPE_DISK:
    440        /* The handle represents a file on disk, this means:
    441         * - WaitForMultipleObjectsEx will always be signalled for it.
    442         * - comparison of current position in file and total size of
    443         *   the file can be used to check if we reached the end yet.
    444         *
    445         * Approach: Loop till either the internal event is signalled
    446         *           or if the end of the file has already been reached.
    447         */
    448       while(WaitForMultipleObjectsEx(1, handles, FALSE, 0, FALSE)
    449             == WAIT_TIMEOUT) {
    450         /* get total size of file */
    451         length = 0;
    452         size.QuadPart = 0;
    453         size.LowPart = GetFileSize(handle, &length);
    454         if((size.LowPart != INVALID_FILE_SIZE) ||
    455             (GetLastError() == NO_ERROR)) {
    456           size.HighPart = (LONG)length;
    457           /* get the current position within the file */
    458           pos.QuadPart = 0;
    459           pos.LowPart = SetFilePointer(handle, 0, &pos.HighPart, FILE_CURRENT);
    460           if((pos.LowPart != INVALID_SET_FILE_POINTER) ||
    461               (GetLastError() == NO_ERROR)) {
    462             /* compare position with size, abort if not equal */
    463             if(size.QuadPart == pos.QuadPart) {
    464               /* sleep and continue waiting */
    465               SleepEx(0, FALSE);
    466               continue;
    467             }
    468           }
    469         }
    470         /* there is some data available, stop waiting */
    471         logmsg("[select_ws_wait_thread] data available, DISK: %p", handle);
    472         SetEvent(signal);
    473       }
    474       break;
    475 
    476     case FILE_TYPE_CHAR:
    477        /* The handle represents a character input, this means:
    478         * - WaitForMultipleObjectsEx will be signalled on any kind of input,
    479         *   including mouse and window size events we do not care about.
    480         *
    481         * Approach: Loop till either the internal event is signalled
    482         *           or we get signalled for an actual key-event.
    483         */
    484       while(WaitForMultipleObjectsEx(2, handles, FALSE, INFINITE, FALSE)
    485             == WAIT_OBJECT_0 + 1) {
    486         /* check if this is an actual console handle */
    487         if(GetConsoleMode(handle, &ret)) {
    488           /* retrieve an event from the console buffer */
    489           length = 0;
    490           if(PeekConsoleInput(handle, &inputrecord, 1, &length)) {
    491             /* check if the event is not an actual key-event */
    492             if(length == 1 && inputrecord.EventType != KEY_EVENT) {
    493               /* purge the non-key-event and continue waiting */
    494               ReadConsoleInput(handle, &inputrecord, 1, &length);
    495               continue;
    496             }
    497           }
    498         }
    499         /* there is some data available, stop waiting */
    500         logmsg("[select_ws_wait_thread] data available, CHAR: %p", handle);
    501         SetEvent(signal);
    502       }
    503       break;
    504 
    505     case FILE_TYPE_PIPE:
    506        /* The handle represents an anonymous or named pipe, this means:
    507         * - WaitForMultipleObjectsEx will always be signalled for it.
    508         * - peek into the pipe and retrieve the amount of data available.
    509         *
    510         * Approach: Loop till either the internal event is signalled
    511         *           or there is data in the pipe available for reading.
    512         */
    513       while(WaitForMultipleObjectsEx(1, handles, FALSE, 0, FALSE)
    514             == WAIT_TIMEOUT) {
    515         /* peek into the pipe and retrieve the amount of data available */
    516         length = 0;
    517         if(PeekNamedPipe(handle, NULL, 0, NULL, &length, NULL)) {
    518           /* if there is no data available, sleep and continue waiting */
    519           if(length == 0) {
    520             SleepEx(0, FALSE);
    521             continue;
    522           }
    523           else {
    524             logmsg("[select_ws_wait_thread] PeekNamedPipe len: %lu", length);
    525           }
    526         }
    527         else {
    528           /* if the pipe has NOT been closed, sleep and continue waiting */
    529           ret = GetLastError();
    530           if(ret != ERROR_BROKEN_PIPE) {
    531             logmsg("[select_ws_wait_thread] PeekNamedPipe error (%lu)", ret);
    532             SleepEx(0, FALSE);
    533             continue;
    534           }
    535           else {
    536             logmsg("[select_ws_wait_thread] pipe closed, PIPE: %p", handle);
    537           }
    538         }
    539         /* there is some data available, stop waiting */
    540         logmsg("[select_ws_wait_thread] data available, PIPE: %p", handle);
    541         SetEvent(signal);
    542       }
    543       break;
    544 
    545     default:
    546       /* The handle has an unknown type, try to wait on it */
    547       if(WaitForMultipleObjectsEx(2, handles, FALSE, INFINITE, FALSE)
    548          == WAIT_OBJECT_0 + 1) {
    549         logmsg("[select_ws_wait_thread] data available, HANDLE: %p", handle);
    550         SetEvent(signal);
    551       }
    552       break;
    553   }
    554 
    555   return 0;
    556 }
    557 
    558 static HANDLE select_ws_wait(HANDLE handle, HANDLE signal, HANDLE abort)
    559 {
    560   typedef uintptr_t curl_win_thread_handle_t;
    561   struct select_ws_wait_data *data;
    562   curl_win_thread_handle_t thread;
    563 
    564   /* allocate internal waiting data structure */
    565   data = malloc(sizeof(struct select_ws_wait_data));
    566   if(data) {
    567     data->handle = handle;
    568     data->signal = signal;
    569     data->abort = abort;
    570 
    571     /* launch waiting thread */
    572     thread = _beginthreadex(NULL, 0, &select_ws_wait_thread, data, 0, NULL);
    573 
    574     /* free data if thread failed to launch */
    575     if(!thread) {
    576       free(data);
    577     }
    578     return (HANDLE)thread;
    579   }
    580   return NULL;
    581 }
    582 
    583 struct select_ws_data {
    584   int fd;                /* provided file descriptor  (indexed by nfd) */
    585   long wsastate;         /* internal pre-select state (indexed by nfd) */
    586   curl_socket_t wsasock; /* internal socket handle    (indexed by nws) */
    587   WSAEVENT wsaevent;     /* internal select event     (indexed by nws) */
    588   HANDLE signal;         /* internal thread signal    (indexed by nth) */
    589   HANDLE thread;         /* internal thread handle    (indexed by nth) */
    590 };
    591 
    592 static int select_ws(int nfds, fd_set *readfds, fd_set *writefds,
    593                      fd_set *exceptfds, struct timeval *tv)
    594 {
    595   DWORD timeout_ms, wait, nfd, nth, nws, i;
    596   HANDLE abort, signal, handle, *handles;
    597   fd_set readsock, writesock, exceptsock;
    598   struct select_ws_data *data;
    599   WSANETWORKEVENTS wsaevents;
    600   curl_socket_t wsasock;
    601   int error, ret, fd;
    602   WSAEVENT wsaevent;
    603 
    604   /* check if the input value is valid */
    605   if(nfds < 0) {
    606     SET_SOCKERRNO(SOCKEINVAL);
    607     return -1;
    608   }
    609 
    610   /* convert struct timeval to milliseconds */
    611   if(tv) {
    612     timeout_ms = (DWORD)curlx_tvtoms(tv);
    613   }
    614   else {
    615     timeout_ms = INFINITE;
    616   }
    617 
    618   /* check if we got descriptors, sleep in case we got none */
    619   if(!nfds) {
    620     SleepEx(timeout_ms, FALSE);
    621     return 0;
    622   }
    623 
    624   /* create internal event to abort waiting threads */
    625   abort = CreateEvent(NULL, TRUE, FALSE, NULL);
    626   if(!abort) {
    627     SET_SOCKERRNO(SOCKENOMEM);
    628     return -1;
    629   }
    630 
    631   /* allocate internal array for the internal data */
    632   data = calloc(nfds, sizeof(struct select_ws_data));
    633   if(!data) {
    634     CloseHandle(abort);
    635     SET_SOCKERRNO(SOCKENOMEM);
    636     return -1;
    637   }
    638 
    639   /* allocate internal array for the internal event handles */
    640   handles = calloc(nfds + 1, sizeof(HANDLE));
    641   if(!handles) {
    642     CloseHandle(abort);
    643     free(data);
    644     SET_SOCKERRNO(SOCKENOMEM);
    645     return -1;
    646   }
    647 
    648   /* loop over the handles in the input descriptor sets */
    649   nfd = 0; /* number of handled file descriptors */
    650   nth = 0; /* number of internal waiting threads */
    651   nws = 0; /* number of handled Winsock sockets */
    652   for(fd = 0; fd < nfds; fd++) {
    653     wsasock = (curl_socket_t)fd;
    654     wsaevents.lNetworkEvents = 0;
    655     handles[nfd] = 0;
    656 
    657     FD_ZERO(&readsock);
    658     FD_ZERO(&writesock);
    659     FD_ZERO(&exceptsock);
    660 
    661     if(FD_ISSET(wsasock, readfds)) {
    662       FD_SET(wsasock, &readsock);
    663       wsaevents.lNetworkEvents |= FD_READ|FD_ACCEPT|FD_CLOSE;
    664     }
    665 
    666     if(FD_ISSET(wsasock, writefds)) {
    667       FD_SET(wsasock, &writesock);
    668       wsaevents.lNetworkEvents |= FD_WRITE|FD_CONNECT|FD_CLOSE;
    669     }
    670 
    671     if(FD_ISSET(wsasock, exceptfds)) {
    672       FD_SET(wsasock, &exceptsock);
    673       wsaevents.lNetworkEvents |= FD_OOB;
    674     }
    675 
    676     /* only wait for events for which we actually care */
    677     if(wsaevents.lNetworkEvents) {
    678       data[nfd].fd = fd;
    679       if(fd == fileno(stdin)) {
    680         signal = CreateEvent(NULL, TRUE, FALSE, NULL);
    681         if(signal) {
    682           handle = GetStdHandle(STD_INPUT_HANDLE);
    683           handle = select_ws_wait(handle, signal, abort);
    684           if(handle) {
    685             handles[nfd] = signal;
    686             data[nth].signal = signal;
    687             data[nth].thread = handle;
    688             nfd++;
    689             nth++;
    690           }
    691           else {
    692             CloseHandle(signal);
    693           }
    694         }
    695       }
    696       else if(fd == fileno(stdout)) {
    697         handles[nfd] = GetStdHandle(STD_OUTPUT_HANDLE);
    698         nfd++;
    699       }
    700       else if(fd == fileno(stderr)) {
    701         handles[nfd] = GetStdHandle(STD_ERROR_HANDLE);
    702         nfd++;
    703       }
    704       else {
    705         wsaevent = WSACreateEvent();
    706         if(wsaevent != WSA_INVALID_EVENT) {
    707           if(wsaevents.lNetworkEvents & FD_WRITE) {
    708             send(wsasock, NULL, 0, 0); /* reset FD_WRITE */
    709           }
    710           error = WSAEventSelect(wsasock, wsaevent, wsaevents.lNetworkEvents);
    711           if(error != SOCKET_ERROR) {
    712             handles[nfd] = (HANDLE)wsaevent;
    713             data[nws].wsasock = wsasock;
    714             data[nws].wsaevent = wsaevent;
    715             data[nfd].wsastate = 0;
    716             tv->tv_sec = 0;
    717             tv->tv_usec = 0;
    718             /* check if the socket is already ready */
    719             if(select(fd + 1, &readsock, &writesock, &exceptsock, tv) == 1) {
    720               logmsg("[select_ws] socket %d is ready", fd);
    721               WSASetEvent(wsaevent);
    722               if(FD_ISSET(wsasock, &readsock))
    723                 data[nfd].wsastate |= FD_READ;
    724               if(FD_ISSET(wsasock, &writesock))
    725                 data[nfd].wsastate |= FD_WRITE;
    726               if(FD_ISSET(wsasock, &exceptsock))
    727                 data[nfd].wsastate |= FD_OOB;
    728             }
    729             nfd++;
    730             nws++;
    731           }
    732           else {
    733             WSACloseEvent(wsaevent);
    734             signal = CreateEvent(NULL, TRUE, FALSE, NULL);
    735             if(signal) {
    736               handle = (HANDLE)wsasock;
    737               handle = select_ws_wait(handle, signal, abort);
    738               if(handle) {
    739                 handles[nfd] = signal;
    740                 data[nth].signal = signal;
    741                 data[nth].thread = handle;
    742                 nfd++;
    743                 nth++;
    744               }
    745               else {
    746                 CloseHandle(signal);
    747               }
    748             }
    749           }
    750         }
    751       }
    752     }
    753   }
    754 
    755   /* wait on the number of handles */
    756   wait = nfd;
    757 
    758   /* make sure we stop waiting on exit signal event */
    759   if(exit_event) {
    760     /* we allocated handles nfds + 1 for this */
    761     handles[nfd] = exit_event;
    762     wait += 1;
    763   }
    764 
    765   /* wait for one of the internal handles to trigger */
    766   wait = WaitForMultipleObjectsEx(wait, handles, FALSE, timeout_ms, FALSE);
    767 
    768   /* signal the abort event handle and join the other waiting threads */
    769   SetEvent(abort);
    770   for(i = 0; i < nth; i++) {
    771     WaitForSingleObjectEx(data[i].thread, INFINITE, FALSE);
    772     CloseHandle(data[i].thread);
    773   }
    774 
    775   /* loop over the internal handles returned in the descriptors */
    776   ret = 0; /* number of ready file descriptors */
    777   for(i = 0; i < nfd; i++) {
    778     fd = data[i].fd;
    779     handle = handles[i];
    780     wsasock = (curl_socket_t)fd;
    781 
    782     /* check if the current internal handle was triggered */
    783     if(wait != WAIT_FAILED && (wait - WAIT_OBJECT_0) <= i &&
    784        WaitForSingleObjectEx(handle, 0, FALSE) == WAIT_OBJECT_0) {
    785       /* first handle stdin, stdout and stderr */
    786       if(fd == fileno(stdin)) {
    787         /* stdin is never ready for write or exceptional */
    788         FD_CLR(wsasock, writefds);
    789         FD_CLR(wsasock, exceptfds);
    790       }
    791       else if(fd == fileno(stdout) || fd == fileno(stderr)) {
    792         /* stdout and stderr are never ready for read or exceptional */
    793         FD_CLR(wsasock, readfds);
    794         FD_CLR(wsasock, exceptfds);
    795       }
    796       else {
    797         /* try to handle the event with the Winsock2 functions */
    798         wsaevents.lNetworkEvents = 0;
    799         error = WSAEnumNetworkEvents(wsasock, handle, &wsaevents);
    800         if(error != SOCKET_ERROR) {
    801           /* merge result from pre-check using select */
    802           wsaevents.lNetworkEvents |= data[i].wsastate;
    803 
    804           /* remove from descriptor set if not ready for read/accept/close */
    805           if(!(wsaevents.lNetworkEvents & (FD_READ|FD_ACCEPT|FD_CLOSE)))
    806             FD_CLR(wsasock, readfds);
    807 
    808           /* remove from descriptor set if not ready for write/connect */
    809           if(!(wsaevents.lNetworkEvents & (FD_WRITE|FD_CONNECT|FD_CLOSE)))
    810             FD_CLR(wsasock, writefds);
    811 
    812           /* remove from descriptor set if not exceptional */
    813           if(!(wsaevents.lNetworkEvents & FD_OOB))
    814             FD_CLR(wsasock, exceptfds);
    815         }
    816       }
    817 
    818       /* check if the event has not been filtered using specific tests */
    819       if(FD_ISSET(wsasock, readfds) || FD_ISSET(wsasock, writefds) ||
    820          FD_ISSET(wsasock, exceptfds)) {
    821         ret++;
    822       }
    823     }
    824     else {
    825       /* remove from all descriptor sets since this handle did not trigger */
    826       FD_CLR(wsasock, readfds);
    827       FD_CLR(wsasock, writefds);
    828       FD_CLR(wsasock, exceptfds);
    829     }
    830   }
    831 
    832   for(fd = 0; fd < nfds; fd++) {
    833     if(FD_ISSET(fd, readfds))
    834       logmsg("[select_ws] %d is readable", fd);
    835     if(FD_ISSET(fd, writefds))
    836       logmsg("[select_ws] %d is writable", fd);
    837     if(FD_ISSET(fd, exceptfds))
    838       logmsg("[select_ws] %d is exceptional", fd);
    839   }
    840 
    841   for(i = 0; i < nws; i++) {
    842     WSAEventSelect(data[i].wsasock, NULL, 0);
    843     WSACloseEvent(data[i].wsaevent);
    844   }
    845 
    846   for(i = 0; i < nth; i++) {
    847     CloseHandle(data[i].signal);
    848   }
    849   CloseHandle(abort);
    850 
    851   free(handles);
    852   free(data);
    853 
    854   return ret;
    855 }
    856 #define SOCKFILT_select(a,b,c,d,e) select_ws(a,b,c,d,e)
    857 #else
    858 #define SOCKFILT_select(a,b,c,d,e) select(a,b,c,d,e)
    859 #endif  /* USE_WINSOCK */
    860 
    861 
    862 #ifndef UNDER_CE
    863 /* Perform the disconnect handshake with sockfilt
    864  * This involves waiting for the disconnect acknowledgment after the DISC
    865  * command, while throwing away anything else that might come in before
    866  * that.
    867  */
    868 static bool disc_handshake(void)
    869 {
    870   if(!write_stdout("DISC\n", 5))
    871     return FALSE;
    872 
    873   do {
    874       unsigned char buffer[BUFFER_SIZE];
    875       ssize_t buffer_len;
    876       if(!read_stdin(buffer, 5))
    877         return FALSE;
    878       logmsg("Received %c%c%c%c (on stdin)",
    879              buffer[0], buffer[1], buffer[2], buffer[3]);
    880 
    881       if(!memcmp("ACKD", buffer, 4)) {
    882         /* got the ack we were waiting for */
    883         break;
    884       }
    885       else if(!memcmp("DISC", buffer, 4)) {
    886         logmsg("Crikey! Client also wants to disconnect");
    887         if(!write_stdout("ACKD\n", 5))
    888           return FALSE;
    889       }
    890       else if(!memcmp("DATA", buffer, 4)) {
    891         /* We must read more data to stay in sync */
    892         logmsg("Throwing away data bytes");
    893         if(!read_data_block(buffer, sizeof(buffer), &buffer_len))
    894           return FALSE;
    895 
    896       }
    897       else if(!memcmp("QUIT", buffer, 4)) {
    898         /* just die */
    899         logmsg("quits");
    900         return FALSE;
    901       }
    902       else {
    903         logmsg("Unexpected message error; aborting");
    904         /*
    905          * The only other messages that could occur here are PING and PORT,
    906          * and both of them occur at the start of a test when nothing should be
    907          * trying to DISC. Therefore, we should not ever get here, but if we
    908          * do, it's probably due to some kind of unclean shutdown situation so
    909          * us shutting down is what we probably ought to be doing, anyway.
    910          */
    911         return FALSE;
    912       }
    913 
    914   } while(TRUE);
    915   return TRUE;
    916 }
    917 #endif
    918 
    919 /*
    920   sockfdp is a pointer to an established stream or CURL_SOCKET_BAD
    921 
    922   if sockfd is CURL_SOCKET_BAD, listendfd is a listening socket we must
    923   accept()
    924 */
    925 static bool juggle(curl_socket_t *sockfdp,
    926                    curl_socket_t listenfd,
    927                    enum sockmode *mode)
    928 {
    929 #ifdef UNDER_CE
    930   (void)sockfdp;
    931   (void)listenfd;
    932   (void)mode;
    933   return FALSE;
    934 #else
    935   struct timeval timeout;
    936   fd_set fds_read;
    937   fd_set fds_write;
    938   fd_set fds_err;
    939   curl_socket_t sockfd = CURL_SOCKET_BAD;
    940   int maxfd = -99;
    941   ssize_t rc;
    942   int error = 0;
    943 
    944   unsigned char buffer[BUFFER_SIZE];
    945   char data[16];
    946 
    947   if(got_exit_signal) {
    948     logmsg("signalled to die, exiting...");
    949     return FALSE;
    950   }
    951 
    952 #ifdef HAVE_GETPPID
    953   /* As a last resort, quit if sockfilt process becomes orphan. Just in case
    954      parent ftpserver process has died without killing its sockfilt children */
    955   if(getppid() <= 1) {
    956     logmsg("process becomes orphan, exiting");
    957     return FALSE;
    958   }
    959 #endif
    960 
    961   timeout.tv_sec = 120;
    962   timeout.tv_usec = 0;
    963 
    964   FD_ZERO(&fds_read);
    965   FD_ZERO(&fds_write);
    966   FD_ZERO(&fds_err);
    967 
    968 #if defined(__DJGPP__)
    969 #pragma GCC diagnostic push
    970 #pragma GCC diagnostic ignored "-Warith-conversion"
    971 #endif
    972   FD_SET((curl_socket_t)fileno(stdin), &fds_read);
    973 #if defined(__DJGPP__)
    974 #pragma GCC diagnostic pop
    975 #endif
    976 
    977   switch(*mode) {
    978 
    979   case PASSIVE_LISTEN:
    980 
    981     /* server mode */
    982     sockfd = listenfd;
    983     /* there's always a socket to wait for */
    984 #if defined(__DJGPP__)
    985 #pragma GCC diagnostic push
    986 #pragma GCC diagnostic ignored "-Warith-conversion"
    987 #endif
    988     FD_SET(sockfd, &fds_read);
    989 #if defined(__DJGPP__)
    990 #pragma GCC diagnostic pop
    991 #endif
    992     maxfd = (int)sockfd;
    993     break;
    994 
    995   case PASSIVE_CONNECT:
    996 
    997     sockfd = *sockfdp;
    998     if(CURL_SOCKET_BAD == sockfd) {
    999       /* eeek, we are supposedly connected and then this cannot be -1 ! */
   1000       logmsg("socket is -1! on %s:%d", __FILE__, __LINE__);
   1001       maxfd = 0; /* stdin */
   1002     }
   1003     else {
   1004       /* there's always a socket to wait for */
   1005 #if defined(__DJGPP__)
   1006 #pragma GCC diagnostic push
   1007 #pragma GCC diagnostic ignored "-Warith-conversion"
   1008 #endif
   1009       FD_SET(sockfd, &fds_read);
   1010 #if defined(__DJGPP__)
   1011 #pragma GCC diagnostic pop
   1012 #endif
   1013       maxfd = (int)sockfd;
   1014     }
   1015     break;
   1016 
   1017   case ACTIVE:
   1018 
   1019     sockfd = *sockfdp;
   1020     /* sockfd turns CURL_SOCKET_BAD when our connection has been closed */
   1021     if(CURL_SOCKET_BAD != sockfd) {
   1022 #if defined(__DJGPP__)
   1023 #pragma GCC diagnostic push
   1024 #pragma GCC diagnostic ignored "-Warith-conversion"
   1025 #endif
   1026       FD_SET(sockfd, &fds_read);
   1027 #if defined(__DJGPP__)
   1028 #pragma GCC diagnostic pop
   1029 #endif
   1030       maxfd = (int)sockfd;
   1031     }
   1032     else {
   1033       logmsg("No socket to read on");
   1034       maxfd = 0;
   1035     }
   1036     break;
   1037 
   1038   case ACTIVE_DISCONNECT:
   1039 
   1040     logmsg("disconnected, no socket to read on");
   1041     maxfd = 0;
   1042     sockfd = CURL_SOCKET_BAD;
   1043     break;
   1044 
   1045   } /* switch(*mode) */
   1046 
   1047 
   1048   do {
   1049 
   1050     /* select() blocking behavior call on blocking descriptors please */
   1051 
   1052     rc = SOCKFILT_select(maxfd + 1, &fds_read, &fds_write, &fds_err, &timeout);
   1053 
   1054     if(got_exit_signal) {
   1055       logmsg("signalled to die, exiting...");
   1056       return FALSE;
   1057     }
   1058 
   1059   } while((rc == -1) && ((error = SOCKERRNO) == SOCKEINTR));
   1060 
   1061   if(rc < 0) {
   1062     logmsg("select() failed with error (%d) %s",
   1063            error, sstrerror(error));
   1064     return FALSE;
   1065   }
   1066 
   1067   if(rc == 0)
   1068     /* timeout */
   1069     return TRUE;
   1070 
   1071 
   1072   if(FD_ISSET(fileno(stdin), &fds_read)) {
   1073     ssize_t buffer_len;
   1074     /* read from stdin, commands/data to be dealt with and possibly passed on
   1075        to the socket
   1076 
   1077        protocol:
   1078 
   1079        4 letter command + LF [mandatory]
   1080 
   1081        4-digit hexadecimal data length + LF [if the command takes data]
   1082        data                       [the data being as long as set above]
   1083 
   1084        Commands:
   1085 
   1086        DATA - plain pass-through data
   1087     */
   1088 
   1089     if(!read_stdin(buffer, 5))
   1090       return FALSE;
   1091 
   1092     logmsg("Received %c%c%c%c (on stdin)",
   1093            buffer[0], buffer[1], buffer[2], buffer[3]);
   1094 
   1095     if(!memcmp("PING", buffer, 4)) {
   1096       /* send reply on stdout, just proving we are alive */
   1097       if(!write_stdout("PONG\n", 5))
   1098         return FALSE;
   1099     }
   1100 
   1101     else if(!memcmp("PORT", buffer, 4)) {
   1102       /* Question asking us what PORT number we are listening to.
   1103          Replies to PORT with "IPv[num]/[port]" */
   1104       snprintf((char *)buffer, sizeof(buffer), "%s/%hu\n",
   1105                ipv_inuse, server_port);
   1106       buffer_len = (ssize_t)strlen((char *)buffer);
   1107       snprintf(data, sizeof(data), "PORT\n%04x\n", (int)buffer_len);
   1108       if(!write_stdout(data, 10))
   1109         return FALSE;
   1110       if(!write_stdout(buffer, buffer_len))
   1111         return FALSE;
   1112     }
   1113     else if(!memcmp("QUIT", buffer, 4)) {
   1114       /* just die */
   1115       logmsg("quits");
   1116       return FALSE;
   1117     }
   1118     else if(!memcmp("DATA", buffer, 4)) {
   1119       /* data IN => data OUT */
   1120       if(!read_data_block(buffer, sizeof(buffer), &buffer_len))
   1121         return FALSE;
   1122 
   1123       if(buffer_len < 0)
   1124         return FALSE;
   1125 
   1126       if(*mode == PASSIVE_LISTEN) {
   1127         logmsg("*** We are disconnected!");
   1128         if(!disc_handshake())
   1129           return FALSE;
   1130       }
   1131       else {
   1132         /* send away on the socket */
   1133         ssize_t bytes_written = swrite(sockfd, buffer, buffer_len);
   1134         if(bytes_written != buffer_len) {
   1135           logmsg("Not all data was sent. Bytes to send: %zd sent: %zd",
   1136                  buffer_len, bytes_written);
   1137         }
   1138       }
   1139     }
   1140     else if(!memcmp("DISC", buffer, 4)) {
   1141       /* disconnect! */
   1142       if(!write_stdout("ACKD\n", 5))
   1143         return FALSE;
   1144       if(sockfd != CURL_SOCKET_BAD) {
   1145         logmsg("====> Client forcibly disconnected");
   1146         sclose(sockfd);
   1147         *sockfdp = CURL_SOCKET_BAD;
   1148         if(*mode == PASSIVE_CONNECT)
   1149           *mode = PASSIVE_LISTEN;
   1150         else
   1151           *mode = ACTIVE_DISCONNECT;
   1152       }
   1153       else
   1154         logmsg("attempt to close already dead connection");
   1155       return TRUE;
   1156     }
   1157   }
   1158 
   1159 
   1160   if((sockfd != CURL_SOCKET_BAD) && (FD_ISSET(sockfd, &fds_read)) ) {
   1161     ssize_t nread_socket;
   1162     if(*mode == PASSIVE_LISTEN) {
   1163       /* there's no stream set up yet, this is an indication that there's a
   1164          client connecting. */
   1165       curl_socket_t newfd = accept(sockfd, NULL, NULL);
   1166       if(CURL_SOCKET_BAD == newfd) {
   1167         error = SOCKERRNO;
   1168         logmsg("accept() failed with error (%d) %s", error, sstrerror(error));
   1169       }
   1170       else {
   1171         logmsg("====> Client connect");
   1172         if(!write_stdout("CNCT\n", 5))
   1173           return FALSE;
   1174         *sockfdp = newfd; /* store the new socket */
   1175         *mode = PASSIVE_CONNECT; /* we have connected */
   1176       }
   1177       return TRUE;
   1178     }
   1179 
   1180     /* read from socket, pass on data to stdout */
   1181     nread_socket = sread(sockfd, buffer, sizeof(buffer));
   1182 
   1183     if(nread_socket > 0) {
   1184       snprintf(data, sizeof(data), "DATA\n%04x\n", (int)nread_socket);
   1185       if(!write_stdout(data, 10))
   1186         return FALSE;
   1187       if(!write_stdout(buffer, nread_socket))
   1188         return FALSE;
   1189 
   1190       logmsg("< %zd bytes data, client => server", nread_socket);
   1191       lograw(buffer, nread_socket);
   1192     }
   1193 
   1194     if(nread_socket <= 0) {
   1195       logmsg("====> Client disconnect");
   1196       if(!disc_handshake())
   1197         return FALSE;
   1198       sclose(sockfd);
   1199       *sockfdp = CURL_SOCKET_BAD;
   1200       if(*mode == PASSIVE_CONNECT)
   1201         *mode = PASSIVE_LISTEN;
   1202       else
   1203         *mode = ACTIVE_DISCONNECT;
   1204       return TRUE;
   1205     }
   1206   }
   1207 
   1208   return TRUE;
   1209 #endif
   1210 }
   1211 
   1212 static int test_sockfilt(int argc, char *argv[])
   1213 {
   1214   srvr_sockaddr_union_t me;
   1215   curl_socket_t sock = CURL_SOCKET_BAD;
   1216   curl_socket_t msgsock = CURL_SOCKET_BAD;
   1217   int wrotepidfile = 0;
   1218   int wroteportfile = 0;
   1219   bool juggle_again;
   1220   int rc;
   1221   int error;
   1222   int arg = 1;
   1223   enum sockmode mode = PASSIVE_LISTEN; /* default */
   1224   const char *addr = NULL;
   1225 
   1226   pidname = ".sockfilt.pid";
   1227   serverlogfile = "log/sockfilt.log";
   1228   server_port = 8999;
   1229 
   1230   while(argc > arg) {
   1231     if(!strcmp("--version", argv[arg])) {
   1232       printf("sockfilt IPv4%s\n",
   1233 #ifdef USE_IPV6
   1234              "/IPv6"
   1235 #else
   1236              ""
   1237 #endif
   1238              );
   1239       return 0;
   1240     }
   1241     else if(!strcmp("--verbose", argv[arg])) {
   1242       verbose = TRUE;
   1243       arg++;
   1244     }
   1245     else if(!strcmp("--pidfile", argv[arg])) {
   1246       arg++;
   1247       if(argc > arg)
   1248         pidname = argv[arg++];
   1249     }
   1250     else if(!strcmp("--portfile", argv[arg])) {
   1251       arg++;
   1252       if(argc > arg)
   1253         portname = argv[arg++];
   1254     }
   1255     else if(!strcmp("--logfile", argv[arg])) {
   1256       arg++;
   1257       if(argc > arg)
   1258         serverlogfile = argv[arg++];
   1259     }
   1260     else if(!strcmp("--ipv6", argv[arg])) {
   1261 #ifdef USE_IPV6
   1262       socket_domain = AF_INET6;
   1263       ipv_inuse = "IPv6";
   1264 #endif
   1265       arg++;
   1266     }
   1267     else if(!strcmp("--ipv4", argv[arg])) {
   1268       /* for completeness, we support this option as well */
   1269 #ifdef USE_IPV6
   1270       socket_domain = AF_INET;
   1271       ipv_inuse = "IPv4";
   1272 #endif
   1273       arg++;
   1274     }
   1275     else if(!strcmp("--bindonly", argv[arg])) {
   1276       s_bind_only = TRUE;
   1277       arg++;
   1278     }
   1279     else if(!strcmp("--port", argv[arg])) {
   1280       arg++;
   1281       if(argc > arg) {
   1282         char *endptr;
   1283         unsigned long ulnum = strtoul(argv[arg], &endptr, 10);
   1284         server_port = util_ultous(ulnum);
   1285         arg++;
   1286       }
   1287     }
   1288     else if(!strcmp("--connect", argv[arg])) {
   1289       /* Asked to actively connect to the specified local port instead of
   1290          doing a passive server-style listening. */
   1291       arg++;
   1292       if(argc > arg) {
   1293         char *endptr;
   1294         unsigned long ulnum = strtoul(argv[arg], &endptr, 10);
   1295         if((endptr != argv[arg] + strlen(argv[arg])) ||
   1296            (ulnum < 1025UL) || (ulnum > 65535UL)) {
   1297           fprintf(stderr, "sockfilt: invalid --connect argument (%s)\n",
   1298                   argv[arg]);
   1299           return 0;
   1300         }
   1301         server_connectport = util_ultous(ulnum);
   1302         arg++;
   1303       }
   1304     }
   1305     else if(!strcmp("--addr", argv[arg])) {
   1306       /* Set an IP address to use with --connect; otherwise use localhost */
   1307       arg++;
   1308       if(argc > arg) {
   1309         addr = argv[arg];
   1310         arg++;
   1311       }
   1312     }
   1313     else {
   1314       puts("Usage: sockfilt [option]\n"
   1315            " --version\n"
   1316            " --verbose\n"
   1317            " --logfile [file]\n"
   1318            " --pidfile [file]\n"
   1319            " --portfile [file]\n"
   1320            " --ipv4\n"
   1321            " --ipv6\n"
   1322            " --bindonly\n"
   1323            " --port [port]\n"
   1324            " --connect [port]\n"
   1325            " --addr [address]");
   1326       return 0;
   1327     }
   1328   }
   1329 
   1330 #ifdef _WIN32
   1331   if(win32_init())
   1332     return 2;
   1333 #endif
   1334 
   1335   CURLX_SET_BINMODE(stdin);
   1336   CURLX_SET_BINMODE(stdout);
   1337   CURLX_SET_BINMODE(stderr);
   1338 
   1339   install_signal_handlers(false);
   1340 
   1341   sock = socket(socket_domain, SOCK_STREAM, 0);
   1342 
   1343   if(CURL_SOCKET_BAD == sock) {
   1344     error = SOCKERRNO;
   1345     logmsg("Error creating socket (%d) %s", error, sstrerror(error));
   1346     write_stdout("FAIL\n", 5);
   1347     goto sockfilt_cleanup;
   1348   }
   1349 
   1350   if(server_connectport) {
   1351     /* Active mode, we should connect to the given port number */
   1352     mode = ACTIVE;
   1353     switch(socket_domain) {
   1354       case AF_INET:
   1355         memset(&me.sa4, 0, sizeof(me.sa4));
   1356         me.sa4.sin_family = AF_INET;
   1357         me.sa4.sin_port = htons(server_connectport);
   1358         me.sa4.sin_addr.s_addr = INADDR_ANY;
   1359         if(!addr)
   1360           addr = "127.0.0.1";
   1361         curlx_inet_pton(AF_INET, addr, &me.sa4.sin_addr);
   1362 
   1363         rc = connect(sock, &me.sa, sizeof(me.sa4));
   1364         break;
   1365 #ifdef USE_IPV6
   1366       case AF_INET6:
   1367         memset(&me.sa6, 0, sizeof(me.sa6));
   1368         me.sa6.sin6_family = AF_INET6;
   1369         me.sa6.sin6_port = htons(server_connectport);
   1370         if(!addr)
   1371           addr = "::1";
   1372         curlx_inet_pton(AF_INET6, addr, &me.sa6.sin6_addr);
   1373 
   1374         rc = connect(sock, &me.sa, sizeof(me.sa6));
   1375         break;
   1376 #endif /* USE_IPV6 */
   1377       default:
   1378         rc = 1;
   1379     }
   1380     if(rc) {
   1381       error = SOCKERRNO;
   1382       logmsg("Error connecting to port %hu (%d) %s",
   1383              server_connectport, error, sstrerror(error));
   1384       write_stdout("FAIL\n", 5);
   1385       goto sockfilt_cleanup;
   1386     }
   1387     logmsg("====> Client connect");
   1388     msgsock = sock; /* use this as stream */
   1389   }
   1390   else {
   1391     /* passive daemon style */
   1392     sock = sockdaemon(sock, &server_port, NULL, s_bind_only);
   1393     if(CURL_SOCKET_BAD == sock) {
   1394       write_stdout("FAIL\n", 5);
   1395       goto sockfilt_cleanup;
   1396     }
   1397     msgsock = CURL_SOCKET_BAD; /* no stream socket yet */
   1398   }
   1399 
   1400   logmsg("Running %s version", ipv_inuse);
   1401 
   1402   if(server_connectport)
   1403     logmsg("Connected to port %hu", server_connectport);
   1404   else if(s_bind_only)
   1405     logmsg("Bound without listening on port %hu", server_port);
   1406   else
   1407     logmsg("Listening on port %hu", server_port);
   1408 
   1409   wrotepidfile = write_pidfile(pidname);
   1410   if(!wrotepidfile) {
   1411     write_stdout("FAIL\n", 5);
   1412     goto sockfilt_cleanup;
   1413   }
   1414   if(portname) {
   1415     wroteportfile = write_portfile(portname, server_port);
   1416     if(!wroteportfile) {
   1417       write_stdout("FAIL\n", 5);
   1418       goto sockfilt_cleanup;
   1419     }
   1420   }
   1421 
   1422   do {
   1423     juggle_again = juggle(&msgsock, sock, &mode);
   1424   } while(juggle_again);
   1425 
   1426 sockfilt_cleanup:
   1427 
   1428   if((msgsock != sock) && (msgsock != CURL_SOCKET_BAD))
   1429     sclose(msgsock);
   1430 
   1431   if(sock != CURL_SOCKET_BAD)
   1432     sclose(sock);
   1433 
   1434   if(wrotepidfile)
   1435     unlink(pidname);
   1436   if(wroteportfile)
   1437     unlink(portname);
   1438 
   1439   restore_signal_handlers(false);
   1440 
   1441   if(got_exit_signal) {
   1442     logmsg("============> sockfilt exits with signal (%d)", exit_signal);
   1443     /*
   1444      * To properly set the return status of the process we
   1445      * must raise the same signal SIGINT or SIGTERM that we
   1446      * caught and let the old handler take care of it.
   1447      */
   1448     raise(exit_signal);
   1449   }
   1450 
   1451   logmsg("============> sockfilt quits");
   1452   return 0;
   1453 }