• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
71 static 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;
80 static bool use_gopher = FALSE;
81 static int serverlogslocked = 0;
82 static bool is_proxy = FALSE;
83 
84 #define REQBUFSIZ (2*1024*1024)
85 
86 #define MAX_SLEEP_TIME_MS 250
87 
88 static long prevtestno = -1;    /* previous test number we served */
89 static long prevpartno = -1;    /* previous part number we served */
90 static 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 
98 struct 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 
135 static curl_socket_t all_sockets[MAX_SOCKETS];
136 static size_t num_sockets = 0;
137 
138 static int ProcessRequest(struct httprequest *req);
139 static 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 
147 const char *serverlogfile = DEFAULT_LOGFILE;
148 static const char *logdir = "log";
149 static 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"
163 const 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 
197 enum {
198   DOCNUMBER_NOTHING = -4,
199   DOCNUMBER_QUIT    = -3,
200   DOCNUMBER_WERULEZ = -2,
201   DOCNUMBER_404     = -1
202 };
203 
204 static const char *end_of_headers = END_OF_HEADERS;
205 
206 /* sent as reply to a QUIT */
207 static const char *docquit =
208 "HTTP/1.1 200 Goodbye" END_OF_HEADERS;
209 
210 /* send back this on 404 file not found */
211 static 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 */
225 static int already_recv_zeroed_chunk = FALSE;
226 
227 /* returns true if the current socket is an IP one */
socket_domain_is_ip(void)228 static 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 */
parse_cmdfile(struct httprequest * req)243 static 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 */
parse_servercmd(struct httprequest * req)261 static 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 
ProcessRequest(struct httprequest * req)365 static 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 %d 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 (%d)", 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 %d 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: %d, prev_partno: %d",
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 */
storerequest(const char * reqbuf,size_t totalsize)788 static 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 
834 storerequest_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 
init_httprequest(struct httprequest * req)844 static 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 
871 static 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 */
get_request(curl_socket_t sock,struct httprequest * req)875 static 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 */
send_doc(curl_socket_t sock,struct httprequest * req)1008 static 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 
1179 retry:
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 %d 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 
connect_to(const char * ipaddr,unsigned short port)1284 static 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 
http_connect(curl_socket_t * infdp,curl_socket_t rootfd,const char * ipaddr,unsigned short ipport,int keepalive_secs)1395 static 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 
1762 http_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 
http_upgrade(struct httprequest * req)1784 static 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 */
accept_connection(curl_socket_t sock)1794 static 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 */
service_connection(curl_socket_t msgsock,struct httprequest * req,curl_socket_t listensock,const char * connecthost,int keepalive_secs)1873 static 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 
main(int argc,char * argv[])1948 int 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 %d returned %d", sock, msgsock);
2338         if(CURL_SOCKET_BAD == msgsock)
2339           goto sws_cleanup;
2340         if(req->delay)
2341           wait_ms(req->delay);
2342       } while(msgsock > 0);
2343       active--;
2344     }
2345 
2346     /* Service all connections that are ready */
2347     for(socket_idx = 1; (socket_idx < num_sockets) && active; ++socket_idx) {
2348       if(FD_ISSET(all_sockets[socket_idx], &input)) {
2349         active--;
2350         if(got_exit_signal)
2351           goto sws_cleanup;
2352 
2353         /* Service this connection until it has nothing available */
2354         do {
2355           rc = service_connection(all_sockets[socket_idx], req, sock,
2356                                   connecthost, keepalive_secs);
2357           if(got_exit_signal)
2358             goto sws_cleanup;
2359 
2360           if(rc < 0) {
2361             logmsg("====> Client disconnect %d", req->connmon);
2362 
2363             if(req->connmon) {
2364               const char *keepopen = "[DISCONNECT]\n";
2365               storerequest(keepopen, strlen(keepopen));
2366             }
2367 
2368             if(!req->open)
2369               /* When instructed to close connection after server-reply we
2370                  wait a very small amount of time before doing so. If this
2371                  is not done client might get an ECONNRESET before reading
2372                  a single byte of server-reply. */
2373               wait_ms(50);
2374 
2375             if(all_sockets[socket_idx] != CURL_SOCKET_BAD) {
2376               sclose(all_sockets[socket_idx]);
2377               all_sockets[socket_idx] = CURL_SOCKET_BAD;
2378             }
2379 
2380             serverlogslocked -= 1;
2381             if(!serverlogslocked)
2382               clear_advisor_read_lock(loglockfile);
2383 
2384             if(req->testno == DOCNUMBER_QUIT)
2385               goto sws_cleanup;
2386           }
2387 
2388           /* Reset the request, unless we're still in the middle of reading */
2389           if(rc && !req->upgrade_request)
2390             /* Note: resetting the HTTP request here can cause problems if:
2391              * 1) req->skipall is TRUE,
2392              * 2) the socket is still open, and
2393              * 3) (stale) data is still available (or about to be available)
2394              *    on that socket
2395              * In that case, this loop will run once more and treat that stale
2396              * data (in service_connection()) as the first data received on
2397              * this new HTTP request and report "** Unusual request" (skipall
2398              * would have otherwise caused that data to be ignored). Normally,
2399              * that socket will be closed by the client and there won't be any
2400              * stale data to cause this, but stranger things have happened (see
2401              * issue #11678).
2402              */
2403             init_httprequest(req);
2404         } while(rc > 0);
2405       }
2406     }
2407 
2408     if(got_exit_signal)
2409       goto sws_cleanup;
2410   }
2411 
2412 sws_cleanup:
2413 
2414   for(socket_idx = 1; socket_idx < num_sockets; ++socket_idx)
2415     if((all_sockets[socket_idx] != sock) &&
2416      (all_sockets[socket_idx] != CURL_SOCKET_BAD))
2417       sclose(all_sockets[socket_idx]);
2418 
2419   if(sock != CURL_SOCKET_BAD)
2420     sclose(sock);
2421 
2422 #ifdef USE_UNIX_SOCKETS
2423   if(unlink_socket && socket_domain == AF_UNIX) {
2424     rc = unlink(unix_socket);
2425     logmsg("unlink(%s) = %d (%s)", unix_socket, rc, strerror(rc));
2426   }
2427 #endif
2428 
2429   free(req);
2430 
2431   if(got_exit_signal)
2432     logmsg("signalled to die");
2433 
2434   if(wrotepidfile)
2435     unlink(pidname);
2436   if(wroteportfile)
2437     unlink(portname);
2438 
2439   if(serverlogslocked) {
2440     serverlogslocked = 0;
2441     clear_advisor_read_lock(loglockfile);
2442   }
2443 
2444   restore_signal_handlers(false);
2445 
2446   if(got_exit_signal) {
2447     logmsg("========> %s sws (%s pid: %ld) exits with signal (%d)",
2448            socket_type, location_str, (long)getpid(), exit_signal);
2449     /*
2450      * To properly set the return status of the process we
2451      * must raise the same signal SIGINT or SIGTERM that we
2452      * caught and let the old handler take care of it.
2453      */
2454     raise(exit_signal);
2455   }
2456 
2457   logmsg("========> sws quits");
2458   return 0;
2459 }
2460