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