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