quickjs-tart

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

sws.c (75360B)


      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 /* sws.c: simple (silly?) web server
     27 
     28    This code was originally graciously donated to the project by Juergen
     29    Wilke. Thanks a bunch!
     30 
     31  */
     32 
     33 #ifdef HAVE_NETINET_TCP_H
     34 #include <netinet/tcp.h> /* for TCP_NODELAY */
     35 #endif
     36 
     37 static bool use_gopher = FALSE;
     38 static bool is_proxy = FALSE;
     39 
     40 #undef REQBUFSIZ
     41 #define REQBUFSIZ (2*1024*1024)
     42 
     43 #define MAX_SLEEP_TIME_MS 250
     44 
     45 static long sws_prevtestno = -1;    /* previous test number we served */
     46 static long sws_prevpartno = -1;    /* previous part number we served */
     47 static bool sws_prevbounce = FALSE; /* instructs the server to override the
     48                                        requested part number to
     49                                        prevpartno + 1 when prevtestno and
     50                                        current test are the same */
     51 
     52 #define RCMD_NORMALREQ 0 /* default request, use the tests file normally */
     53 #define RCMD_IDLE      1 /* told to sit idle */
     54 #define RCMD_STREAM    2 /* told to stream */
     55 
     56 struct sws_httprequest {
     57   char reqbuf[REQBUFSIZ]; /* buffer area for the incoming request */
     58   bool connect_request; /* if a CONNECT */
     59   unsigned short connect_port; /* the port number CONNECT used */
     60   size_t checkindex; /* where to start checking of the request */
     61   size_t offset;     /* size of the incoming request */
     62   long testno;       /* test number found in the request */
     63   long partno;       /* part number found in the request */
     64   bool open;      /* keep connection open info, as found in the request */
     65   bool auth_req;  /* authentication required, don't wait for body unless
     66                      there's an Authorization header */
     67   bool auth;      /* Authorization header present in the incoming request */
     68   size_t cl;      /* Content-Length of the incoming request */
     69   bool digest;    /* Authorization digest header found */
     70   bool ntlm;      /* Authorization NTLM header found */
     71   int delay;      /* if non-zero, delay this number of msec after connect */
     72   int writedelay; /* if non-zero, delay this number of milliseconds between
     73                      writes in the response */
     74   int skip;       /* if non-zero, the server is instructed to not read this
     75                      many bytes from a PUT/POST request. Ie the client sends N
     76                      bytes said in Content-Length, but the server only reads N
     77                      - skip bytes. */
     78   int rcmd;       /* doing a special command, see defines above */
     79   int prot_version;  /* HTTP version * 10 */
     80   int callcount;  /* times sws_ProcessRequest() gets called */
     81   bool skipall;   /* skip all incoming data */
     82   bool noexpect;  /* refuse Expect: (don't read the body) */
     83   bool connmon;   /* monitor the state of the connection, log disconnects */
     84   bool upgrade;   /* test case allows upgrade */
     85   bool upgrade_request; /* upgrade request found and allowed */
     86   bool close;     /* similar to swsclose in response: close connection after
     87                      response is sent */
     88   int done_processing;
     89 };
     90 
     91 #define MAX_SOCKETS 1024
     92 
     93 static curl_socket_t all_sockets[MAX_SOCKETS];
     94 static size_t num_sockets = 0;
     95 
     96 #define SWSVERSION "curl test suite HTTP server/0.1"
     97 
     98 #define REQUEST_DUMP  "server.input"
     99 #define RESPONSE_DUMP "server.response"
    100 
    101 /* when told to run as proxy, we store the logs in different files so that
    102    they can co-exist with the same program running as a "server" */
    103 #define REQUEST_PROXY_DUMP  "proxy.input"
    104 #define RESPONSE_PROXY_DUMP "proxy.response"
    105 
    106 /* file in which additional instructions may be found */
    107 static const char *cmdfile = "log/server.cmd";
    108 
    109 /* very-big-path support */
    110 #define MAXDOCNAMELEN 140000
    111 #define MAXDOCNAMELEN_TXT "139999"
    112 
    113 #define REQUEST_KEYWORD_SIZE 256
    114 #define REQUEST_KEYWORD_SIZE_TXT "255"
    115 
    116 #define CMD_AUTH_REQUIRED "auth_required"
    117 
    118 /* 'idle' means that it will accept the request fine but never respond
    119    any data. Just keep the connection alive. */
    120 #define CMD_IDLE "idle"
    121 
    122 /* 'stream' means to send a never-ending stream of data */
    123 #define CMD_STREAM "stream"
    124 
    125 /* 'connection-monitor' will output when a server/proxy connection gets
    126    disconnected as for some cases it is important that it gets done at the
    127    proper point - like with NTLM */
    128 #define CMD_CONNECTIONMONITOR "connection-monitor"
    129 
    130 /* upgrade to http2/websocket/xxxx */
    131 #define CMD_UPGRADE "upgrade"
    132 
    133 /* close connection */
    134 #define CMD_SWSCLOSE "swsclose"
    135 
    136 /* deny Expect: requests */
    137 #define CMD_NOEXPECT "no-expect"
    138 
    139 #define END_OF_HEADERS "\r\n\r\n"
    140 
    141 static const char *end_of_headers = END_OF_HEADERS;
    142 
    143 /* sent as reply to a QUIT */
    144 static const char *docquit_sws =
    145 "HTTP/1.1 200 Goodbye" END_OF_HEADERS;
    146 
    147 /* send back this on 404 file not found */
    148 static const char *doc404 = "HTTP/1.1 404 Not Found\r\n"
    149     "Server: " SWSVERSION "\r\n"
    150     "Connection: close\r\n"
    151     "Content-Type: text/html"
    152     END_OF_HEADERS
    153     "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n"
    154     "<HTML><HEAD>\n"
    155     "<TITLE>404 Not Found</TITLE>\n"
    156     "</HEAD><BODY>\n"
    157     "<H1>Not Found</H1>\n"
    158     "The requested URL was not found on this server.\n"
    159     "<P><HR><ADDRESS>" SWSVERSION "</ADDRESS>\n" "</BODY></HTML>\n";
    160 
    161 /* work around for handling trailing headers */
    162 static int already_recv_zeroed_chunk = FALSE;
    163 
    164 #ifdef TCP_NODELAY
    165 /* returns true if the current socket is an IP one */
    166 static bool socket_domain_is_ip(void)
    167 {
    168   switch(socket_domain) {
    169   case AF_INET:
    170 #ifdef USE_IPV6
    171   case AF_INET6:
    172 #endif
    173     return true;
    174   default:
    175   /* case AF_UNIX: */
    176     return false;
    177   }
    178 }
    179 #endif
    180 
    181 /* parse the file on disk that might have a test number for us */
    182 static int parse_cmdfile(struct sws_httprequest *req)
    183 {
    184   FILE *f = fopen(cmdfile, FOPEN_READTEXT);
    185   if(f) {
    186     int testnum = DOCNUMBER_NOTHING;
    187     char buf[256];
    188     while(fgets(buf, sizeof(buf), f)) {
    189       if(1 == sscanf(buf, "Testnum %d", &testnum)) {
    190         logmsg("[%s] cmdfile says testnum %d", cmdfile, testnum);
    191         req->testno = testnum;
    192       }
    193     }
    194     fclose(f);
    195   }
    196   return 0;
    197 }
    198 
    199 /* based on the testno, parse the correct server commands */
    200 static int sws_parse_servercmd(struct sws_httprequest *req)
    201 {
    202   FILE *stream;
    203   int error;
    204 
    205   stream = test2fopen(req->testno, logdir);
    206   req->close = FALSE;
    207   req->connmon = FALSE;
    208 
    209   if(!stream) {
    210     error = errno;
    211     logmsg("fopen() failed with error (%d) %s", error, strerror(error));
    212     logmsg("  Couldn't open test file %ld", req->testno);
    213     req->open = FALSE; /* closes connection */
    214     return 1; /* done */
    215   }
    216   else {
    217     char *orgcmd = NULL;
    218     char *cmd = NULL;
    219     size_t cmdsize = 0;
    220     int num = 0;
    221 
    222     /* get the custom server control "commands" */
    223     error = getpart(&orgcmd, &cmdsize, "reply", "servercmd", stream);
    224     fclose(stream);
    225     if(error) {
    226       logmsg("getpart() failed with error (%d)", error);
    227       req->open = FALSE; /* closes connection */
    228       return 1; /* done */
    229     }
    230 
    231     cmd = orgcmd;
    232     while(cmd && cmdsize) {
    233       char *check;
    234 
    235       if(!strncmp(CMD_AUTH_REQUIRED, cmd, strlen(CMD_AUTH_REQUIRED))) {
    236         logmsg("instructed to require authorization header");
    237         req->auth_req = TRUE;
    238       }
    239       else if(!strncmp(CMD_IDLE, cmd, strlen(CMD_IDLE))) {
    240         logmsg("instructed to idle");
    241         req->rcmd = RCMD_IDLE;
    242         req->open = TRUE;
    243       }
    244       else if(!strncmp(CMD_STREAM, cmd, strlen(CMD_STREAM))) {
    245         logmsg("instructed to stream");
    246         req->rcmd = RCMD_STREAM;
    247       }
    248       else if(!strncmp(CMD_CONNECTIONMONITOR, cmd,
    249                        strlen(CMD_CONNECTIONMONITOR))) {
    250         logmsg("enabled connection monitoring");
    251         req->connmon = TRUE;
    252       }
    253       else if(!strncmp(CMD_UPGRADE, cmd, strlen(CMD_UPGRADE))) {
    254         logmsg("enabled upgrade");
    255         req->upgrade = TRUE;
    256       }
    257       else if(!strncmp(CMD_SWSCLOSE, cmd, strlen(CMD_SWSCLOSE))) {
    258         logmsg("swsclose: close this connection after response");
    259         req->close = TRUE;
    260       }
    261       else if(1 == sscanf(cmd, "skip: %d", &num)) {
    262         logmsg("instructed to skip this number of bytes %d", num);
    263         req->skip = num;
    264       }
    265       else if(!strncmp(CMD_NOEXPECT, cmd, strlen(CMD_NOEXPECT))) {
    266         logmsg("instructed to reject Expect: 100-continue");
    267         req->noexpect = TRUE;
    268       }
    269       else if(1 == sscanf(cmd, "delay: %d", &num)) {
    270         logmsg("instructed to delay %d msecs after connect", num);
    271         req->delay = num;
    272       }
    273       else if(1 == sscanf(cmd, "writedelay: %d", &num)) {
    274         logmsg("instructed to delay %d msecs between packets", num);
    275         req->writedelay = num;
    276       }
    277       else {
    278         logmsg("Unknown <servercmd> instruction found: %s", cmd);
    279       }
    280       /* try to deal with CRLF or just LF */
    281       check = strchr(cmd, '\r');
    282       if(!check)
    283         check = strchr(cmd, '\n');
    284 
    285       if(check) {
    286         /* get to the letter following the newline */
    287         while((*check == '\r') || (*check == '\n'))
    288           check++;
    289 
    290         if(!*check)
    291           /* if we reached a zero, get out */
    292           break;
    293         cmd = check;
    294       }
    295       else
    296         break;
    297     }
    298     free(orgcmd);
    299   }
    300 
    301   return 0; /* OK! */
    302 }
    303 
    304 static int sws_ProcessRequest(struct sws_httprequest *req)
    305 {
    306   char *line = &req->reqbuf[req->checkindex];
    307   bool chunked = FALSE;
    308   static char request[REQUEST_KEYWORD_SIZE];
    309   int prot_major = 0;
    310   int prot_minor = 0;
    311   char *end = strstr(line, end_of_headers);
    312 
    313   req->callcount++;
    314 
    315   logmsg("Process %zu bytes request%s", req->offset,
    316          req->callcount > 1 ? " [CONTINUED]" : "");
    317 
    318   /* try to figure out the request characteristics as soon as possible, but
    319      only once! */
    320 
    321   if(use_gopher &&
    322      (req->testno == DOCNUMBER_NOTHING) &&
    323      !strncmp("/verifiedserver", line, 15)) {
    324     logmsg("Are-we-friendly question received");
    325     req->testno = DOCNUMBER_WERULEZ;
    326     return 1; /* done */
    327   }
    328 
    329   else if(req->testno == DOCNUMBER_NOTHING) {
    330     char *http;
    331     bool fine = FALSE;
    332     char *httppath = NULL;
    333     size_t npath = 0; /* httppath length */
    334 
    335     if(sscanf(line,
    336               "%" REQUEST_KEYWORD_SIZE_TXT"s ", request)) {
    337       http = strstr(line + strlen(request), "HTTP/");
    338 
    339       if(http && sscanf(http, "HTTP/%d.%d",
    340                         &prot_major,
    341                         &prot_minor) == 2) {
    342         /* between the request keyword and HTTP/ there's a path */
    343         httppath = line + strlen(request);
    344         npath = http - httppath;
    345 
    346         /* trim leading spaces */
    347         while(npath && ISSPACE(*httppath)) {
    348           httppath++;
    349           npath--;
    350         }
    351         /* trim ending spaces */
    352         while(npath && ISSPACE(httppath[npath - 1])) {
    353           npath--;
    354         }
    355         if(npath)
    356           fine = TRUE;
    357       }
    358     }
    359 
    360     if(fine) {
    361       char *ptr;
    362 
    363       req->prot_version = prot_major*10 + prot_minor;
    364 
    365       /* find the last slash */
    366       ptr = &httppath[npath];
    367       while(ptr >= httppath) {
    368         if(*ptr == '/')
    369           break;
    370         ptr--;
    371       }
    372 
    373       /* get the number after it */
    374       if(*ptr == '/') {
    375         if((npath + strlen(request)) < 400)
    376           logmsg("Got request: %s %.*s HTTP/%d.%d",
    377                  request, (int)npath, httppath, prot_major, prot_minor);
    378         else
    379           logmsg("Got a *HUGE* request HTTP/%d.%d", prot_major, prot_minor);
    380 
    381         if(!strncmp("/verifiedserver", ptr, 15)) {
    382           logmsg("Are-we-friendly question received");
    383           req->testno = DOCNUMBER_WERULEZ;
    384           return 1; /* done */
    385         }
    386 
    387         if(!strncmp("/quit", ptr, 5)) {
    388           logmsg("Request-to-quit received");
    389           req->testno = DOCNUMBER_QUIT;
    390           return 1; /* done */
    391         }
    392 
    393         ptr++; /* skip the slash */
    394 
    395         req->testno = strtol(ptr, &ptr, 10);
    396 
    397         if(req->testno > 10000) {
    398           req->partno = req->testno % 10000;
    399           req->testno /= 10000;
    400         }
    401         else
    402           req->partno = 0;
    403 
    404         if(req->testno) {
    405           logmsg("Serve test number %ld part %ld", req->testno, req->partno);
    406         }
    407         else {
    408           logmsg("No test number in path");
    409           req->testno = DOCNUMBER_NOTHING;
    410         }
    411 
    412       }
    413 
    414       if(req->testno == DOCNUMBER_NOTHING) {
    415         /* didn't find any in the first scan, try alternative test case
    416            number placements */
    417         static char doc[MAXDOCNAMELEN];
    418         if(sscanf(req->reqbuf, "CONNECT %" MAXDOCNAMELEN_TXT "s HTTP/%d.%d",
    419                   doc, &prot_major, &prot_minor) == 3) {
    420           char *portp = NULL;
    421 
    422           logmsg("Received a CONNECT %s HTTP/%d.%d request",
    423                  doc, prot_major, prot_minor);
    424 
    425           req->connect_request = TRUE;
    426 
    427           if(req->prot_version == 10)
    428             req->open = FALSE; /* HTTP 1.0 closes connection by default */
    429 
    430           if(doc[0] == '[') {
    431             char *p = &doc[1];
    432             unsigned long part = 0;
    433             /* scan through the hexgroups and store the value of the last group
    434                in the 'part' variable and use as test case number!! */
    435             while(*p && (ISXDIGIT(*p) || (*p == ':') || (*p == '.'))) {
    436               char *endp;
    437               part = strtoul(p, &endp, 16);
    438               if(ISXDIGIT(*p))
    439                 p = endp;
    440               else
    441                 p++;
    442             }
    443             if(*p != ']')
    444               logmsg("Invalid CONNECT IPv6 address format");
    445             else if(*(p + 1) != ':')
    446               logmsg("Invalid CONNECT IPv6 port format");
    447             else
    448               portp = p + 1;
    449 
    450             req->testno = part;
    451           }
    452           else
    453             portp = strchr(doc, ':');
    454 
    455           if(portp && (*(portp + 1) != '\0') && ISDIGIT(*(portp + 1))) {
    456             unsigned long ulnum = strtoul(portp + 1, NULL, 10);
    457             if(!ulnum || (ulnum > 65535UL))
    458               logmsg("Invalid CONNECT port received");
    459             else
    460               req->connect_port = util_ultous(ulnum);
    461 
    462           }
    463           logmsg("Port number: %d, test case number: %ld",
    464                  req->connect_port, req->testno);
    465         }
    466       }
    467 
    468       if(req->testno == DOCNUMBER_NOTHING)
    469         /* might get the test number */
    470         parse_cmdfile(req);
    471 
    472       if(req->testno == DOCNUMBER_NOTHING) {
    473         logmsg("Did not find test number in PATH");
    474         req->testno = DOCNUMBER_404;
    475       }
    476       else
    477         sws_parse_servercmd(req);
    478     }
    479     else if((req->offset >= 3)) {
    480       unsigned char *l = (unsigned char *)line;
    481       logmsg("** Unusual request. Starts with %02x %02x %02x (%c%c%c)",
    482              l[0], l[1], l[2], l[0], l[1], l[2]);
    483     }
    484   }
    485 
    486   if(!end) {
    487     /* we don't have a complete request yet! */
    488     logmsg("request not complete yet");
    489     return 0; /* not complete yet */
    490   }
    491   logmsg("- request found to be complete (%ld)", req->testno);
    492 
    493   if(req->testno == DOCNUMBER_NOTHING) {
    494     /* check for a Testno: header with the test case number */
    495     char *testno = strstr(line, "\nTestno: ");
    496     if(testno) {
    497       req->testno = strtol(&testno[9], NULL, 10);
    498       logmsg("Found test number %ld in Testno: header!", req->testno);
    499     }
    500     else {
    501       logmsg("No Testno: header");
    502     }
    503   }
    504 
    505   /* find and parse <servercmd> for this test */
    506   sws_parse_servercmd(req);
    507 
    508   if(use_gopher) {
    509     /* when using gopher we cannot check the request until the entire
    510        thing has been received */
    511     char *ptr;
    512 
    513     /* find the last slash in the line */
    514     ptr = strrchr(line, '/');
    515 
    516     if(ptr) {
    517       ptr++; /* skip the slash */
    518 
    519       /* skip all non-numericals following the slash */
    520       while(*ptr && !ISDIGIT(*ptr))
    521         ptr++;
    522 
    523       req->testno = strtol(ptr, &ptr, 10);
    524 
    525       if(req->testno > 10000) {
    526         req->partno = req->testno % 10000;
    527         req->testno /= 10000;
    528       }
    529       else
    530         req->partno = 0;
    531 
    532       logmsg("Requested GOPHER test number %ld part %ld",
    533              req->testno, req->partno);
    534     }
    535   }
    536 
    537   /* **** Persistence ****
    538    *
    539    * If the request is an HTTP/1.0 one, we close the connection unconditionally
    540    * when we're done.
    541    *
    542    * If the request is an HTTP/1.1 one, we MUST check for a "Connection:"
    543    * header that might say "close". If it does, we close a connection when
    544    * this request is processed. Otherwise, we keep the connection alive for X
    545    * seconds.
    546    */
    547 
    548   do {
    549     if(got_exit_signal)
    550       return 1; /* done */
    551 
    552     if((req->cl == 0) && !CURL_STRNICMP("Content-Length:", line, 15)) {
    553       /* If we don't ignore content-length, we read it and we read the whole
    554          request including the body before we return. If we've been told to
    555          ignore the content-length, we will return as soon as all headers
    556          have been received */
    557       curl_off_t clen;
    558       const char *p = line + strlen("Content-Length:");
    559       if(curlx_str_numblanks(&p, &clen)) {
    560         /* this assumes that a zero Content-Length is valid */
    561         logmsg("Found invalid '%s' in the request", line);
    562         req->open = FALSE; /* closes connection */
    563         return 1; /* done */
    564       }
    565       if(req->skipall)
    566         req->cl = 0;
    567       else
    568         req->cl = (size_t)clen - req->skip;
    569 
    570       logmsg("Found Content-Length: %zu in the request", (size_t)clen);
    571       if(req->skip)
    572         logmsg("... but will abort after %zu bytes", req->cl);
    573     }
    574     else if(!CURL_STRNICMP("Transfer-Encoding: chunked", line,
    575                            strlen("Transfer-Encoding: chunked"))) {
    576       /* chunked data coming in */
    577       chunked = TRUE;
    578     }
    579     else if(req->noexpect && !CURL_STRNICMP("Expect: 100-continue", line,
    580                                             strlen("Expect: 100-continue"))) {
    581       if(req->cl)
    582         req->cl = 0;
    583       req->skipall = TRUE;
    584       logmsg("Found Expect: 100-continue, ignore body");
    585     }
    586 
    587     if(chunked) {
    588       if(strstr(req->reqbuf, "\r\n0\r\n\r\n")) {
    589         /* end of chunks reached */
    590         return 1; /* done */
    591       }
    592       else if(strstr(req->reqbuf, "\r\n0\r\n")) {
    593         char *last_crlf_char = strstr(req->reqbuf, "\r\n\r\n");
    594         while(TRUE) {
    595           if(!strstr(last_crlf_char + 4, "\r\n\r\n"))
    596             break;
    597           last_crlf_char = strstr(last_crlf_char + 4, "\r\n\r\n");
    598         }
    599         if(last_crlf_char &&
    600            last_crlf_char > strstr(req->reqbuf, "\r\n0\r\n"))
    601           return 1;
    602         already_recv_zeroed_chunk = TRUE;
    603         return 0;
    604       }
    605       else if(already_recv_zeroed_chunk && strstr(req->reqbuf, "\r\n\r\n"))
    606         return 1;
    607       else
    608         return 0; /* not done */
    609     }
    610 
    611     line = strchr(line, '\n');
    612     if(line)
    613       line++;
    614 
    615   } while(line);
    616 
    617   if(!req->auth && strstr(req->reqbuf, "Authorization:")) {
    618     req->auth = TRUE; /* Authorization: header present! */
    619     if(req->auth_req)
    620       logmsg("Authorization header found, as required");
    621   }
    622 
    623   if(strstr(req->reqbuf, "Authorization: Negotiate")) {
    624     /* Negotiate iterations */
    625     static long prev_testno = -1;
    626     static long prev_partno = -1;
    627     logmsg("Negotiate: prev_testno: %ld, prev_partno: %ld",
    628            prev_testno, prev_partno);
    629     if(req->testno != prev_testno) {
    630       prev_testno = req->testno;
    631       prev_partno = req->partno;
    632     }
    633     prev_partno += 1;
    634     req->partno = prev_partno;
    635   }
    636   else if(!req->digest && strstr(req->reqbuf, "Authorization: Digest")) {
    637     /* If the client is passing this Digest-header, we set the part number
    638        to 1000. Not only to spice up the complexity of this, but to make
    639        Digest stuff to work in the test suite. */
    640     req->partno += 1000;
    641     req->digest = TRUE; /* header found */
    642     logmsg("Received Digest request, sending back data %ld", req->partno);
    643   }
    644   else if(!req->ntlm &&
    645           strstr(req->reqbuf, "Authorization: NTLM TlRMTVNTUAAD")) {
    646     /* If the client is passing this type-3 NTLM header */
    647     req->partno += 1002;
    648     req->ntlm = TRUE; /* NTLM found */
    649     logmsg("Received NTLM type-3, sending back data %ld", req->partno);
    650     if(req->cl) {
    651       logmsg("  Expecting %zu POSTed bytes", req->cl);
    652     }
    653   }
    654   else if(!req->ntlm &&
    655           strstr(req->reqbuf, "Authorization: NTLM TlRMTVNTUAAB")) {
    656     /* If the client is passing this type-1 NTLM header */
    657     req->partno += 1001;
    658     req->ntlm = TRUE; /* NTLM found */
    659     logmsg("Received NTLM type-1, sending back data %ld", req->partno);
    660   }
    661   else if((req->partno >= 1000) &&
    662           strstr(req->reqbuf, "Authorization: Basic")) {
    663     /* If the client is passing this Basic-header and the part number is
    664        already >=1000, we add 1 to the part number.  This allows simple Basic
    665        authentication negotiation to work in the test suite. */
    666     req->partno += 1;
    667     logmsg("Received Basic request, sending back data %ld", req->partno);
    668   }
    669   if(strstr(req->reqbuf, "Connection: close"))
    670     req->open = FALSE; /* close connection after this request */
    671 
    672   if(req->open &&
    673      req->prot_version >= 11 &&
    674      req->reqbuf + req->offset > end + strlen(end_of_headers) &&
    675      !req->cl &&
    676      (!strncmp(req->reqbuf, "GET", strlen("GET")) ||
    677       !strncmp(req->reqbuf, "HEAD", strlen("HEAD")))) {
    678     /* If we have a persistent connection, HTTP version >= 1.1
    679        and GET/HEAD request, enable pipelining. */
    680     req->checkindex = (end - req->reqbuf) + strlen(end_of_headers);
    681   }
    682 
    683   /* If authentication is required and no auth was provided, end now. This
    684      makes the server NOT wait for PUT/POST data and you can then make the
    685      test case send a rejection before any such data has been sent. Test case
    686      154 uses this.*/
    687   if(req->auth_req && !req->auth) {
    688     logmsg("Return early due to auth requested by none provided");
    689     return 1; /* done */
    690   }
    691 
    692   if(req->upgrade && strstr(req->reqbuf, "Upgrade:")) {
    693     /* we allow upgrade and there was one! */
    694     logmsg("Found Upgrade: in request and allow it");
    695     req->upgrade_request = TRUE;
    696     return 0; /* not done */
    697   }
    698 
    699   if(req->cl > 0) {
    700     if(req->cl <= req->offset - (end - req->reqbuf) - strlen(end_of_headers))
    701       return 1; /* done */
    702     else
    703       return 0; /* not complete yet */
    704   }
    705 
    706   return 1; /* done */
    707 }
    708 
    709 /* store the entire request in a file */
    710 static void sws_storerequest(const char *reqbuf, size_t totalsize)
    711 {
    712   int res;
    713   int error = 0;
    714   size_t written;
    715   size_t writeleft;
    716   FILE *dump;
    717   char dumpfile[256];
    718 
    719   snprintf(dumpfile, sizeof(dumpfile), "%s/%s",
    720            logdir, is_proxy ? REQUEST_PROXY_DUMP : REQUEST_DUMP);
    721 
    722   if(!reqbuf)
    723     return;
    724   if(totalsize == 0)
    725     return;
    726 
    727   do {
    728     dump = fopen(dumpfile, "ab");
    729     /* !checksrc! disable ERRNOVAR 1 */
    730   } while(!dump && ((error = errno) == EINTR));
    731   if(!dump) {
    732     logmsg("[2] Error opening file %s error (%d) %s",
    733            dumpfile, error, strerror(error));
    734     logmsg("Failed to write request input ");
    735     return;
    736   }
    737 
    738   writeleft = totalsize;
    739   do {
    740     written = fwrite(&reqbuf[totalsize-writeleft], 1, writeleft, dump);
    741     if(got_exit_signal)
    742       goto storerequest_cleanup;
    743     if(written > 0)
    744       writeleft -= written;
    745     error = errno;
    746     /* !checksrc! disable ERRNOVAR 1 */
    747   } while((writeleft > 0) && (error == EINTR));
    748 
    749   if(writeleft == 0)
    750     logmsg("Wrote request (%zu bytes) input to %s", totalsize, dumpfile);
    751   else if(writeleft > 0) {
    752     logmsg("Error writing file %s error (%d) %s",
    753            dumpfile, error, strerror(error));
    754     logmsg("Wrote only (%zu bytes) of (%zu bytes) request input to %s",
    755            totalsize-writeleft, totalsize, dumpfile);
    756   }
    757 
    758 storerequest_cleanup:
    759 
    760   res = fclose(dump);
    761   if(res)
    762     logmsg("Error closing file %s error (%d) %s",
    763            dumpfile, errno, strerror(errno));
    764 }
    765 
    766 static void init_httprequest(struct sws_httprequest *req)
    767 {
    768   req->checkindex = 0;
    769   req->offset = 0;
    770   req->testno = DOCNUMBER_NOTHING;
    771   req->partno = 0;
    772   req->connect_request = FALSE;
    773   req->open = TRUE;
    774   req->auth_req = FALSE;
    775   req->auth = FALSE;
    776   req->cl = 0;
    777   req->digest = FALSE;
    778   req->ntlm = FALSE;
    779   req->skip = 0;
    780   req->skipall = FALSE;
    781   req->noexpect = FALSE;
    782   req->delay = 0;
    783   req->writedelay = 0;
    784   req->rcmd = RCMD_NORMALREQ;
    785   req->prot_version = 0;
    786   req->callcount = 0;
    787   req->connect_port = 0;
    788   req->done_processing = 0;
    789   req->upgrade = 0;
    790   req->upgrade_request = 0;
    791 }
    792 
    793 static int sws_send_doc(curl_socket_t sock, struct sws_httprequest *req);
    794 
    795 /* returns 1 if the connection should be serviced again immediately, 0 if there
    796    is no data waiting, or < 0 if it should be closed */
    797 static int sws_get_request(curl_socket_t sock, struct sws_httprequest *req)
    798 {
    799   int fail = 0;
    800   char *reqbuf = req->reqbuf;
    801   ssize_t got = 0;
    802   int overflow = 0;
    803 
    804   if(req->upgrade_request) {
    805     /* upgraded connection, work it differently until end of connection */
    806     logmsg("Upgraded connection, this is no longer HTTP/1");
    807     sws_send_doc(sock, req);
    808 
    809     /* dump the request received so far to the external file */
    810     reqbuf[req->offset] = '\0';
    811     sws_storerequest(reqbuf, req->offset);
    812     req->offset = 0;
    813 
    814     /* read websocket traffic */
    815     if(req->open) {
    816       logmsg("wait for websocket traffic");
    817       do {
    818         got = sread(sock, reqbuf + req->offset, REQBUFSIZ - req->offset);
    819         if(got > 0) {
    820           req->offset += got;
    821           logmsg("Got %zu bytes from client", got);
    822         }
    823 
    824         if((got == -1) &&
    825            ((SOCKERRNO == EAGAIN) || (SOCKERRNO == SOCKEWOULDBLOCK))) {
    826           int rc;
    827           fd_set input;
    828           fd_set output;
    829           struct timeval timeout = {0};
    830           timeout.tv_sec = 1; /* 1000 ms */
    831 
    832           logmsg("Got EAGAIN from sread");
    833           FD_ZERO(&input);
    834           FD_ZERO(&output);
    835           got = 0;
    836 #if defined(__DJGPP__)
    837 #pragma GCC diagnostic push
    838 #pragma GCC diagnostic ignored "-Warith-conversion"
    839 #endif
    840           FD_SET(sock, &input);
    841 #if defined(__DJGPP__)
    842 #pragma GCC diagnostic pop
    843 #endif
    844           do {
    845             logmsg("Wait until readable");
    846             rc = select((int)sock + 1, &input, &output, NULL, &timeout);
    847           } while(rc < 0 && SOCKERRNO == SOCKEINTR && !got_exit_signal);
    848           logmsg("readable %d", rc);
    849           if(rc)
    850             got = 1;
    851         }
    852       } while(got > 0);
    853     }
    854     else {
    855       logmsg("NO wait for websocket traffic");
    856     }
    857     if(req->offset) {
    858       logmsg("log the websocket traffic");
    859       /* dump the incoming websocket traffic to the external file */
    860       reqbuf[req->offset] = '\0';
    861       sws_storerequest(reqbuf, req->offset);
    862       req->offset = 0;
    863     }
    864     init_httprequest(req);
    865 
    866     return -1;
    867   }
    868 
    869   if(req->offset >= REQBUFSIZ-1) {
    870     /* buffer is already full; do nothing */
    871     overflow = 1;
    872   }
    873   else {
    874     if(req->skip)
    875       /* we are instructed to not read the entire thing, so we make sure to
    876          only read what we're supposed to and NOT read the entire thing the
    877          client wants to send! */
    878       got = sread(sock, reqbuf + req->offset, req->cl);
    879     else
    880       got = sread(sock, reqbuf + req->offset, REQBUFSIZ-1 - req->offset);
    881 
    882     if(got_exit_signal)
    883       return -1;
    884     if(got == 0) {
    885       logmsg("Connection closed by client");
    886       fail = 1;
    887     }
    888     else if(got < 0) {
    889       int error = SOCKERRNO;
    890       if(EAGAIN == error || SOCKEWOULDBLOCK == error) {
    891         /* nothing to read at the moment */
    892         return 0;
    893       }
    894       logmsg("recv() returned error (%d) %s", error, sstrerror(error));
    895       fail = 1;
    896     }
    897     if(fail) {
    898       /* dump the request received so far to the external file */
    899       reqbuf[req->offset] = '\0';
    900       sws_storerequest(reqbuf, req->offset);
    901       return -1;
    902     }
    903 
    904     logmsg("Read %zd bytes", got);
    905 
    906     req->offset += (size_t)got;
    907     reqbuf[req->offset] = '\0';
    908 
    909     req->done_processing = sws_ProcessRequest(req);
    910     if(got_exit_signal)
    911       return -1;
    912   }
    913 
    914   if(overflow || (req->offset == REQBUFSIZ-1 && got > 0)) {
    915     logmsg("Request would overflow buffer, closing connection");
    916     /* dump request received so far to external file anyway */
    917     reqbuf[REQBUFSIZ-1] = '\0';
    918     fail = 1;
    919   }
    920   else if(req->offset > REQBUFSIZ-1) {
    921     logmsg("Request buffer overflow, closing connection");
    922     /* dump request received so far to external file anyway */
    923     reqbuf[REQBUFSIZ-1] = '\0';
    924     fail = 1;
    925   }
    926   else
    927     reqbuf[req->offset] = '\0';
    928 
    929   /* at the end of a request dump it to an external file */
    930   if(fail || req->done_processing)
    931     sws_storerequest(reqbuf, req->offset);
    932   if(got_exit_signal)
    933     return -1;
    934 
    935   return fail ? -1 : 1;
    936 }
    937 
    938 /* returns -1 on failure */
    939 static int sws_send_doc(curl_socket_t sock, struct sws_httprequest *req)
    940 {
    941   ssize_t written;
    942   size_t count;
    943   const char *buffer;
    944   char *ptr = NULL;
    945   FILE *stream;
    946   char *cmd = NULL;
    947   size_t cmdsize = 0;
    948   FILE *dump;
    949   bool persistent = TRUE;
    950   bool sendfailure = FALSE;
    951   size_t responsesize;
    952   int error = 0;
    953   int res;
    954   static char weare[256];
    955   char responsedump[256];
    956 
    957   snprintf(responsedump, sizeof(responsedump), "%s/%s",
    958            logdir, is_proxy ? RESPONSE_PROXY_DUMP : RESPONSE_DUMP);
    959 
    960   switch(req->rcmd) {
    961   default:
    962   case RCMD_NORMALREQ:
    963     break; /* continue with business as usual */
    964   case RCMD_STREAM:
    965 #define STREAMTHIS "a string to stream 01234567890\n"
    966     count = strlen(STREAMTHIS);
    967     for(;;) {
    968       written = swrite(sock, STREAMTHIS, count);
    969       if(got_exit_signal)
    970         return -1;
    971       if(written != (ssize_t)count) {
    972         logmsg("Stopped streaming");
    973         break;
    974       }
    975     }
    976     return -1;
    977   case RCMD_IDLE:
    978     /* Do nothing. Sit idle. Pretend it rains. */
    979     return 0;
    980   }
    981 
    982   req->open = FALSE;
    983 
    984   if(req->testno < 0) {
    985     size_t msglen;
    986     char msgbuf[64];
    987 
    988     switch(req->testno) {
    989     case DOCNUMBER_QUIT:
    990       logmsg("Replying to QUIT");
    991       buffer = docquit_sws;
    992       break;
    993     case DOCNUMBER_WERULEZ:
    994       /* we got a "friends?" question, reply back that we sure are */
    995       logmsg("Identifying ourselves as friends");
    996       snprintf(msgbuf, sizeof(msgbuf), "WE ROOLZ: %ld\r\n",
    997                (long)our_getpid());
    998       msglen = strlen(msgbuf);
    999       if(use_gopher)
   1000         snprintf(weare, sizeof(weare), "%s", msgbuf);
   1001       else
   1002         snprintf(weare, sizeof(weare),
   1003                  "HTTP/1.1 200 OK\r\nContent-Length: %u\r\n\r\n%s",
   1004                  (unsigned int)msglen, msgbuf);
   1005       buffer = weare;
   1006       break;
   1007     case DOCNUMBER_404:
   1008     default:
   1009       logmsg("Replying to with a 404");
   1010       buffer = doc404;
   1011       break;
   1012     }
   1013 
   1014     count = strlen(buffer);
   1015   }
   1016   else {
   1017     char partbuf[80];
   1018 
   1019     /* select the <data> tag for "normal" requests and the <connect> one
   1020        for CONNECT requests (within the <reply> section) */
   1021     const char *section = req->connect_request ? "connect" : "data";
   1022 
   1023     if(req->partno)
   1024       snprintf(partbuf, sizeof(partbuf), "%s%ld", section, req->partno);
   1025     else
   1026       snprintf(partbuf, sizeof(partbuf), "%s", section);
   1027 
   1028     logmsg("Send response test%ld section <%s>", req->testno, partbuf);
   1029 
   1030     stream = test2fopen(req->testno, logdir);
   1031     if(!stream) {
   1032       error = errno;
   1033       logmsg("fopen() failed with error (%d) %s", error, strerror(error));
   1034       return 0;
   1035     }
   1036     else {
   1037       error = getpart(&ptr, &count, "reply", partbuf, stream);
   1038       fclose(stream);
   1039       if(error) {
   1040         logmsg("getpart() failed with error (%d)", error);
   1041         return 0;
   1042       }
   1043       buffer = ptr;
   1044     }
   1045 
   1046     if(got_exit_signal) {
   1047       free(ptr);
   1048       return -1;
   1049     }
   1050 
   1051     /* re-open the same file again */
   1052     stream = test2fopen(req->testno, logdir);
   1053     if(!stream) {
   1054       error = errno;
   1055       logmsg("fopen() failed with error (%d) %s", error, strerror(error));
   1056       free(ptr);
   1057       return 0;
   1058     }
   1059     else {
   1060       /* get the custom server control "commands" */
   1061       error = getpart(&cmd, &cmdsize, "reply", "postcmd", stream);
   1062       fclose(stream);
   1063       if(error) {
   1064         logmsg("getpart() failed with error (%d)", error);
   1065         free(ptr);
   1066         return 0;
   1067       }
   1068     }
   1069   }
   1070 
   1071   if(got_exit_signal) {
   1072     free(ptr);
   1073     free(cmd);
   1074     return -1;
   1075   }
   1076 
   1077   /* If the word 'swsclose' is present anywhere in the reply chunk, the
   1078      connection will be closed after the data has been sent to the requesting
   1079      client... */
   1080   if(strstr(buffer, "swsclose") || !count || req->close) {
   1081     persistent = FALSE;
   1082     logmsg("connection close instruction \"swsclose\" found in response");
   1083   }
   1084   if(strstr(buffer, "swsbounce")) {
   1085     sws_prevbounce = TRUE;
   1086     logmsg("enable \"swsbounce\" in the next request");
   1087   }
   1088   else
   1089     sws_prevbounce = FALSE;
   1090 
   1091   dump = fopen(responsedump, "ab");
   1092   if(!dump) {
   1093     error = errno;
   1094     logmsg("fopen() failed with error (%d) %s", error, strerror(error));
   1095     logmsg("  [5] Error opening file '%s'", responsedump);
   1096     free(ptr);
   1097     free(cmd);
   1098     return -1;
   1099   }
   1100 
   1101   responsesize = count;
   1102   do {
   1103     /* Ok, we send no more than N bytes at a time, just to make sure that
   1104        larger chunks are split up so that the client will need to do multiple
   1105        recv() calls to get it and thus we exercise that code better */
   1106     size_t num = count;
   1107     if(num > 20)
   1108       num = 20;
   1109 
   1110 retry:
   1111     written = swrite(sock, buffer, num);
   1112     if(written < 0) {
   1113       if((SOCKEWOULDBLOCK == SOCKERRNO) || (EAGAIN == SOCKERRNO)) {
   1114         curlx_wait_ms(10);
   1115         goto retry;
   1116       }
   1117       sendfailure = TRUE;
   1118       break;
   1119     }
   1120 
   1121     /* write to file as well */
   1122     fwrite(buffer, 1, (size_t)written, dump);
   1123 
   1124     count -= written;
   1125     buffer += written;
   1126 
   1127     if(req->writedelay) {
   1128       int msecs_left = req->writedelay;
   1129       int intervals = msecs_left / MAX_SLEEP_TIME_MS;
   1130       if(msecs_left%MAX_SLEEP_TIME_MS)
   1131         intervals++;
   1132       logmsg("Pausing %d milliseconds after writing %zd bytes",
   1133              msecs_left, written);
   1134       while((intervals > 0) && !got_exit_signal) {
   1135         int sleep_time = msecs_left > MAX_SLEEP_TIME_MS ?
   1136           MAX_SLEEP_TIME_MS : msecs_left;
   1137         intervals--;
   1138         curlx_wait_ms(sleep_time);
   1139         msecs_left -= sleep_time;
   1140       }
   1141     }
   1142   } while((count > 0) && !got_exit_signal);
   1143 
   1144   res = fclose(dump);
   1145   if(res)
   1146     logmsg("Error closing file %s error (%d) %s",
   1147            responsedump, errno, strerror(errno));
   1148 
   1149   if(got_exit_signal) {
   1150     free(ptr);
   1151     free(cmd);
   1152     return -1;
   1153   }
   1154 
   1155   if(sendfailure) {
   1156     logmsg("Sending response failed. Only (%zu bytes) of (%zu bytes) "
   1157            "were sent",
   1158            responsesize-count, responsesize);
   1159     sws_prevtestno = req->testno;
   1160     sws_prevpartno = req->partno;
   1161     free(ptr);
   1162     free(cmd);
   1163     return -1;
   1164   }
   1165 
   1166   logmsg("Response sent (%zu bytes) and written to %s",
   1167          responsesize, responsedump);
   1168   free(ptr);
   1169 
   1170   if(cmdsize > 0) {
   1171     char command[32];
   1172     int quarters;
   1173     int num;
   1174     ptr = cmd;
   1175     do {
   1176       if(2 == sscanf(ptr, "%31s %d", command, &num)) {
   1177         if(!strcmp("wait", command)) {
   1178           logmsg("Told to sleep for %d seconds", num);
   1179           quarters = num * 4;
   1180           while((quarters > 0) && !got_exit_signal) {
   1181             quarters--;
   1182             res = curlx_wait_ms(250);
   1183             if(res) {
   1184               /* should not happen */
   1185               error = SOCKERRNO;
   1186               logmsg("curlx_wait_ms() failed with error (%d) %s",
   1187                      error, sstrerror(error));
   1188               break;
   1189             }
   1190           }
   1191           if(!quarters)
   1192             logmsg("Continuing after sleeping %d seconds", num);
   1193         }
   1194         else
   1195           logmsg("Unknown command in reply command section");
   1196       }
   1197       ptr = strchr(ptr, '\n');
   1198       if(ptr)
   1199         ptr++;
   1200       else
   1201         ptr = NULL;
   1202     } while(ptr && *ptr);
   1203   }
   1204   free(cmd);
   1205   req->open = use_gopher ? FALSE : persistent;
   1206 
   1207   sws_prevtestno = req->testno;
   1208   sws_prevpartno = req->partno;
   1209 
   1210   return 0;
   1211 }
   1212 
   1213 static curl_socket_t connect_to(const char *ipaddr, unsigned short port)
   1214 {
   1215   srvr_sockaddr_union_t serveraddr;
   1216   curl_socket_t serverfd;
   1217   int error;
   1218   int rc = 0;
   1219   const char *op_br = "";
   1220   const char *cl_br = "";
   1221 
   1222 #ifdef USE_IPV6
   1223   if(socket_domain == AF_INET6) {
   1224     op_br = "[";
   1225     cl_br = "]";
   1226   }
   1227 #endif
   1228 
   1229   if(!ipaddr)
   1230     return CURL_SOCKET_BAD;
   1231 
   1232   logmsg("about to connect to %s%s%s:%hu",
   1233          op_br, ipaddr, cl_br, port);
   1234 
   1235 
   1236   serverfd = socket(socket_domain, SOCK_STREAM, 0);
   1237   if(CURL_SOCKET_BAD == serverfd) {
   1238     error = SOCKERRNO;
   1239     logmsg("Error creating socket for server connection (%d) %s",
   1240            error, sstrerror(error));
   1241     return CURL_SOCKET_BAD;
   1242   }
   1243 
   1244 #ifdef TCP_NODELAY
   1245   if(socket_domain_is_ip()) {
   1246     /* Disable the Nagle algorithm */
   1247     curl_socklen_t flag = 1;
   1248     if(0 != setsockopt(serverfd, IPPROTO_TCP, TCP_NODELAY,
   1249                        (void *)&flag, sizeof(flag)))
   1250       logmsg("====> TCP_NODELAY for server connection failed");
   1251   }
   1252 #endif
   1253 
   1254   /* We want to do the connect() in a non-blocking mode, since
   1255    * Windows has an internal retry logic that may lead to long
   1256    * timeouts if the peer is not listening. */
   1257   if(0 != curlx_nonblock(serverfd, TRUE)) {
   1258     error = SOCKERRNO;
   1259     logmsg("curlx_nonblock(TRUE) failed with error (%d) %s",
   1260            error, sstrerror(error));
   1261     sclose(serverfd);
   1262     return CURL_SOCKET_BAD;
   1263   }
   1264 
   1265   switch(socket_domain) {
   1266   case AF_INET:
   1267     memset(&serveraddr.sa4, 0, sizeof(serveraddr.sa4));
   1268     serveraddr.sa4.sin_family = AF_INET;
   1269     serveraddr.sa4.sin_port = htons(port);
   1270     if(curlx_inet_pton(AF_INET, ipaddr, &serveraddr.sa4.sin_addr) < 1) {
   1271       logmsg("Error inet_pton failed AF_INET conversion of '%s'", ipaddr);
   1272       sclose(serverfd);
   1273       return CURL_SOCKET_BAD;
   1274     }
   1275 
   1276     rc = connect(serverfd, &serveraddr.sa, sizeof(serveraddr.sa4));
   1277     break;
   1278 #ifdef USE_IPV6
   1279   case AF_INET6:
   1280     memset(&serveraddr.sa6, 0, sizeof(serveraddr.sa6));
   1281     serveraddr.sa6.sin6_family = AF_INET6;
   1282     serveraddr.sa6.sin6_port = htons(port);
   1283     if(curlx_inet_pton(AF_INET6, ipaddr, &serveraddr.sa6.sin6_addr) < 1) {
   1284       logmsg("Error inet_pton failed AF_INET6 conversion of '%s'", ipaddr);
   1285       sclose(serverfd);
   1286       return CURL_SOCKET_BAD;
   1287     }
   1288 
   1289     rc = connect(serverfd, &serveraddr.sa, sizeof(serveraddr.sa6));
   1290     break;
   1291 #endif /* USE_IPV6 */
   1292 #ifdef USE_UNIX_SOCKETS
   1293   case AF_UNIX:
   1294     logmsg("Proxying through Unix socket is not (yet?) supported.");
   1295     return CURL_SOCKET_BAD;
   1296 #endif /* USE_UNIX_SOCKETS */
   1297   }
   1298 
   1299   if(got_exit_signal) {
   1300     sclose(serverfd);
   1301     return CURL_SOCKET_BAD;
   1302   }
   1303 
   1304   if(rc) {
   1305     error = SOCKERRNO;
   1306     if((error == SOCKEINPROGRESS) || (error == SOCKEWOULDBLOCK)) {
   1307       fd_set output;
   1308       struct timeval timeout = {0};
   1309       timeout.tv_sec = 1; /* 1000 ms */
   1310 
   1311       FD_ZERO(&output);
   1312 #if defined(__DJGPP__)
   1313 #pragma GCC diagnostic push
   1314 #pragma GCC diagnostic ignored "-Warith-conversion"
   1315 #endif
   1316       FD_SET(serverfd, &output);
   1317 #if defined(__DJGPP__)
   1318 #pragma GCC diagnostic pop
   1319 #endif
   1320       while(1) {
   1321         rc = select((int)serverfd + 1, NULL, &output, NULL, &timeout);
   1322         if(rc < 0 && SOCKERRNO != SOCKEINTR)
   1323           goto error;
   1324         else if(rc > 0) {
   1325           curl_socklen_t errSize = sizeof(error);
   1326           if(0 != getsockopt(serverfd, SOL_SOCKET, SO_ERROR,
   1327                              (void *)&error, &errSize))
   1328             error = SOCKERRNO;
   1329           if((0 == error) || (SOCKEISCONN == error))
   1330             goto success;
   1331           else if((error != SOCKEINPROGRESS) && (error != SOCKEWOULDBLOCK))
   1332             goto error;
   1333         }
   1334         else if(!rc) {
   1335           logmsg("Timeout connecting to server port %hu", port);
   1336           sclose(serverfd);
   1337           return CURL_SOCKET_BAD;
   1338         }
   1339       }
   1340     }
   1341 error:
   1342     logmsg("Error connecting to server port %hu (%d) %s",
   1343            port, error, sstrerror(error));
   1344     sclose(serverfd);
   1345     return CURL_SOCKET_BAD;
   1346   }
   1347 success:
   1348   logmsg("connected fine to %s%s%s:%hu, now tunnel",
   1349          op_br, ipaddr, cl_br, port);
   1350 
   1351   if(0 != curlx_nonblock(serverfd, FALSE)) {
   1352     error = SOCKERRNO;
   1353     logmsg("curlx_nonblock(FALSE) failed with error (%d) %s",
   1354            error, sstrerror(error));
   1355     sclose(serverfd);
   1356     return CURL_SOCKET_BAD;
   1357   }
   1358 
   1359   return serverfd;
   1360 }
   1361 
   1362 /*
   1363  * A CONNECT has been received, a CONNECT response has been sent.
   1364  *
   1365  * This function needs to connect to the server, and then pass data between
   1366  * the client and the server back and forth until the connection is closed by
   1367  * either end.
   1368  *
   1369  * When doing FTP through a CONNECT proxy, we expect that the data connection
   1370  * will be setup while the first connect is still being kept up. Therefore we
   1371  * must accept a new connection and deal with it appropriately.
   1372  */
   1373 
   1374 #define data_or_ctrl(x) ((x)?"DATA":"CTRL")
   1375 
   1376 #define SWS_CTRL  0
   1377 #define SWS_DATA  1
   1378 
   1379 static void http_connect(curl_socket_t *infdp,
   1380                          curl_socket_t rootfd,
   1381                          const char *ipaddr,
   1382                          unsigned short ipport,
   1383                          int keepalive_secs)
   1384 {
   1385   curl_socket_t serverfd[2] = {CURL_SOCKET_BAD, CURL_SOCKET_BAD};
   1386   curl_socket_t clientfd[2] = {CURL_SOCKET_BAD, CURL_SOCKET_BAD};
   1387   ssize_t toc[2] = {0, 0}; /* number of bytes to client */
   1388   ssize_t tos[2] = {0, 0}; /* number of bytes to server */
   1389   char readclient[2][256];
   1390   char readserver[2][256];
   1391   bool poll_client_rd[2] = { TRUE, TRUE };
   1392   bool poll_server_rd[2] = { TRUE, TRUE };
   1393   bool poll_client_wr[2] = { TRUE, TRUE };
   1394   bool poll_server_wr[2] = { TRUE, TRUE };
   1395   bool primary = FALSE;
   1396   bool secondary = FALSE;
   1397   int max_tunnel_idx; /* SWS_CTRL or SWS_DATA */
   1398   int loop;
   1399   int i;
   1400   int timeout_count = 0;
   1401 
   1402   /* primary tunnel client endpoint already connected */
   1403   clientfd[SWS_CTRL] = *infdp;
   1404 
   1405   /* Sleep here to make sure the client reads CONNECT response's
   1406      'end of headers' separate from the server data that follows.
   1407      This is done to prevent triggering libcurl known bug #39. */
   1408   for(loop = 2; (loop > 0) && !got_exit_signal; loop--)
   1409     curlx_wait_ms(250);
   1410   if(got_exit_signal)
   1411     goto http_connect_cleanup;
   1412 
   1413   serverfd[SWS_CTRL] = connect_to(ipaddr, ipport);
   1414   if(serverfd[SWS_CTRL] == CURL_SOCKET_BAD)
   1415     goto http_connect_cleanup;
   1416 
   1417   /* Primary tunnel socket endpoints are now connected. Tunnel data back and
   1418      forth over the primary tunnel until client or server breaks the primary
   1419      tunnel, simultaneously allowing establishment, operation and teardown of
   1420      a secondary tunnel that may be used for passive FTP data connection. */
   1421 
   1422   max_tunnel_idx = SWS_CTRL;
   1423   primary = TRUE;
   1424 
   1425   while(!got_exit_signal) {
   1426 
   1427     fd_set input;
   1428     fd_set output;
   1429     ssize_t rc;
   1430     curl_socket_t maxfd = (curl_socket_t)-1;
   1431     struct timeval timeout = {0};
   1432     timeout.tv_sec = 1; /* 1000 ms */
   1433 
   1434     FD_ZERO(&input);
   1435     FD_ZERO(&output);
   1436 
   1437     if((clientfd[SWS_DATA] == CURL_SOCKET_BAD) &&
   1438        (serverfd[SWS_DATA] == CURL_SOCKET_BAD) &&
   1439        poll_client_rd[SWS_CTRL] && poll_client_wr[SWS_CTRL] &&
   1440        poll_server_rd[SWS_CTRL] && poll_server_wr[SWS_CTRL]) {
   1441       /* listener socket is monitored to allow client to establish
   1442          secondary tunnel only when this tunnel is not established
   1443          and primary one is fully operational */
   1444 #if defined(__DJGPP__)
   1445 #pragma GCC diagnostic push
   1446 #pragma GCC diagnostic ignored "-Warith-conversion"
   1447 #endif
   1448       FD_SET(rootfd, &input);
   1449 #if defined(__DJGPP__)
   1450 #pragma GCC diagnostic pop
   1451 #endif
   1452       maxfd = rootfd;
   1453     }
   1454 
   1455     /* set tunnel sockets to wait for */
   1456     for(i = 0; i <= max_tunnel_idx; i++) {
   1457       /* client side socket monitoring */
   1458       if(clientfd[i] != CURL_SOCKET_BAD) {
   1459         if(poll_client_rd[i]) {
   1460           /* unless told not to do so, monitor readability */
   1461 #if defined(__DJGPP__)
   1462 #pragma GCC diagnostic push
   1463 #pragma GCC diagnostic ignored "-Warith-conversion"
   1464 #endif
   1465           FD_SET(clientfd[i], &input);
   1466 #if defined(__DJGPP__)
   1467 #pragma GCC diagnostic pop
   1468 #endif
   1469           if(clientfd[i] > maxfd)
   1470             maxfd = clientfd[i];
   1471         }
   1472         if(poll_client_wr[i] && toc[i]) {
   1473           /* unless told not to do so, monitor writability
   1474              if there is data ready to be sent to client */
   1475 #if defined(__DJGPP__)
   1476 #pragma GCC diagnostic push
   1477 #pragma GCC diagnostic ignored "-Warith-conversion"
   1478 #endif
   1479           FD_SET(clientfd[i], &output);
   1480 #if defined(__DJGPP__)
   1481 #pragma GCC diagnostic pop
   1482 #endif
   1483           if(clientfd[i] > maxfd)
   1484             maxfd = clientfd[i];
   1485         }
   1486       }
   1487       /* server side socket monitoring */
   1488       if(serverfd[i] != CURL_SOCKET_BAD) {
   1489         if(poll_server_rd[i]) {
   1490           /* unless told not to do so, monitor readability */
   1491 #if defined(__DJGPP__)
   1492 #pragma GCC diagnostic push
   1493 #pragma GCC diagnostic ignored "-Warith-conversion"
   1494 #endif
   1495           FD_SET(serverfd[i], &input);
   1496 #if defined(__DJGPP__)
   1497 #pragma GCC diagnostic pop
   1498 #endif
   1499           if(serverfd[i] > maxfd)
   1500             maxfd = serverfd[i];
   1501         }
   1502         if(poll_server_wr[i] && tos[i]) {
   1503           /* unless told not to do so, monitor writability
   1504              if there is data ready to be sent to server */
   1505 #if defined(__DJGPP__)
   1506 #pragma GCC diagnostic push
   1507 #pragma GCC diagnostic ignored "-Warith-conversion"
   1508 #endif
   1509           FD_SET(serverfd[i], &output);
   1510 #if defined(__DJGPP__)
   1511 #pragma GCC diagnostic pop
   1512 #endif
   1513           if(serverfd[i] > maxfd)
   1514             maxfd = serverfd[i];
   1515         }
   1516       }
   1517     }
   1518     if(got_exit_signal)
   1519       break;
   1520 
   1521     do {
   1522       rc = select((int)maxfd + 1, &input, &output, NULL, &timeout);
   1523     } while(rc < 0 && SOCKERRNO == SOCKEINTR && !got_exit_signal);
   1524 
   1525     if(got_exit_signal)
   1526       break;
   1527 
   1528     if(rc > 0) {
   1529       /* socket action */
   1530       bool tcp_fin_wr = FALSE;
   1531       timeout_count = 0;
   1532 
   1533       /* ---------------------------------------------------------- */
   1534 
   1535       /* passive mode FTP may establish a secondary tunnel */
   1536       if((clientfd[SWS_DATA] == CURL_SOCKET_BAD) &&
   1537          (serverfd[SWS_DATA] == CURL_SOCKET_BAD) && FD_ISSET(rootfd, &input)) {
   1538         /* a new connection on listener socket (most likely from client) */
   1539         curl_socket_t datafd = accept(rootfd, NULL, NULL);
   1540         if(datafd != CURL_SOCKET_BAD) {
   1541           static struct sws_httprequest *req2;
   1542           int err = 0;
   1543           if(!req2) {
   1544             req2 = malloc(sizeof(*req2));
   1545             if(!req2)
   1546               goto http_connect_cleanup;  /* fail */
   1547           }
   1548           memset(req2, 0, sizeof(*req2));
   1549           logmsg("====> Client connect DATA");
   1550 #ifdef TCP_NODELAY
   1551           if(socket_domain_is_ip()) {
   1552             /* Disable the Nagle algorithm */
   1553             curl_socklen_t flag = 1;
   1554             if(0 != setsockopt(datafd, IPPROTO_TCP, TCP_NODELAY,
   1555                                (void *)&flag, sizeof(flag)))
   1556               logmsg("====> TCP_NODELAY for client DATA connection failed");
   1557           }
   1558 #endif
   1559           init_httprequest(req2);
   1560           while(!req2->done_processing) {
   1561             err = sws_get_request(datafd, req2);
   1562             if(err < 0) {
   1563               /* this socket must be closed, done or not */
   1564               break;
   1565             }
   1566           }
   1567 
   1568           /* skip this and close the socket if err < 0 */
   1569           if(err >= 0) {
   1570             err = sws_send_doc(datafd, req2);
   1571             if(!err && req2->connect_request) {
   1572               /* sleep to prevent triggering libcurl known bug #39. */
   1573               for(loop = 2; (loop > 0) && !got_exit_signal; loop--)
   1574                 curlx_wait_ms(250);
   1575               if(!got_exit_signal) {
   1576                 /* connect to the server */
   1577                 serverfd[SWS_DATA] = connect_to(ipaddr, req2->connect_port);
   1578                 if(serverfd[SWS_DATA] != CURL_SOCKET_BAD) {
   1579                   /* secondary tunnel established, now we have two
   1580                      connections */
   1581                   poll_client_rd[SWS_DATA] = TRUE;
   1582                   poll_client_wr[SWS_DATA] = TRUE;
   1583                   poll_server_rd[SWS_DATA] = TRUE;
   1584                   poll_server_wr[SWS_DATA] = TRUE;
   1585                   max_tunnel_idx = SWS_DATA;
   1586                   secondary = TRUE;
   1587                   toc[SWS_DATA] = 0;
   1588                   tos[SWS_DATA] = 0;
   1589                   clientfd[SWS_DATA] = datafd;
   1590                   datafd = CURL_SOCKET_BAD;
   1591                 }
   1592               }
   1593             }
   1594           }
   1595           if(datafd != CURL_SOCKET_BAD) {
   1596             /* secondary tunnel not established */
   1597             shutdown(datafd, SHUT_RDWR);
   1598             sclose(datafd);
   1599           }
   1600         }
   1601         if(got_exit_signal)
   1602           break;
   1603       }
   1604 
   1605       /* ---------------------------------------------------------- */
   1606 
   1607       /* react to tunnel endpoint readable/writable notifications */
   1608       for(i = 0; i <= max_tunnel_idx; i++) {
   1609         size_t len;
   1610         if(clientfd[i] != CURL_SOCKET_BAD) {
   1611           len = sizeof(readclient[i]) - tos[i];
   1612           if(len && FD_ISSET(clientfd[i], &input)) {
   1613             /* read from client */
   1614             rc = sread(clientfd[i], &readclient[i][tos[i]], len);
   1615             if(rc <= 0) {
   1616               logmsg("[%s] got %zd, STOP READING client", data_or_ctrl(i), rc);
   1617               shutdown(clientfd[i], SHUT_RD);
   1618               poll_client_rd[i] = FALSE;
   1619             }
   1620             else {
   1621               logmsg("[%s] READ %zd bytes from client", data_or_ctrl(i), rc);
   1622               logmsg("[%s] READ \"%s\"", data_or_ctrl(i),
   1623                      data_to_hex(&readclient[i][tos[i]], rc));
   1624               tos[i] += rc;
   1625             }
   1626           }
   1627         }
   1628         if(serverfd[i] != CURL_SOCKET_BAD) {
   1629           len = sizeof(readserver[i])-toc[i];
   1630           if(len && FD_ISSET(serverfd[i], &input)) {
   1631             /* read from server */
   1632             rc = sread(serverfd[i], &readserver[i][toc[i]], len);
   1633             if(rc <= 0) {
   1634               logmsg("[%s] got %zd, STOP READING server", data_or_ctrl(i), rc);
   1635               shutdown(serverfd[i], SHUT_RD);
   1636               poll_server_rd[i] = FALSE;
   1637             }
   1638             else {
   1639               logmsg("[%s] READ %zd bytes from server", data_or_ctrl(i), rc);
   1640               logmsg("[%s] READ \"%s\"", data_or_ctrl(i),
   1641                      data_to_hex(&readserver[i][toc[i]], rc));
   1642               toc[i] += rc;
   1643             }
   1644           }
   1645         }
   1646         if(clientfd[i] != CURL_SOCKET_BAD) {
   1647           if(toc[i] && FD_ISSET(clientfd[i], &output)) {
   1648             /* write to client */
   1649             rc = swrite(clientfd[i], readserver[i], toc[i]);
   1650             if(rc <= 0) {
   1651               logmsg("[%s] got %zd, STOP WRITING client", data_or_ctrl(i), rc);
   1652               shutdown(clientfd[i], SHUT_WR);
   1653               poll_client_wr[i] = FALSE;
   1654               tcp_fin_wr = TRUE;
   1655             }
   1656             else {
   1657               logmsg("[%s] SENT %zd bytes to client", data_or_ctrl(i), rc);
   1658               logmsg("[%s] SENT \"%s\"", data_or_ctrl(i),
   1659                      data_to_hex(readserver[i], rc));
   1660               if(toc[i] - rc)
   1661                 memmove(&readserver[i][0], &readserver[i][rc], toc[i]-rc);
   1662               toc[i] -= rc;
   1663             }
   1664           }
   1665         }
   1666         if(serverfd[i] != CURL_SOCKET_BAD) {
   1667           if(tos[i] && FD_ISSET(serverfd[i], &output)) {
   1668             /* write to server */
   1669             rc = swrite(serverfd[i], readclient[i], tos[i]);
   1670             if(rc <= 0) {
   1671               logmsg("[%s] got %zd, STOP WRITING server", data_or_ctrl(i), rc);
   1672               shutdown(serverfd[i], SHUT_WR);
   1673               poll_server_wr[i] = FALSE;
   1674               tcp_fin_wr = TRUE;
   1675             }
   1676             else {
   1677               logmsg("[%s] SENT %zd bytes to server", data_or_ctrl(i), rc);
   1678               logmsg("[%s] SENT \"%s\"", data_or_ctrl(i),
   1679                      data_to_hex(readclient[i], rc));
   1680               if(tos[i] - rc)
   1681                 memmove(&readclient[i][0], &readclient[i][rc], tos[i]-rc);
   1682               tos[i] -= rc;
   1683             }
   1684           }
   1685         }
   1686       }
   1687       if(got_exit_signal)
   1688         break;
   1689 
   1690       /* ---------------------------------------------------------- */
   1691 
   1692       /* endpoint read/write disabling, endpoint closing and tunnel teardown */
   1693       for(i = 0; i <= max_tunnel_idx; i++) {
   1694         for(loop = 2; loop > 0; loop--) {
   1695           /* loop twice to satisfy condition interdependencies without
   1696              having to await select timeout or another socket event */
   1697           if(clientfd[i] != CURL_SOCKET_BAD) {
   1698             if(poll_client_rd[i] && !poll_server_wr[i]) {
   1699               logmsg("[%s] DISABLED READING client", data_or_ctrl(i));
   1700               shutdown(clientfd[i], SHUT_RD);
   1701               poll_client_rd[i] = FALSE;
   1702             }
   1703             if(poll_client_wr[i] && !poll_server_rd[i] && !toc[i]) {
   1704               logmsg("[%s] DISABLED WRITING client", data_or_ctrl(i));
   1705               shutdown(clientfd[i], SHUT_WR);
   1706               poll_client_wr[i] = FALSE;
   1707               tcp_fin_wr = TRUE;
   1708             }
   1709           }
   1710           if(serverfd[i] != CURL_SOCKET_BAD) {
   1711             if(poll_server_rd[i] && !poll_client_wr[i]) {
   1712               logmsg("[%s] DISABLED READING server", data_or_ctrl(i));
   1713               shutdown(serverfd[i], SHUT_RD);
   1714               poll_server_rd[i] = FALSE;
   1715             }
   1716             if(poll_server_wr[i] && !poll_client_rd[i] && !tos[i]) {
   1717               logmsg("[%s] DISABLED WRITING server", data_or_ctrl(i));
   1718               shutdown(serverfd[i], SHUT_WR);
   1719               poll_server_wr[i] = FALSE;
   1720               tcp_fin_wr = TRUE;
   1721             }
   1722           }
   1723         }
   1724       }
   1725 
   1726       if(tcp_fin_wr)
   1727         /* allow kernel to place FIN bit packet on the wire */
   1728         curlx_wait_ms(250);
   1729 
   1730       /* socket clearing */
   1731       for(i = 0; i <= max_tunnel_idx; i++) {
   1732         for(loop = 2; loop > 0; loop--) {
   1733           if(clientfd[i] != CURL_SOCKET_BAD) {
   1734             if(!poll_client_wr[i] && !poll_client_rd[i]) {
   1735               logmsg("[%s] CLOSING client socket", data_or_ctrl(i));
   1736               sclose(clientfd[i]);
   1737               clientfd[i] = CURL_SOCKET_BAD;
   1738               if(serverfd[i] == CURL_SOCKET_BAD) {
   1739                 logmsg("[%s] ENDING", data_or_ctrl(i));
   1740                 if(i == SWS_DATA)
   1741                   secondary = FALSE;
   1742                 else
   1743                   primary = FALSE;
   1744               }
   1745             }
   1746           }
   1747           if(serverfd[i] != CURL_SOCKET_BAD) {
   1748             if(!poll_server_wr[i] && !poll_server_rd[i]) {
   1749               logmsg("[%s] CLOSING server socket", data_or_ctrl(i));
   1750               sclose(serverfd[i]);
   1751               serverfd[i] = CURL_SOCKET_BAD;
   1752               if(clientfd[i] == CURL_SOCKET_BAD) {
   1753                 logmsg("[%s] ENDING", data_or_ctrl(i));
   1754                 if(i == SWS_DATA)
   1755                   secondary = FALSE;
   1756                 else
   1757                   primary = FALSE;
   1758               }
   1759             }
   1760           }
   1761         }
   1762       }
   1763 
   1764       /* ---------------------------------------------------------- */
   1765 
   1766       max_tunnel_idx = secondary ? SWS_DATA : SWS_CTRL;
   1767 
   1768       if(!primary)
   1769         /* exit loop upon primary tunnel teardown */
   1770         break;
   1771 
   1772     } /* (rc > 0) */
   1773     else {
   1774       timeout_count++;
   1775       if(timeout_count > keepalive_secs) {
   1776         logmsg("CONNECT proxy timeout after %d idle seconds!", timeout_count);
   1777         break;
   1778       }
   1779     }
   1780   }
   1781 
   1782 http_connect_cleanup:
   1783 
   1784   for(i = SWS_DATA; i >= SWS_CTRL; i--) {
   1785     if(serverfd[i] != CURL_SOCKET_BAD) {
   1786       logmsg("[%s] CLOSING server socket (cleanup)", data_or_ctrl(i));
   1787       shutdown(serverfd[i], SHUT_RDWR);
   1788       sclose(serverfd[i]);
   1789     }
   1790     if(clientfd[i] != CURL_SOCKET_BAD) {
   1791       logmsg("[%s] CLOSING client socket (cleanup)", data_or_ctrl(i));
   1792       shutdown(clientfd[i], SHUT_RDWR);
   1793       sclose(clientfd[i]);
   1794     }
   1795     if((serverfd[i] != CURL_SOCKET_BAD) ||
   1796        (clientfd[i] != CURL_SOCKET_BAD)) {
   1797       logmsg("[%s] ABORTING", data_or_ctrl(i));
   1798     }
   1799   }
   1800 
   1801   *infdp = CURL_SOCKET_BAD;
   1802 }
   1803 
   1804 static void http_upgrade(struct sws_httprequest *req)
   1805 {
   1806   (void)req;
   1807   logmsg("Upgraded to ... %u", req->upgrade_request);
   1808   /* left to implement */
   1809 }
   1810 
   1811 
   1812 /* returns a socket handle, or 0 if there are no more waiting sockets,
   1813    or < 0 if there was an error */
   1814 static curl_socket_t accept_connection(curl_socket_t sock)
   1815 {
   1816   curl_socket_t msgsock = CURL_SOCKET_BAD;
   1817   int error;
   1818   int flag = 1;
   1819 
   1820   if(MAX_SOCKETS == num_sockets) {
   1821     logmsg("Too many open sockets!");
   1822     return CURL_SOCKET_BAD;
   1823   }
   1824 
   1825   msgsock = accept(sock, NULL, NULL);
   1826 
   1827   if(got_exit_signal) {
   1828     if(CURL_SOCKET_BAD != msgsock)
   1829       sclose(msgsock);
   1830     return CURL_SOCKET_BAD;
   1831   }
   1832 
   1833   if(CURL_SOCKET_BAD == msgsock) {
   1834     error = SOCKERRNO;
   1835     if(EAGAIN == error || SOCKEWOULDBLOCK == error) {
   1836       /* nothing to accept */
   1837       return 0;
   1838     }
   1839     logmsg("MAJOR ERROR, accept() failed with error (%d) %s",
   1840            error, sstrerror(error));
   1841     return CURL_SOCKET_BAD;
   1842   }
   1843 
   1844   if(0 != curlx_nonblock(msgsock, TRUE)) {
   1845     error = SOCKERRNO;
   1846     logmsg("curlx_nonblock failed with error (%d) %s",
   1847            error, sstrerror(error));
   1848     sclose(msgsock);
   1849     return CURL_SOCKET_BAD;
   1850   }
   1851 
   1852   if(0 != setsockopt(msgsock, SOL_SOCKET, SO_KEEPALIVE,
   1853                      (void *)&flag, sizeof(flag))) {
   1854     error = SOCKERRNO;
   1855     logmsg("setsockopt(SO_KEEPALIVE) failed with error (%d) %s",
   1856            error, sstrerror(error));
   1857     sclose(msgsock);
   1858     return CURL_SOCKET_BAD;
   1859   }
   1860 
   1861   /*
   1862   ** As soon as this server accepts a connection from the test harness it
   1863   ** must set the server logs advisor read lock to indicate that server
   1864   ** logs should not be read until this lock is removed by this server.
   1865   */
   1866 
   1867   if(!serverlogslocked)
   1868     set_advisor_read_lock(loglockfile);
   1869   serverlogslocked += 1;
   1870 
   1871   logmsg("====> Client connect");
   1872 
   1873   all_sockets[num_sockets] = msgsock;
   1874   num_sockets += 1;
   1875 
   1876 #ifdef TCP_NODELAY
   1877   if(socket_domain_is_ip()) {
   1878     /*
   1879      * Disable the Nagle algorithm to make it easier to send out a large
   1880      * response in many small segments to torture the clients more.
   1881      */
   1882     if(0 != setsockopt(msgsock, IPPROTO_TCP, TCP_NODELAY,
   1883                        (void *)&flag, sizeof(flag)))
   1884       logmsg("====> TCP_NODELAY failed");
   1885   }
   1886 #endif
   1887 
   1888   return msgsock;
   1889 }
   1890 
   1891 /* returns 1 if the connection should be serviced again immediately, 0 if there
   1892    is no data waiting, or < 0 if it should be closed */
   1893 static int service_connection(curl_socket_t msgsock,
   1894                               struct sws_httprequest *req,
   1895                               curl_socket_t listensock,
   1896                               const char *connecthost,
   1897                               int keepalive_secs)
   1898 {
   1899   if(got_exit_signal)
   1900     return -1;
   1901 
   1902   while(!req->done_processing) {
   1903     int rc = sws_get_request(msgsock, req);
   1904     if(rc <= 0) {
   1905       /* Nothing further to read now, possibly because the socket was closed */
   1906       return rc;
   1907     }
   1908   }
   1909 
   1910   if(sws_prevbounce) {
   1911     /* bounce treatment requested */
   1912     if(req->testno == sws_prevtestno) {
   1913       req->partno = sws_prevpartno + 1;
   1914       logmsg("BOUNCE part number to %ld", req->partno);
   1915     }
   1916     else {
   1917       sws_prevbounce = FALSE;
   1918       sws_prevtestno = -1;
   1919       sws_prevpartno = -1;
   1920     }
   1921   }
   1922 
   1923   sws_send_doc(msgsock, req);
   1924   if(got_exit_signal)
   1925     return -1;
   1926 
   1927   if(req->testno < 0) {
   1928     logmsg("special request received, no persistency");
   1929     return -1;
   1930   }
   1931   if(!req->open) {
   1932     logmsg("instructed to close connection after server-reply");
   1933     return -1;
   1934   }
   1935 
   1936   if(req->connect_request) {
   1937     /* a CONNECT request, setup and talk the tunnel */
   1938     if(!is_proxy) {
   1939       logmsg("received CONNECT but isn't running as proxy!");
   1940       return 1;
   1941     }
   1942     else {
   1943       http_connect(&msgsock, listensock, connecthost, req->connect_port,
   1944                    keepalive_secs);
   1945       return -1;
   1946     }
   1947   }
   1948 
   1949   if(req->upgrade_request) {
   1950     /* an upgrade request, switch to another protocol here */
   1951     http_upgrade(req);
   1952     return 1;
   1953   }
   1954 
   1955   /* if we got a CONNECT, loop and get another request as well! */
   1956 
   1957   if(req->open) {
   1958     logmsg("=> persistent connection request ended, awaits new request\n");
   1959     return 1;
   1960   }
   1961   else {
   1962     logmsg("=> NOT a persistent connection, close close CLOSE\n");
   1963   }
   1964 
   1965   return -1;
   1966 }
   1967 
   1968 static int test_sws(int argc, char *argv[])
   1969 {
   1970   srvr_sockaddr_union_t me;
   1971   curl_socket_t sock = CURL_SOCKET_BAD;
   1972   int wrotepidfile = 0;
   1973   int wroteportfile = 0;
   1974   int flag;
   1975   unsigned short port = 8999;
   1976 #ifdef USE_UNIX_SOCKETS
   1977   const char *unix_socket = NULL;
   1978   bool unlink_socket = false;
   1979 #endif
   1980   struct sws_httprequest *req = NULL;
   1981   int rc = 0;
   1982   int error;
   1983   int arg = 1;
   1984   const char *connecthost = "127.0.0.1";
   1985   char port_str[11];
   1986   const char *location_str = port_str;
   1987   int keepalive_secs = 5;
   1988   const char *protocol_type = "HTTP";
   1989 
   1990   /* a default CONNECT port is basically pointless but still ... */
   1991   size_t socket_idx;
   1992 
   1993   pidname = ".http.pid";
   1994   portname = ".http.port";
   1995   serverlogfile = "log/sws.log";
   1996   serverlogslocked = 0;
   1997 
   1998   while(argc > arg) {
   1999     if(!strcmp("--version", argv[arg])) {
   2000       puts("sws IPv4"
   2001 #ifdef USE_IPV6
   2002              "/IPv6"
   2003 #endif
   2004 #ifdef USE_UNIX_SOCKETS
   2005              "/unix"
   2006 #endif
   2007           );
   2008       return 0;
   2009     }
   2010     else if(!strcmp("--pidfile", argv[arg])) {
   2011       arg++;
   2012       if(argc > arg)
   2013         pidname = argv[arg++];
   2014     }
   2015     else if(!strcmp("--portfile", argv[arg])) {
   2016       arg++;
   2017       if(argc > arg)
   2018         portname = argv[arg++];
   2019     }
   2020     else if(!strcmp("--logfile", argv[arg])) {
   2021       arg++;
   2022       if(argc > arg)
   2023         serverlogfile = argv[arg++];
   2024     }
   2025     else if(!strcmp("--logdir", argv[arg])) {
   2026       arg++;
   2027       if(argc > arg)
   2028         logdir = argv[arg++];
   2029     }
   2030     else if(!strcmp("--cmdfile", argv[arg])) {
   2031       arg++;
   2032       if(argc > arg)
   2033         cmdfile = argv[arg++];
   2034     }
   2035     else if(!strcmp("--gopher", argv[arg])) {
   2036       arg++;
   2037       use_gopher = TRUE;
   2038       protocol_type = "GOPHER";
   2039       end_of_headers = "\r\n"; /* gopher style is much simpler */
   2040     }
   2041     else if(!strcmp("--ipv4", argv[arg])) {
   2042       socket_type = "IPv4";
   2043       socket_domain = AF_INET;
   2044       location_str = port_str;
   2045       arg++;
   2046     }
   2047     else if(!strcmp("--ipv6", argv[arg])) {
   2048 #ifdef USE_IPV6
   2049       socket_type = "IPv6";
   2050       socket_domain = AF_INET6;
   2051       location_str = port_str;
   2052 #endif
   2053       arg++;
   2054     }
   2055     else if(!strcmp("--unix-socket", argv[arg])) {
   2056       arg++;
   2057       if(argc > arg) {
   2058 #ifdef USE_UNIX_SOCKETS
   2059         unix_socket = argv[arg];
   2060         if(strlen(unix_socket) >= sizeof(me.sau.sun_path)) {
   2061           fprintf(stderr,
   2062                   "sws: socket path must be shorter than %u chars: %s\n",
   2063                   (unsigned int)sizeof(me.sau.sun_path), unix_socket);
   2064           return 0;
   2065         }
   2066         socket_type = "unix";
   2067         socket_domain = AF_UNIX;
   2068         location_str = unix_socket;
   2069 #endif
   2070         arg++;
   2071       }
   2072     }
   2073     else if(!strcmp("--port", argv[arg])) {
   2074       arg++;
   2075       if(argc > arg) {
   2076         char *endptr;
   2077         unsigned long ulnum = strtoul(argv[arg], &endptr, 10);
   2078         if((endptr != argv[arg] + strlen(argv[arg])) ||
   2079            (ulnum && ((ulnum < 1025UL) || (ulnum > 65535UL)))) {
   2080           fprintf(stderr, "sws: invalid --port argument (%s)\n",
   2081                   argv[arg]);
   2082           return 0;
   2083         }
   2084         port = util_ultous(ulnum);
   2085         arg++;
   2086       }
   2087     }
   2088     else if(!strcmp("--srcdir", argv[arg])) {
   2089       arg++;
   2090       if(argc > arg) {
   2091         srcpath = argv[arg];
   2092         arg++;
   2093       }
   2094     }
   2095     else if(!strcmp("--keepalive", argv[arg])) {
   2096       arg++;
   2097       if(argc > arg) {
   2098         char *endptr;
   2099         unsigned long ulnum = strtoul(argv[arg], &endptr, 10);
   2100         if((endptr != argv[arg] + strlen(argv[arg])) ||
   2101            (ulnum && (ulnum > 65535UL))) {
   2102           fprintf(stderr, "sws: invalid --keepalive argument (%s), must "
   2103                   "be number of seconds\n", argv[arg]);
   2104           return 0;
   2105         }
   2106         keepalive_secs = util_ultous(ulnum);
   2107         arg++;
   2108       }
   2109     }
   2110     else if(!strcmp("--connect", argv[arg])) {
   2111       /* The connect host IP number that the proxy will connect to no matter
   2112          what the client asks for, but also use this as a hint that we run as
   2113          a proxy and do a few different internal choices */
   2114       arg++;
   2115       if(argc > arg) {
   2116         connecthost = argv[arg];
   2117         arg++;
   2118         is_proxy = TRUE;
   2119         logmsg("Run as proxy, CONNECT to host %s", connecthost);
   2120       }
   2121     }
   2122     else {
   2123       puts("Usage: sws [option]\n"
   2124            " --version\n"
   2125            " --logfile [file]\n"
   2126            " --logdir [directory]\n"
   2127            " --pidfile [file]\n"
   2128            " --portfile [file]\n"
   2129            " --ipv4\n"
   2130            " --ipv6\n"
   2131            " --unix-socket [file]\n"
   2132            " --port [port]\n"
   2133            " --srcdir [path]\n"
   2134            " --connect [ip4-addr]\n"
   2135            " --gopher");
   2136       return 0;
   2137     }
   2138   }
   2139 
   2140   snprintf(loglockfile, sizeof(loglockfile), "%s/%s/sws-%s%s-%s.lock",
   2141            logdir, SERVERLOGS_LOCKDIR, protocol_type,
   2142            is_proxy ? "-proxy" : "", socket_type);
   2143 
   2144 #ifdef _WIN32
   2145   if(win32_init())
   2146     return 2;
   2147 #endif
   2148 
   2149   install_signal_handlers(false);
   2150 
   2151   req = calloc(1, sizeof(*req));
   2152   if(!req)
   2153     goto sws_cleanup;
   2154 
   2155   sock = socket(socket_domain, SOCK_STREAM, 0);
   2156 
   2157   all_sockets[0] = sock;
   2158   num_sockets = 1;
   2159 
   2160   if(CURL_SOCKET_BAD == sock) {
   2161     error = SOCKERRNO;
   2162     logmsg("Error creating socket (%d) %s", error, sstrerror(error));
   2163     goto sws_cleanup;
   2164   }
   2165 
   2166   flag = 1;
   2167   if(0 != setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
   2168                      (void *)&flag, sizeof(flag))) {
   2169     error = SOCKERRNO;
   2170     logmsg("setsockopt(SO_REUSEADDR) failed with error (%d) %s",
   2171            error, sstrerror(error));
   2172     goto sws_cleanup;
   2173   }
   2174   if(0 != curlx_nonblock(sock, TRUE)) {
   2175     error = SOCKERRNO;
   2176     logmsg("curlx_nonblock failed with error (%d) %s",
   2177            error, sstrerror(error));
   2178     goto sws_cleanup;
   2179   }
   2180 
   2181   switch(socket_domain) {
   2182   case AF_INET:
   2183     memset(&me.sa4, 0, sizeof(me.sa4));
   2184     me.sa4.sin_family = AF_INET;
   2185     me.sa4.sin_addr.s_addr = INADDR_ANY;
   2186     me.sa4.sin_port = htons(port);
   2187     rc = bind(sock, &me.sa, sizeof(me.sa4));
   2188     break;
   2189 #ifdef USE_IPV6
   2190   case AF_INET6:
   2191     memset(&me.sa6, 0, sizeof(me.sa6));
   2192     me.sa6.sin6_family = AF_INET6;
   2193     me.sa6.sin6_addr = in6addr_any;
   2194     me.sa6.sin6_port = htons(port);
   2195     rc = bind(sock, &me.sa, sizeof(me.sa6));
   2196     break;
   2197 #endif /* USE_IPV6 */
   2198 #ifdef USE_UNIX_SOCKETS
   2199   case AF_UNIX:
   2200     rc = bind_unix_socket(sock, unix_socket, &me.sau);
   2201 #endif /* USE_UNIX_SOCKETS */
   2202   }
   2203   if(rc) {
   2204     error = SOCKERRNO;
   2205 #ifdef USE_UNIX_SOCKETS
   2206     if(socket_domain == AF_UNIX)
   2207       logmsg("Error binding socket on path %s (%d) %s",
   2208              unix_socket, error, sstrerror(error));
   2209     else
   2210 #endif
   2211       logmsg("Error binding socket on port %hu (%d) %s",
   2212              port, error, sstrerror(error));
   2213     goto sws_cleanup;
   2214   }
   2215 
   2216   if(!port) {
   2217     /* The system was supposed to choose a port number, figure out which
   2218        port we actually got and update the listener port value with it. */
   2219     curl_socklen_t la_size;
   2220     srvr_sockaddr_union_t localaddr;
   2221 #ifdef USE_IPV6
   2222     if(socket_domain != AF_INET6)
   2223 #endif
   2224       la_size = sizeof(localaddr.sa4);
   2225 #ifdef USE_IPV6
   2226     else
   2227       la_size = sizeof(localaddr.sa6);
   2228 #endif
   2229     memset(&localaddr.sa, 0, (size_t)la_size);
   2230     if(getsockname(sock, &localaddr.sa, &la_size) < 0) {
   2231       error = SOCKERRNO;
   2232       logmsg("getsockname() failed with error (%d) %s",
   2233              error, sstrerror(error));
   2234       sclose(sock);
   2235       goto sws_cleanup;
   2236     }
   2237     switch(localaddr.sa.sa_family) {
   2238     case AF_INET:
   2239       port = ntohs(localaddr.sa4.sin_port);
   2240       break;
   2241 #ifdef USE_IPV6
   2242     case AF_INET6:
   2243       port = ntohs(localaddr.sa6.sin6_port);
   2244       break;
   2245 #endif
   2246     default:
   2247       break;
   2248     }
   2249     if(!port) {
   2250       /* Real failure, listener port shall not be zero beyond this point. */
   2251       logmsg("Apparently getsockname() succeeded, with listener port zero.");
   2252       logmsg("A valid reason for this failure is a binary built without");
   2253       logmsg("proper network library linkage. This might not be the only");
   2254       logmsg("reason, but double check it before anything else.");
   2255       sclose(sock);
   2256       goto sws_cleanup;
   2257     }
   2258   }
   2259 #ifdef USE_UNIX_SOCKETS
   2260   if(socket_domain != AF_UNIX)
   2261 #endif
   2262     snprintf(port_str, sizeof(port_str), "port %hu", port);
   2263 
   2264   logmsg("Running %s %s version on %s",
   2265          protocol_type, socket_type, location_str);
   2266 
   2267   /* start accepting connections */
   2268   rc = listen(sock, 50);
   2269   if(rc) {
   2270     error = SOCKERRNO;
   2271     logmsg("listen() failed with error (%d) %s", error, sstrerror(error));
   2272     goto sws_cleanup;
   2273   }
   2274 
   2275 #ifdef USE_UNIX_SOCKETS
   2276   /* listen succeeds, so let's assume a valid listening Unix socket */
   2277   unlink_socket = true;
   2278 #endif
   2279 
   2280   /*
   2281   ** As soon as this server writes its pid file the test harness will
   2282   ** attempt to connect to this server and initiate its verification.
   2283   */
   2284 
   2285   wrotepidfile = write_pidfile(pidname);
   2286   if(!wrotepidfile)
   2287     goto sws_cleanup;
   2288 
   2289   wroteportfile = write_portfile(portname, port);
   2290   if(!wroteportfile)
   2291     goto sws_cleanup;
   2292 
   2293   /* initialization of httprequest struct is done before sws_get_request(),
   2294      but the pipelining struct field must be initialized previously to FALSE
   2295      every time a new connection arrives. */
   2296 
   2297   init_httprequest(req);
   2298 
   2299   for(;;) {
   2300     fd_set input;
   2301     fd_set output;
   2302     curl_socket_t maxfd = (curl_socket_t)-1;
   2303     int active;
   2304     struct timeval timeout = {0};
   2305     timeout.tv_usec = 250000L; /* 250 ms */
   2306 
   2307     /* Clear out closed sockets */
   2308     for(socket_idx = num_sockets - 1; socket_idx >= 1; --socket_idx) {
   2309       if(CURL_SOCKET_BAD == all_sockets[socket_idx]) {
   2310         char *dst = (char *) (all_sockets + socket_idx);
   2311         char *src = (char *) (all_sockets + socket_idx + 1);
   2312         char *end = (char *) (all_sockets + num_sockets);
   2313         memmove(dst, src, end - src);
   2314         num_sockets -= 1;
   2315       }
   2316     }
   2317 
   2318     if(got_exit_signal)
   2319       goto sws_cleanup;
   2320 
   2321     /* Set up for select */
   2322     FD_ZERO(&input);
   2323     FD_ZERO(&output);
   2324 
   2325     for(socket_idx = 0; socket_idx < num_sockets; ++socket_idx) {
   2326       /* Listen on all sockets */
   2327 #if defined(__DJGPP__)
   2328 #pragma GCC diagnostic push
   2329 #pragma GCC diagnostic ignored "-Warith-conversion"
   2330 #endif
   2331       FD_SET(all_sockets[socket_idx], &input);
   2332 #if defined(__DJGPP__)
   2333 #pragma GCC diagnostic pop
   2334 #endif
   2335       if(all_sockets[socket_idx] > maxfd)
   2336         maxfd = all_sockets[socket_idx];
   2337     }
   2338 
   2339     if(got_exit_signal)
   2340       goto sws_cleanup;
   2341 
   2342     do {
   2343       rc = select((int)maxfd + 1, &input, &output, NULL, &timeout);
   2344     } while(rc < 0 && SOCKERRNO == SOCKEINTR && !got_exit_signal);
   2345 
   2346     if(got_exit_signal)
   2347       goto sws_cleanup;
   2348 
   2349     if(rc < 0) {
   2350       error = SOCKERRNO;
   2351       logmsg("select() failed with error (%d) %s", error, sstrerror(error));
   2352       goto sws_cleanup;
   2353     }
   2354 
   2355     if(rc == 0) {
   2356       /* Timed out - try again */
   2357       continue;
   2358     }
   2359     active = rc; /* a positive number */
   2360 
   2361     /* Check if the listening socket is ready to accept */
   2362     if(FD_ISSET(all_sockets[0], &input)) {
   2363       /* Service all queued connections */
   2364       curl_socket_t msgsock;
   2365       do {
   2366         msgsock = accept_connection(sock);
   2367         logmsg("accept_connection %ld returned %ld",
   2368                (long)sock, (long)msgsock);
   2369         if(CURL_SOCKET_BAD == msgsock)
   2370           goto sws_cleanup;
   2371         if(req->delay)
   2372           curlx_wait_ms(req->delay);
   2373       } while(msgsock > 0);
   2374       active--;
   2375     }
   2376 
   2377     /* Service all connections that are ready */
   2378     for(socket_idx = 1; (socket_idx < num_sockets) && active; ++socket_idx) {
   2379       if(FD_ISSET(all_sockets[socket_idx], &input)) {
   2380         active--;
   2381         if(got_exit_signal)
   2382           goto sws_cleanup;
   2383 
   2384         /* Service this connection until it has nothing available */
   2385         do {
   2386           rc = service_connection(all_sockets[socket_idx], req, sock,
   2387                                   connecthost, keepalive_secs);
   2388           if(got_exit_signal)
   2389             goto sws_cleanup;
   2390 
   2391           if(rc < 0) {
   2392             logmsg("====> Client disconnect %d", req->connmon);
   2393 
   2394             if(req->connmon) {
   2395               const char *keepopen = "[DISCONNECT]\n";
   2396               sws_storerequest(keepopen, strlen(keepopen));
   2397             }
   2398 
   2399             if(!req->open)
   2400               /* When instructed to close connection after server-reply we
   2401                  wait a very small amount of time before doing so. If this
   2402                  is not done client might get an ECONNRESET before reading
   2403                  a single byte of server-reply. */
   2404               curlx_wait_ms(50);
   2405 
   2406             if(all_sockets[socket_idx] != CURL_SOCKET_BAD) {
   2407               sclose(all_sockets[socket_idx]);
   2408               all_sockets[socket_idx] = CURL_SOCKET_BAD;
   2409             }
   2410 
   2411             serverlogslocked -= 1;
   2412             if(!serverlogslocked)
   2413               clear_advisor_read_lock(loglockfile);
   2414 
   2415             if(req->testno == DOCNUMBER_QUIT)
   2416               goto sws_cleanup;
   2417           }
   2418 
   2419           /* Reset the request, unless we're still in the middle of reading */
   2420           if(rc && !req->upgrade_request)
   2421             /* Note: resetting the HTTP request here can cause problems if:
   2422              * 1) req->skipall is TRUE,
   2423              * 2) the socket is still open, and
   2424              * 3) (stale) data is still available (or about to be available)
   2425              *    on that socket
   2426              * In that case, this loop will run once more and treat that stale
   2427              * data (in service_connection()) as the first data received on
   2428              * this new HTTP request and report "** Unusual request" (skipall
   2429              * would have otherwise caused that data to be ignored). Normally,
   2430              * that socket will be closed by the client and there won't be any
   2431              * stale data to cause this, but stranger things have happened (see
   2432              * issue #11678).
   2433              */
   2434             init_httprequest(req);
   2435         } while(rc > 0);
   2436       }
   2437     }
   2438 
   2439     if(got_exit_signal)
   2440       goto sws_cleanup;
   2441   }
   2442 
   2443 sws_cleanup:
   2444 
   2445   for(socket_idx = 1; socket_idx < num_sockets; ++socket_idx)
   2446     if((all_sockets[socket_idx] != sock) &&
   2447      (all_sockets[socket_idx] != CURL_SOCKET_BAD))
   2448       sclose(all_sockets[socket_idx]);
   2449 
   2450   if(sock != CURL_SOCKET_BAD)
   2451     sclose(sock);
   2452 
   2453 #ifdef USE_UNIX_SOCKETS
   2454   if(unlink_socket && socket_domain == AF_UNIX && unix_socket) {
   2455     rc = unlink(unix_socket);
   2456     logmsg("unlink(%s) = %d (%s)", unix_socket, rc, strerror(rc));
   2457   }
   2458 #endif
   2459 
   2460   free(req);
   2461 
   2462   if(got_exit_signal)
   2463     logmsg("signalled to die");
   2464 
   2465   if(wrotepidfile)
   2466     unlink(pidname);
   2467   if(wroteportfile)
   2468     unlink(portname);
   2469 
   2470   if(serverlogslocked) {
   2471     serverlogslocked = 0;
   2472     clear_advisor_read_lock(loglockfile);
   2473   }
   2474 
   2475   restore_signal_handlers(false);
   2476 
   2477   if(got_exit_signal) {
   2478     logmsg("========> %s sws (%s pid: %ld) exits with signal (%d)",
   2479            socket_type, location_str, (long)our_getpid(), exit_signal);
   2480     /*
   2481      * To properly set the return status of the process we
   2482      * must raise the same signal SIGINT or SIGTERM that we
   2483      * caught and let the old handler take care of it.
   2484      */
   2485     raise(exit_signal);
   2486   }
   2487 
   2488   logmsg("========> sws quits");
   2489   return 0;
   2490 }