• 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: %ld\r\n", (long)getpid());
998       msglen = strlen(msgbuf);
999       if(use_gopher)
1000         msnprintf(weare, sizeof(weare), "%s", msgbuf);
1001       else
1002         msnprintf(weare, sizeof(weare),
1003                   "HTTP/1.1 200 OK\r\nContent-Length: %zu\r\n\r\n%s",
1004                   msglen, msgbuf);
1005       buffer = weare;
1006       break;
1007     case DOCNUMBER_404:
1008     default:
1009       logmsg("Replying to with a 404");
1010       buffer = doc404;
1011       break;
1012     }
1013 
1014     count = strlen(buffer);
1015   }
1016   else {
1017     char partbuf[80];
1018 
1019     /* select the <data> tag for "normal" requests and the <connect> one
1020        for CONNECT requests (within the <reply> section) */
1021     const char *section = req->connect_request?"connect":"data";
1022 
1023     if(req->partno)
1024       msnprintf(partbuf, sizeof(partbuf), "%s%ld", section, req->partno);
1025     else
1026       msnprintf(partbuf, sizeof(partbuf), "%s", section);
1027 
1028     logmsg("Send response test%ld section <%s>", req->testno, partbuf);
1029 
1030     stream = test2fopen(req->testno);
1031     if(!stream) {
1032       error = errno;
1033       logmsg("fopen() failed with error: %d %s", error, strerror(error));
1034       return 0;
1035     }
1036     else {
1037       error = getpart(&ptr, &count, "reply", partbuf, stream);
1038       fclose(stream);
1039       if(error) {
1040         logmsg("getpart() failed with error: %d", error);
1041         return 0;
1042       }
1043       buffer = ptr;
1044     }
1045 
1046     if(got_exit_signal) {
1047       free(ptr);
1048       return -1;
1049     }
1050 
1051     /* re-open the same file again */
1052     stream = test2fopen(req->testno);
1053     if(!stream) {
1054       error = errno;
1055       logmsg("fopen() failed with error: %d %s", error, strerror(error));
1056       free(ptr);
1057       return 0;
1058     }
1059     else {
1060       /* get the custom server control "commands" */
1061       error = getpart(&cmd, &cmdsize, "reply", "postcmd", stream);
1062       fclose(stream);
1063       if(error) {
1064         logmsg("getpart() failed with error: %d", error);
1065         free(ptr);
1066         return 0;
1067       }
1068     }
1069   }
1070 
1071   if(got_exit_signal) {
1072     free(ptr);
1073     free(cmd);
1074     return -1;
1075   }
1076 
1077   /* If the word 'swsclose' is present anywhere in the reply chunk, the
1078      connection will be closed after the data has been sent to the requesting
1079      client... */
1080   if(strstr(buffer, "swsclose") || !count || req->close) {
1081     persistent = FALSE;
1082     logmsg("connection close instruction \"swsclose\" found in response");
1083   }
1084   if(strstr(buffer, "swsbounce")) {
1085     prevbounce = TRUE;
1086     logmsg("enable \"swsbounce\" in the next request");
1087   }
1088   else
1089     prevbounce = FALSE;
1090 
1091   dump = fopen(responsedump, "ab");
1092   if(!dump) {
1093     error = errno;
1094     logmsg("fopen() failed with error: %d %s", error, strerror(error));
1095     logmsg("  [5] Error opening file: %s", responsedump);
1096     free(ptr);
1097     free(cmd);
1098     return -1;
1099   }
1100 
1101   responsesize = count;
1102   do {
1103     /* Ok, we send no more than N bytes at a time, just to make sure that
1104        larger chunks are split up so that the client will need to do multiple
1105        recv() calls to get it and thus we exercise that code better */
1106     size_t num = count;
1107     if(num > 20)
1108       num = 20;
1109 
1110     retry:
1111     written = swrite(sock, buffer, num);
1112     if(written < 0) {
1113       if((EWOULDBLOCK == SOCKERRNO) || (EAGAIN == SOCKERRNO)) {
1114         wait_ms(10);
1115         goto retry;
1116       }
1117       sendfailure = TRUE;
1118       break;
1119     }
1120 
1121     /* write to file as well */
1122     fwrite(buffer, 1, (size_t)written, dump);
1123 
1124     count -= written;
1125     buffer += written;
1126 
1127     if(req->writedelay) {
1128       int quarters = req->writedelay * 4;
1129       logmsg("Pausing %d seconds", req->writedelay);
1130       while((quarters > 0) && !got_exit_signal) {
1131         quarters--;
1132         wait_ms(250);
1133       }
1134     }
1135   } while((count > 0) && !got_exit_signal);
1136 
1137   do {
1138     res = fclose(dump);
1139   } while(res && ((error = errno) == EINTR));
1140   if(res)
1141     logmsg("Error closing file %s error: %d %s",
1142            responsedump, error, strerror(error));
1143 
1144   if(got_exit_signal) {
1145     free(ptr);
1146     free(cmd);
1147     return -1;
1148   }
1149 
1150   if(sendfailure) {
1151     logmsg("Sending response failed. Only (%zu bytes) of (%zu bytes) "
1152            "were sent",
1153            responsesize-count, responsesize);
1154     prevtestno = req->testno;
1155     prevpartno = req->partno;
1156     free(ptr);
1157     free(cmd);
1158     return -1;
1159   }
1160 
1161   logmsg("Response sent (%zu bytes) and written to %s",
1162          responsesize, responsedump);
1163   free(ptr);
1164 
1165   if(cmdsize > 0) {
1166     char command[32];
1167     int quarters;
1168     int num;
1169     ptr = cmd;
1170     do {
1171       if(2 == sscanf(ptr, "%31s %d", command, &num)) {
1172         if(!strcmp("wait", command)) {
1173           logmsg("Told to sleep for %d seconds", num);
1174           quarters = num * 4;
1175           while((quarters > 0) && !got_exit_signal) {
1176             quarters--;
1177             res = wait_ms(250);
1178             if(res) {
1179               /* should not happen */
1180               error = errno;
1181               logmsg("wait_ms() failed with error: (%d) %s",
1182                      error, strerror(error));
1183               break;
1184             }
1185           }
1186           if(!quarters)
1187             logmsg("Continuing after sleeping %d seconds", num);
1188         }
1189         else
1190           logmsg("Unknown command in reply command section");
1191       }
1192       ptr = strchr(ptr, '\n');
1193       if(ptr)
1194         ptr++;
1195       else
1196         ptr = NULL;
1197     } while(ptr && *ptr);
1198   }
1199   free(cmd);
1200   req->open = use_gopher?FALSE:persistent;
1201 
1202   prevtestno = req->testno;
1203   prevpartno = req->partno;
1204 
1205   return 0;
1206 }
1207 
connect_to(const char * ipaddr,unsigned short port)1208 static curl_socket_t connect_to(const char *ipaddr, unsigned short port)
1209 {
1210   srvr_sockaddr_union_t serveraddr;
1211   curl_socket_t serverfd;
1212   int error;
1213   int rc = 0;
1214   const char *op_br = "";
1215   const char *cl_br = "";
1216 
1217 #ifdef ENABLE_IPV6
1218   if(socket_domain == AF_INET6) {
1219     op_br = "[";
1220     cl_br = "]";
1221   }
1222 #endif
1223 
1224   if(!ipaddr)
1225     return CURL_SOCKET_BAD;
1226 
1227   logmsg("about to connect to %s%s%s:%hu",
1228          op_br, ipaddr, cl_br, port);
1229 
1230 
1231   serverfd = socket(socket_domain, SOCK_STREAM, 0);
1232   if(CURL_SOCKET_BAD == serverfd) {
1233     error = SOCKERRNO;
1234     logmsg("Error creating socket for server connection: (%d) %s",
1235            error, strerror(error));
1236     return CURL_SOCKET_BAD;
1237   }
1238 
1239 #ifdef TCP_NODELAY
1240   if(socket_domain_is_ip()) {
1241     /* Disable the Nagle algorithm */
1242     curl_socklen_t flag = 1;
1243     if(0 != setsockopt(serverfd, IPPROTO_TCP, TCP_NODELAY,
1244                        (void *)&flag, sizeof(flag)))
1245       logmsg("====> TCP_NODELAY for server connection failed");
1246   }
1247 #endif
1248 
1249   switch(socket_domain) {
1250   case AF_INET:
1251     memset(&serveraddr.sa4, 0, sizeof(serveraddr.sa4));
1252     serveraddr.sa4.sin_family = AF_INET;
1253     serveraddr.sa4.sin_port = htons(port);
1254     if(Curl_inet_pton(AF_INET, ipaddr, &serveraddr.sa4.sin_addr) < 1) {
1255       logmsg("Error inet_pton failed AF_INET conversion of '%s'", ipaddr);
1256       sclose(serverfd);
1257       return CURL_SOCKET_BAD;
1258     }
1259 
1260     rc = connect(serverfd, &serveraddr.sa, sizeof(serveraddr.sa4));
1261     break;
1262 #ifdef ENABLE_IPV6
1263   case AF_INET6:
1264     memset(&serveraddr.sa6, 0, sizeof(serveraddr.sa6));
1265     serveraddr.sa6.sin6_family = AF_INET6;
1266     serveraddr.sa6.sin6_port = htons(port);
1267     if(Curl_inet_pton(AF_INET6, ipaddr, &serveraddr.sa6.sin6_addr) < 1) {
1268       logmsg("Error inet_pton failed AF_INET6 conversion of '%s'", ipaddr);
1269       sclose(serverfd);
1270       return CURL_SOCKET_BAD;
1271     }
1272 
1273     rc = connect(serverfd, &serveraddr.sa, sizeof(serveraddr.sa6));
1274     break;
1275 #endif /* ENABLE_IPV6 */
1276 #ifdef USE_UNIX_SOCKETS
1277   case AF_UNIX:
1278     logmsg("Proxying through Unix socket is not (yet?) supported.");
1279     return CURL_SOCKET_BAD;
1280 #endif /* USE_UNIX_SOCKETS */
1281   }
1282 
1283   if(got_exit_signal) {
1284     sclose(serverfd);
1285     return CURL_SOCKET_BAD;
1286   }
1287 
1288   if(rc) {
1289     error = SOCKERRNO;
1290     logmsg("Error connecting to server port %hu: (%d) %s",
1291            port, error, strerror(error));
1292     sclose(serverfd);
1293     return CURL_SOCKET_BAD;
1294   }
1295 
1296   logmsg("connected fine to %s%s%s:%hu, now tunnel",
1297          op_br, ipaddr, cl_br, port);
1298 
1299   return serverfd;
1300 }
1301 
1302 /*
1303  * A CONNECT has been received, a CONNECT response has been sent.
1304  *
1305  * This function needs to connect to the server, and then pass data between
1306  * the client and the server back and forth until the connection is closed by
1307  * either end.
1308  *
1309  * When doing FTP through a CONNECT proxy, we expect that the data connection
1310  * will be setup while the first connect is still being kept up. Therefore we
1311  * must accept a new connection and deal with it appropriately.
1312  */
1313 
1314 #define data_or_ctrl(x) ((x)?"DATA":"CTRL")
1315 
1316 #define CTRL  0
1317 #define DATA  1
1318 
http_connect(curl_socket_t * infdp,curl_socket_t rootfd,const char * ipaddr,unsigned short ipport)1319 static void http_connect(curl_socket_t *infdp,
1320                          curl_socket_t rootfd,
1321                          const char *ipaddr,
1322                          unsigned short ipport)
1323 {
1324   curl_socket_t serverfd[2] = {CURL_SOCKET_BAD, CURL_SOCKET_BAD};
1325   curl_socket_t clientfd[2] = {CURL_SOCKET_BAD, CURL_SOCKET_BAD};
1326   ssize_t toc[2] = {0, 0}; /* number of bytes to client */
1327   ssize_t tos[2] = {0, 0}; /* number of bytes to server */
1328   char readclient[2][256];
1329   char readserver[2][256];
1330   bool poll_client_rd[2] = { TRUE, TRUE };
1331   bool poll_server_rd[2] = { TRUE, TRUE };
1332   bool poll_client_wr[2] = { TRUE, TRUE };
1333   bool poll_server_wr[2] = { TRUE, TRUE };
1334   bool primary = FALSE;
1335   bool secondary = FALSE;
1336   int max_tunnel_idx; /* CTRL or DATA */
1337   int loop;
1338   int i;
1339   int timeout_count = 0;
1340 
1341   /* primary tunnel client endpoint already connected */
1342   clientfd[CTRL] = *infdp;
1343 
1344   /* Sleep here to make sure the client reads CONNECT response's
1345      'end of headers' separate from the server data that follows.
1346      This is done to prevent triggering libcurl known bug #39. */
1347   for(loop = 2; (loop > 0) && !got_exit_signal; loop--)
1348     wait_ms(250);
1349   if(got_exit_signal)
1350     goto http_connect_cleanup;
1351 
1352   serverfd[CTRL] = connect_to(ipaddr, ipport);
1353   if(serverfd[CTRL] == CURL_SOCKET_BAD)
1354     goto http_connect_cleanup;
1355 
1356   /* Primary tunnel socket endpoints are now connected. Tunnel data back and
1357      forth over the primary tunnel until client or server breaks the primary
1358      tunnel, simultaneously allowing establishment, operation and teardown of
1359      a secondary tunnel that may be used for passive FTP data connection. */
1360 
1361   max_tunnel_idx = CTRL;
1362   primary = TRUE;
1363 
1364   while(!got_exit_signal) {
1365 
1366     fd_set input;
1367     fd_set output;
1368     struct timeval timeout = {1, 0}; /* 1000 ms */
1369     ssize_t rc;
1370     curl_socket_t maxfd = (curl_socket_t)-1;
1371 
1372     FD_ZERO(&input);
1373     FD_ZERO(&output);
1374 
1375     if((clientfd[DATA] == CURL_SOCKET_BAD) &&
1376        (serverfd[DATA] == CURL_SOCKET_BAD) &&
1377        poll_client_rd[CTRL] && poll_client_wr[CTRL] &&
1378        poll_server_rd[CTRL] && poll_server_wr[CTRL]) {
1379       /* listener socket is monitored to allow client to establish
1380          secondary tunnel only when this tunnel is not established
1381          and primary one is fully operational */
1382       FD_SET(rootfd, &input);
1383       maxfd = rootfd;
1384     }
1385 
1386     /* set tunnel sockets to wait for */
1387     for(i = 0; i <= max_tunnel_idx; i++) {
1388       /* client side socket monitoring */
1389       if(clientfd[i] != CURL_SOCKET_BAD) {
1390         if(poll_client_rd[i]) {
1391           /* unless told not to do so, monitor readability */
1392           FD_SET(clientfd[i], &input);
1393           if(clientfd[i] > maxfd)
1394             maxfd = clientfd[i];
1395         }
1396         if(poll_client_wr[i] && toc[i]) {
1397           /* unless told not to do so, monitor writability
1398              if there is data ready to be sent to client */
1399           FD_SET(clientfd[i], &output);
1400           if(clientfd[i] > maxfd)
1401             maxfd = clientfd[i];
1402         }
1403       }
1404       /* server side socket monitoring */
1405       if(serverfd[i] != CURL_SOCKET_BAD) {
1406         if(poll_server_rd[i]) {
1407           /* unless told not to do so, monitor readability */
1408           FD_SET(serverfd[i], &input);
1409           if(serverfd[i] > maxfd)
1410             maxfd = serverfd[i];
1411         }
1412         if(poll_server_wr[i] && tos[i]) {
1413           /* unless told not to do so, monitor writability
1414              if there is data ready to be sent to server */
1415           FD_SET(serverfd[i], &output);
1416           if(serverfd[i] > maxfd)
1417             maxfd = serverfd[i];
1418         }
1419       }
1420     }
1421     if(got_exit_signal)
1422       break;
1423 
1424     do {
1425       rc = select((int)maxfd + 1, &input, &output, NULL, &timeout);
1426     } while(rc < 0 && errno == EINTR && !got_exit_signal);
1427 
1428     if(got_exit_signal)
1429       break;
1430 
1431     if(rc > 0) {
1432       /* socket action */
1433       bool tcp_fin_wr = FALSE;
1434       timeout_count = 0;
1435 
1436       /* ---------------------------------------------------------- */
1437 
1438       /* passive mode FTP may establish a secondary tunnel */
1439       if((clientfd[DATA] == CURL_SOCKET_BAD) &&
1440          (serverfd[DATA] == CURL_SOCKET_BAD) && FD_ISSET(rootfd, &input)) {
1441         /* a new connection on listener socket (most likely from client) */
1442         curl_socket_t datafd = accept(rootfd, NULL, NULL);
1443         if(datafd != CURL_SOCKET_BAD) {
1444           static struct httprequest *req2;
1445           int err = 0;
1446           if(!req2) {
1447             req2 = malloc(sizeof(*req2));
1448             if(!req2)
1449               exit(1);
1450           }
1451           memset(req2, 0, sizeof(*req2));
1452           logmsg("====> Client connect DATA");
1453 #ifdef TCP_NODELAY
1454           if(socket_domain_is_ip()) {
1455             /* Disable the Nagle algorithm */
1456             curl_socklen_t flag = 1;
1457             if(0 != setsockopt(datafd, IPPROTO_TCP, TCP_NODELAY,
1458                                (void *)&flag, sizeof(flag)))
1459               logmsg("====> TCP_NODELAY for client DATA connection failed");
1460           }
1461 #endif
1462           init_httprequest(req2);
1463           while(!req2->done_processing) {
1464             err = get_request(datafd, req2);
1465             if(err < 0) {
1466               /* this socket must be closed, done or not */
1467               break;
1468             }
1469           }
1470 
1471           /* skip this and close the socket if err < 0 */
1472           if(err >= 0) {
1473             err = send_doc(datafd, req2);
1474             if(!err && req2->connect_request) {
1475               /* sleep to prevent triggering libcurl known bug #39. */
1476               for(loop = 2; (loop > 0) && !got_exit_signal; loop--)
1477                 wait_ms(250);
1478               if(!got_exit_signal) {
1479                 /* connect to the server */
1480                 serverfd[DATA] = connect_to(ipaddr, req2->connect_port);
1481                 if(serverfd[DATA] != CURL_SOCKET_BAD) {
1482                   /* secondary tunnel established, now we have two
1483                      connections */
1484                   poll_client_rd[DATA] = TRUE;
1485                   poll_client_wr[DATA] = TRUE;
1486                   poll_server_rd[DATA] = TRUE;
1487                   poll_server_wr[DATA] = TRUE;
1488                   max_tunnel_idx = DATA;
1489                   secondary = TRUE;
1490                   toc[DATA] = 0;
1491                   tos[DATA] = 0;
1492                   clientfd[DATA] = datafd;
1493                   datafd = CURL_SOCKET_BAD;
1494                 }
1495               }
1496             }
1497           }
1498           if(datafd != CURL_SOCKET_BAD) {
1499             /* secondary tunnel not established */
1500             shutdown(datafd, SHUT_RDWR);
1501             sclose(datafd);
1502           }
1503         }
1504         if(got_exit_signal)
1505           break;
1506       }
1507 
1508       /* ---------------------------------------------------------- */
1509 
1510       /* react to tunnel endpoint readable/writable notifications */
1511       for(i = 0; i <= max_tunnel_idx; i++) {
1512         size_t len;
1513         if(clientfd[i] != CURL_SOCKET_BAD) {
1514           len = sizeof(readclient[i]) - tos[i];
1515           if(len && FD_ISSET(clientfd[i], &input)) {
1516             /* read from client */
1517             rc = sread(clientfd[i], &readclient[i][tos[i]], len);
1518             if(rc <= 0) {
1519               logmsg("[%s] got %zd, STOP READING client", data_or_ctrl(i), rc);
1520               shutdown(clientfd[i], SHUT_RD);
1521               poll_client_rd[i] = FALSE;
1522             }
1523             else {
1524               logmsg("[%s] READ %zd bytes from client", data_or_ctrl(i), rc);
1525               logmsg("[%s] READ \"%s\"", data_or_ctrl(i),
1526                      data_to_hex(&readclient[i][tos[i]], rc));
1527               tos[i] += rc;
1528             }
1529           }
1530         }
1531         if(serverfd[i] != CURL_SOCKET_BAD) {
1532           len = sizeof(readserver[i])-toc[i];
1533           if(len && FD_ISSET(serverfd[i], &input)) {
1534             /* read from server */
1535             rc = sread(serverfd[i], &readserver[i][toc[i]], len);
1536             if(rc <= 0) {
1537               logmsg("[%s] got %zd, STOP READING server", data_or_ctrl(i), rc);
1538               shutdown(serverfd[i], SHUT_RD);
1539               poll_server_rd[i] = FALSE;
1540             }
1541             else {
1542               logmsg("[%s] READ %zd bytes from server", data_or_ctrl(i), rc);
1543               logmsg("[%s] READ \"%s\"", data_or_ctrl(i),
1544                      data_to_hex(&readserver[i][toc[i]], rc));
1545               toc[i] += rc;
1546             }
1547           }
1548         }
1549         if(clientfd[i] != CURL_SOCKET_BAD) {
1550           if(toc[i] && FD_ISSET(clientfd[i], &output)) {
1551             /* write to client */
1552             rc = swrite(clientfd[i], readserver[i], toc[i]);
1553             if(rc <= 0) {
1554               logmsg("[%s] got %zd, STOP WRITING client", data_or_ctrl(i), rc);
1555               shutdown(clientfd[i], SHUT_WR);
1556               poll_client_wr[i] = FALSE;
1557               tcp_fin_wr = TRUE;
1558             }
1559             else {
1560               logmsg("[%s] SENT %zd bytes to client", data_or_ctrl(i), rc);
1561               logmsg("[%s] SENT \"%s\"", data_or_ctrl(i),
1562                      data_to_hex(readserver[i], rc));
1563               if(toc[i] - rc)
1564                 memmove(&readserver[i][0], &readserver[i][rc], toc[i]-rc);
1565               toc[i] -= rc;
1566             }
1567           }
1568         }
1569         if(serverfd[i] != CURL_SOCKET_BAD) {
1570           if(tos[i] && FD_ISSET(serverfd[i], &output)) {
1571             /* write to server */
1572             rc = swrite(serverfd[i], readclient[i], tos[i]);
1573             if(rc <= 0) {
1574               logmsg("[%s] got %zd, STOP WRITING server", data_or_ctrl(i), rc);
1575               shutdown(serverfd[i], SHUT_WR);
1576               poll_server_wr[i] = FALSE;
1577               tcp_fin_wr = TRUE;
1578             }
1579             else {
1580               logmsg("[%s] SENT %zd bytes to server", data_or_ctrl(i), rc);
1581               logmsg("[%s] SENT \"%s\"", data_or_ctrl(i),
1582                      data_to_hex(readclient[i], rc));
1583               if(tos[i] - rc)
1584                 memmove(&readclient[i][0], &readclient[i][rc], tos[i]-rc);
1585               tos[i] -= rc;
1586             }
1587           }
1588         }
1589       }
1590       if(got_exit_signal)
1591         break;
1592 
1593       /* ---------------------------------------------------------- */
1594 
1595       /* endpoint read/write disabling, endpoint closing and tunnel teardown */
1596       for(i = 0; i <= max_tunnel_idx; i++) {
1597         for(loop = 2; loop > 0; loop--) {
1598           /* loop twice to satisfy condition interdependencies without
1599              having to await select timeout or another socket event */
1600           if(clientfd[i] != CURL_SOCKET_BAD) {
1601             if(poll_client_rd[i] && !poll_server_wr[i]) {
1602               logmsg("[%s] DISABLED READING client", data_or_ctrl(i));
1603               shutdown(clientfd[i], SHUT_RD);
1604               poll_client_rd[i] = FALSE;
1605             }
1606             if(poll_client_wr[i] && !poll_server_rd[i] && !toc[i]) {
1607               logmsg("[%s] DISABLED WRITING client", data_or_ctrl(i));
1608               shutdown(clientfd[i], SHUT_WR);
1609               poll_client_wr[i] = FALSE;
1610               tcp_fin_wr = TRUE;
1611             }
1612           }
1613           if(serverfd[i] != CURL_SOCKET_BAD) {
1614             if(poll_server_rd[i] && !poll_client_wr[i]) {
1615               logmsg("[%s] DISABLED READING server", data_or_ctrl(i));
1616               shutdown(serverfd[i], SHUT_RD);
1617               poll_server_rd[i] = FALSE;
1618             }
1619             if(poll_server_wr[i] && !poll_client_rd[i] && !tos[i]) {
1620               logmsg("[%s] DISABLED WRITING server", data_or_ctrl(i));
1621               shutdown(serverfd[i], SHUT_WR);
1622               poll_server_wr[i] = FALSE;
1623               tcp_fin_wr = TRUE;
1624             }
1625           }
1626         }
1627       }
1628 
1629       if(tcp_fin_wr)
1630         /* allow kernel to place FIN bit packet on the wire */
1631         wait_ms(250);
1632 
1633       /* socket clearing */
1634       for(i = 0; i <= max_tunnel_idx; i++) {
1635         for(loop = 2; loop > 0; loop--) {
1636           if(clientfd[i] != CURL_SOCKET_BAD) {
1637             if(!poll_client_wr[i] && !poll_client_rd[i]) {
1638               logmsg("[%s] CLOSING client socket", data_or_ctrl(i));
1639               sclose(clientfd[i]);
1640               clientfd[i] = CURL_SOCKET_BAD;
1641               if(serverfd[i] == CURL_SOCKET_BAD) {
1642                 logmsg("[%s] ENDING", data_or_ctrl(i));
1643                 if(i == DATA)
1644                   secondary = FALSE;
1645                 else
1646                   primary = FALSE;
1647               }
1648             }
1649           }
1650           if(serverfd[i] != CURL_SOCKET_BAD) {
1651             if(!poll_server_wr[i] && !poll_server_rd[i]) {
1652               logmsg("[%s] CLOSING server socket", data_or_ctrl(i));
1653               sclose(serverfd[i]);
1654               serverfd[i] = CURL_SOCKET_BAD;
1655               if(clientfd[i] == CURL_SOCKET_BAD) {
1656                 logmsg("[%s] ENDING", data_or_ctrl(i));
1657                 if(i == DATA)
1658                   secondary = FALSE;
1659                 else
1660                   primary = FALSE;
1661               }
1662             }
1663           }
1664         }
1665       }
1666 
1667       /* ---------------------------------------------------------- */
1668 
1669       max_tunnel_idx = secondary ? DATA : CTRL;
1670 
1671       if(!primary)
1672         /* exit loop upon primary tunnel teardown */
1673         break;
1674 
1675     } /* (rc > 0) */
1676     else {
1677       timeout_count++;
1678       if(timeout_count > 5) {
1679         logmsg("CONNECT proxy timeout after %d idle seconds!", timeout_count);
1680         break;
1681       }
1682     }
1683   }
1684 
1685 http_connect_cleanup:
1686 
1687   for(i = DATA; i >= CTRL; i--) {
1688     if(serverfd[i] != CURL_SOCKET_BAD) {
1689       logmsg("[%s] CLOSING server socket (cleanup)", data_or_ctrl(i));
1690       shutdown(serverfd[i], SHUT_RDWR);
1691       sclose(serverfd[i]);
1692     }
1693     if(clientfd[i] != CURL_SOCKET_BAD) {
1694       logmsg("[%s] CLOSING client socket (cleanup)", data_or_ctrl(i));
1695       shutdown(clientfd[i], SHUT_RDWR);
1696       sclose(clientfd[i]);
1697     }
1698     if((serverfd[i] != CURL_SOCKET_BAD) ||
1699        (clientfd[i] != CURL_SOCKET_BAD)) {
1700       logmsg("[%s] ABORTING", data_or_ctrl(i));
1701     }
1702   }
1703 
1704   *infdp = CURL_SOCKET_BAD;
1705 }
1706 
http2(struct httprequest * req)1707 static void http2(struct httprequest *req)
1708 {
1709   (void)req;
1710   logmsg("switched to http2");
1711   /* left to implement */
1712 }
1713 
1714 
1715 /* returns a socket handle, or 0 if there are no more waiting sockets,
1716    or < 0 if there was an error */
accept_connection(curl_socket_t sock)1717 static curl_socket_t accept_connection(curl_socket_t sock)
1718 {
1719   curl_socket_t msgsock = CURL_SOCKET_BAD;
1720   int error;
1721   int flag = 1;
1722 
1723   if(MAX_SOCKETS == num_sockets) {
1724     logmsg("Too many open sockets!");
1725     return CURL_SOCKET_BAD;
1726   }
1727 
1728   msgsock = accept(sock, NULL, NULL);
1729 
1730   if(got_exit_signal) {
1731     if(CURL_SOCKET_BAD != msgsock)
1732       sclose(msgsock);
1733     return CURL_SOCKET_BAD;
1734   }
1735 
1736   if(CURL_SOCKET_BAD == msgsock) {
1737     error = SOCKERRNO;
1738     if(EAGAIN == error || EWOULDBLOCK == error) {
1739       /* nothing to accept */
1740       return 0;
1741     }
1742     logmsg("MAJOR ERROR: accept() failed with error: (%d) %s",
1743            error, strerror(error));
1744     return CURL_SOCKET_BAD;
1745   }
1746 
1747   if(0 != curlx_nonblock(msgsock, TRUE)) {
1748     error = SOCKERRNO;
1749     logmsg("curlx_nonblock failed with error: (%d) %s",
1750            error, strerror(error));
1751     sclose(msgsock);
1752     return CURL_SOCKET_BAD;
1753   }
1754 
1755   if(0 != setsockopt(msgsock, SOL_SOCKET, SO_KEEPALIVE,
1756                      (void *)&flag, sizeof(flag))) {
1757     error = SOCKERRNO;
1758     logmsg("setsockopt(SO_KEEPALIVE) failed with error: (%d) %s",
1759            error, strerror(error));
1760     sclose(msgsock);
1761     return CURL_SOCKET_BAD;
1762   }
1763 
1764   /*
1765   ** As soon as this server accepts a connection from the test harness it
1766   ** must set the server logs advisor read lock to indicate that server
1767   ** logs should not be read until this lock is removed by this server.
1768   */
1769 
1770   if(!serverlogslocked)
1771     set_advisor_read_lock(SERVERLOGS_LOCK);
1772   serverlogslocked += 1;
1773 
1774   logmsg("====> Client connect");
1775 
1776   all_sockets[num_sockets] = msgsock;
1777   num_sockets += 1;
1778 
1779 #ifdef TCP_NODELAY
1780   if(socket_domain_is_ip()) {
1781     /*
1782      * Disable the Nagle algorithm to make it easier to send out a large
1783      * response in many small segments to torture the clients more.
1784      */
1785     if(0 != setsockopt(msgsock, IPPROTO_TCP, TCP_NODELAY,
1786                        (void *)&flag, sizeof(flag)))
1787       logmsg("====> TCP_NODELAY failed");
1788   }
1789 #endif
1790 
1791   return msgsock;
1792 }
1793 
1794 /* returns 1 if the connection should be serviced again immediately, 0 if there
1795    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)1796 static int service_connection(curl_socket_t msgsock, struct httprequest *req,
1797                               curl_socket_t listensock,
1798                               const char *connecthost)
1799 {
1800   if(got_exit_signal)
1801     return -1;
1802 
1803   while(!req->done_processing) {
1804     int rc = get_request(msgsock, req);
1805     if(rc <= 0) {
1806       /* Nothing further to read now, possibly because the socket was closed */
1807       return rc;
1808     }
1809   }
1810 
1811   if(prevbounce) {
1812     /* bounce treatment requested */
1813     if((req->testno == prevtestno) &&
1814        (req->partno == prevpartno)) {
1815       req->partno++;
1816       logmsg("BOUNCE part number to %ld", req->partno);
1817     }
1818     else {
1819       prevbounce = FALSE;
1820       prevtestno = -1;
1821       prevpartno = -1;
1822     }
1823   }
1824 
1825   send_doc(msgsock, req);
1826   if(got_exit_signal)
1827     return -1;
1828 
1829   if(req->testno < 0) {
1830     logmsg("special request received, no persistency");
1831     return -1;
1832   }
1833   if(!req->open) {
1834     logmsg("instructed to close connection after server-reply");
1835     return -1;
1836   }
1837 
1838   if(req->connect_request) {
1839     /* a CONNECT request, setup and talk the tunnel */
1840     if(!is_proxy) {
1841       logmsg("received CONNECT but isn't running as proxy!");
1842       return 1;
1843     }
1844     else {
1845       http_connect(&msgsock, listensock, connecthost, req->connect_port);
1846       return -1;
1847     }
1848   }
1849 
1850   if(req->upgrade_request) {
1851     /* an upgrade request, switch to http2 here */
1852     http2(req);
1853     return -1;
1854   }
1855 
1856   /* if we got a CONNECT, loop and get another request as well! */
1857 
1858   if(req->open) {
1859     logmsg("=> persistent connection request ended, awaits new request\n");
1860     return 1;
1861   }
1862 
1863   return -1;
1864 }
1865 
main(int argc,char * argv[])1866 int main(int argc, char *argv[])
1867 {
1868   srvr_sockaddr_union_t me;
1869   curl_socket_t sock = CURL_SOCKET_BAD;
1870   int wrotepidfile = 0;
1871   int flag;
1872   unsigned short port = DEFAULT_PORT;
1873 #ifdef USE_UNIX_SOCKETS
1874   const char *unix_socket = NULL;
1875   bool unlink_socket = false;
1876 #endif
1877   const char *pidname = ".http.pid";
1878   const char *portname = ".http.port";
1879   struct httprequest *req;
1880   int rc = 0;
1881   int error;
1882   int arg = 1;
1883   long pid;
1884   const char *connecthost = "127.0.0.1";
1885   const char *socket_type = "IPv4";
1886   char port_str[11];
1887   const char *location_str = port_str;
1888 
1889   /* a default CONNECT port is basically pointless but still ... */
1890   size_t socket_idx;
1891 
1892   req = calloc(1, sizeof(*req));
1893   if(!req)
1894     return 0;
1895 
1896   while(argc>arg) {
1897     if(!strcmp("--version", argv[arg])) {
1898       puts("sws IPv4"
1899 #ifdef ENABLE_IPV6
1900              "/IPv6"
1901 #endif
1902 #ifdef USE_UNIX_SOCKETS
1903              "/unix"
1904 #endif
1905           );
1906       return 0;
1907     }
1908     else if(!strcmp("--pidfile", argv[arg])) {
1909       arg++;
1910       if(argc>arg)
1911         pidname = argv[arg++];
1912     }
1913     else if(!strcmp("--portfile", argv[arg])) {
1914       arg++;
1915       if(argc>arg)
1916         portname = argv[arg++];
1917     }
1918     else if(!strcmp("--logfile", argv[arg])) {
1919       arg++;
1920       if(argc>arg)
1921         serverlogfile = argv[arg++];
1922     }
1923     else if(!strcmp("--cmdfile", argv[arg])) {
1924       arg++;
1925       if(argc>arg)
1926         cmdfile = argv[arg++];
1927     }
1928     else if(!strcmp("--gopher", argv[arg])) {
1929       arg++;
1930       use_gopher = TRUE;
1931       end_of_headers = "\r\n"; /* gopher style is much simpler */
1932     }
1933     else if(!strcmp("--ipv4", argv[arg])) {
1934       socket_type = "IPv4";
1935       socket_domain = AF_INET;
1936       location_str = port_str;
1937       arg++;
1938     }
1939     else if(!strcmp("--ipv6", argv[arg])) {
1940 #ifdef ENABLE_IPV6
1941       socket_type = "IPv6";
1942       socket_domain = AF_INET6;
1943       location_str = port_str;
1944 #endif
1945       arg++;
1946     }
1947     else if(!strcmp("--unix-socket", argv[arg])) {
1948       arg++;
1949       if(argc>arg) {
1950 #ifdef USE_UNIX_SOCKETS
1951         unix_socket = argv[arg];
1952         if(strlen(unix_socket) >= sizeof(me.sau.sun_path)) {
1953           fprintf(stderr, "sws: socket path must be shorter than %zu chars\n",
1954                   sizeof(me.sau.sun_path));
1955           return 0;
1956         }
1957         socket_type = "unix";
1958         socket_domain = AF_UNIX;
1959         location_str = unix_socket;
1960 #endif
1961         arg++;
1962       }
1963     }
1964     else if(!strcmp("--port", argv[arg])) {
1965       arg++;
1966       if(argc>arg) {
1967         char *endptr;
1968         unsigned long ulnum = strtoul(argv[arg], &endptr, 10);
1969         if((endptr != argv[arg] + strlen(argv[arg])) ||
1970            (ulnum && ((ulnum < 1025UL) || (ulnum > 65535UL)))) {
1971           fprintf(stderr, "sws: invalid --port argument (%s)\n",
1972                   argv[arg]);
1973           return 0;
1974         }
1975         port = curlx_ultous(ulnum);
1976         arg++;
1977       }
1978     }
1979     else if(!strcmp("--srcdir", argv[arg])) {
1980       arg++;
1981       if(argc>arg) {
1982         path = argv[arg];
1983         arg++;
1984       }
1985     }
1986     else if(!strcmp("--connect", argv[arg])) {
1987       /* The connect host IP number that the proxy will connect to no matter
1988          what the client asks for, but also use this as a hint that we run as
1989          a proxy and do a few different internal choices */
1990       arg++;
1991       if(argc>arg) {
1992         connecthost = argv[arg];
1993         arg++;
1994         is_proxy = TRUE;
1995         logmsg("Run as proxy, CONNECT to host %s", connecthost);
1996       }
1997     }
1998     else {
1999       puts("Usage: sws [option]\n"
2000            " --version\n"
2001            " --logfile [file]\n"
2002            " --pidfile [file]\n"
2003            " --portfile [file]\n"
2004            " --ipv4\n"
2005            " --ipv6\n"
2006            " --unix-socket [file]\n"
2007            " --port [port]\n"
2008            " --srcdir [path]\n"
2009            " --connect [ip4-addr]\n"
2010            " --gopher");
2011       return 0;
2012     }
2013   }
2014 
2015 #ifdef WIN32
2016   win32_init();
2017   atexit(win32_cleanup);
2018 #endif
2019 
2020   install_signal_handlers(false);
2021 
2022   pid = (long)getpid();
2023 
2024   sock = socket(socket_domain, SOCK_STREAM, 0);
2025 
2026   all_sockets[0] = sock;
2027   num_sockets = 1;
2028 
2029   if(CURL_SOCKET_BAD == sock) {
2030     error = SOCKERRNO;
2031     logmsg("Error creating socket: (%d) %s",
2032            error, strerror(error));
2033     goto sws_cleanup;
2034   }
2035 
2036   flag = 1;
2037   if(0 != setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
2038                      (void *)&flag, sizeof(flag))) {
2039     error = SOCKERRNO;
2040     logmsg("setsockopt(SO_REUSEADDR) failed with error: (%d) %s",
2041            error, strerror(error));
2042     goto sws_cleanup;
2043   }
2044   if(0 != curlx_nonblock(sock, TRUE)) {
2045     error = SOCKERRNO;
2046     logmsg("curlx_nonblock failed with error: (%d) %s",
2047            error, strerror(error));
2048     goto sws_cleanup;
2049   }
2050 
2051   switch(socket_domain) {
2052   case AF_INET:
2053     memset(&me.sa4, 0, sizeof(me.sa4));
2054     me.sa4.sin_family = AF_INET;
2055     me.sa4.sin_addr.s_addr = INADDR_ANY;
2056     me.sa4.sin_port = htons(port);
2057     rc = bind(sock, &me.sa, sizeof(me.sa4));
2058     break;
2059 #ifdef ENABLE_IPV6
2060   case AF_INET6:
2061     memset(&me.sa6, 0, sizeof(me.sa6));
2062     me.sa6.sin6_family = AF_INET6;
2063     me.sa6.sin6_addr = in6addr_any;
2064     me.sa6.sin6_port = htons(port);
2065     rc = bind(sock, &me.sa, sizeof(me.sa6));
2066     break;
2067 #endif /* ENABLE_IPV6 */
2068 #ifdef USE_UNIX_SOCKETS
2069   case AF_UNIX:
2070     memset(&me.sau, 0, sizeof(me.sau));
2071     me.sau.sun_family = AF_UNIX;
2072     strncpy(me.sau.sun_path, unix_socket, sizeof(me.sau.sun_path) - 1);
2073     rc = bind(sock, &me.sa, sizeof(me.sau));
2074     if(0 != rc && errno == EADDRINUSE) {
2075       struct_stat statbuf;
2076       /* socket already exists. Perhaps it is stale? */
2077       curl_socket_t unixfd = socket(AF_UNIX, SOCK_STREAM, 0);
2078       if(CURL_SOCKET_BAD == unixfd) {
2079         error = SOCKERRNO;
2080         logmsg("Error binding socket, failed to create socket at %s: (%d) %s",
2081                unix_socket, error, strerror(error));
2082         goto sws_cleanup;
2083       }
2084       /* check whether the server is alive */
2085       rc = connect(unixfd, &me.sa, sizeof(me.sau));
2086       error = errno;
2087       sclose(unixfd);
2088       if(ECONNREFUSED != error) {
2089         logmsg("Error binding socket, failed to connect to %s: (%d) %s",
2090                unix_socket, error, strerror(error));
2091         goto sws_cleanup;
2092       }
2093       /* socket server is not alive, now check if it was actually a socket. */
2094 #ifdef WIN32
2095       /* Windows does not have lstat function. */
2096       rc = curlx_win32_stat(unix_socket, &statbuf);
2097 #else
2098       rc = lstat(unix_socket, &statbuf);
2099 #endif
2100       if(0 != rc) {
2101         logmsg("Error binding socket, failed to stat %s: (%d) %s",
2102                unix_socket, errno, strerror(errno));
2103         goto sws_cleanup;
2104       }
2105 #ifdef S_IFSOCK
2106       if((statbuf.st_mode & S_IFSOCK) != S_IFSOCK) {
2107         logmsg("Error binding socket, failed to stat %s: (%d) %s",
2108                unix_socket, error, strerror(error));
2109         goto sws_cleanup;
2110       }
2111 #endif
2112       /* dead socket, cleanup and retry bind */
2113       rc = unlink(unix_socket);
2114       if(0 != rc) {
2115         logmsg("Error binding socket, failed to unlink %s: (%d) %s",
2116                unix_socket, errno, strerror(errno));
2117         goto sws_cleanup;
2118       }
2119       /* stale socket is gone, retry bind */
2120       rc = bind(sock, &me.sa, sizeof(me.sau));
2121     }
2122     break;
2123 #endif /* USE_UNIX_SOCKETS */
2124   }
2125   if(0 != rc) {
2126     error = SOCKERRNO;
2127     logmsg("Error binding socket: (%d) %s", error, strerror(error));
2128     goto sws_cleanup;
2129   }
2130 
2131   if(!port) {
2132     /* The system was supposed to choose a port number, figure out which
2133        port we actually got and update the listener port value with it. */
2134     curl_socklen_t la_size;
2135     srvr_sockaddr_union_t localaddr;
2136 #ifdef ENABLE_IPV6
2137     if(socket_domain != AF_INET6)
2138 #endif
2139       la_size = sizeof(localaddr.sa4);
2140 #ifdef ENABLE_IPV6
2141     else
2142       la_size = sizeof(localaddr.sa6);
2143 #endif
2144     memset(&localaddr.sa, 0, (size_t)la_size);
2145     if(getsockname(sock, &localaddr.sa, &la_size) < 0) {
2146       error = SOCKERRNO;
2147       logmsg("getsockname() failed with error: (%d) %s",
2148              error, strerror(error));
2149       sclose(sock);
2150       goto sws_cleanup;
2151     }
2152     switch(localaddr.sa.sa_family) {
2153     case AF_INET:
2154       port = ntohs(localaddr.sa4.sin_port);
2155       break;
2156 #ifdef ENABLE_IPV6
2157     case AF_INET6:
2158       port = ntohs(localaddr.sa6.sin6_port);
2159       break;
2160 #endif
2161     default:
2162       break;
2163     }
2164     if(!port) {
2165       /* Real failure, listener port shall not be zero beyond this point. */
2166       logmsg("Apparently getsockname() succeeded, with listener port zero.");
2167       logmsg("A valid reason for this failure is a binary built without");
2168       logmsg("proper network library linkage. This might not be the only");
2169       logmsg("reason, but double check it before anything else.");
2170       sclose(sock);
2171       goto sws_cleanup;
2172     }
2173   }
2174 #ifdef USE_UNIX_SOCKETS
2175   if(socket_domain != AF_UNIX)
2176 #endif
2177     msnprintf(port_str, sizeof(port_str), "port %hu", port);
2178 
2179   logmsg("Running %s %s version on %s",
2180          use_gopher?"GOPHER":"HTTP", socket_type, location_str);
2181 
2182   /* start accepting connections */
2183   rc = listen(sock, 5);
2184   if(0 != rc) {
2185     error = SOCKERRNO;
2186     logmsg("listen() failed with error: (%d) %s",
2187            error, strerror(error));
2188     goto sws_cleanup;
2189   }
2190 
2191 #ifdef USE_UNIX_SOCKETS
2192   /* listen succeeds, so let's assume a valid listening Unix socket */
2193   unlink_socket = true;
2194 #endif
2195 
2196   /*
2197   ** As soon as this server writes its pid file the test harness will
2198   ** attempt to connect to this server and initiate its verification.
2199   */
2200 
2201   wrotepidfile = write_pidfile(pidname);
2202   if(!wrotepidfile)
2203     goto sws_cleanup;
2204 
2205   wrotepidfile = write_portfile(portname, port);
2206   if(!wrotepidfile)
2207     goto sws_cleanup;
2208 
2209   /* initialization of httprequest struct is done before get_request(), but
2210      the pipelining struct field must be initialized previously to FALSE
2211      every time a new connection arrives. */
2212 
2213   init_httprequest(req);
2214 
2215   for(;;) {
2216     fd_set input;
2217     fd_set output;
2218     struct timeval timeout = {0, 250000L}; /* 250 ms */
2219     curl_socket_t maxfd = (curl_socket_t)-1;
2220     int active;
2221 
2222     /* Clear out closed sockets */
2223     for(socket_idx = num_sockets - 1; socket_idx >= 1; --socket_idx) {
2224       if(CURL_SOCKET_BAD == all_sockets[socket_idx]) {
2225         char *dst = (char *) (all_sockets + socket_idx);
2226         char *src = (char *) (all_sockets + socket_idx + 1);
2227         char *end = (char *) (all_sockets + num_sockets);
2228         memmove(dst, src, end - src);
2229         num_sockets -= 1;
2230       }
2231     }
2232 
2233     if(got_exit_signal)
2234       goto sws_cleanup;
2235 
2236     /* Set up for select */
2237     FD_ZERO(&input);
2238     FD_ZERO(&output);
2239 
2240     for(socket_idx = 0; socket_idx < num_sockets; ++socket_idx) {
2241       /* Listen on all sockets */
2242       FD_SET(all_sockets[socket_idx], &input);
2243       if(all_sockets[socket_idx] > maxfd)
2244         maxfd = all_sockets[socket_idx];
2245     }
2246 
2247     if(got_exit_signal)
2248       goto sws_cleanup;
2249 
2250     do {
2251       rc = select((int)maxfd + 1, &input, &output, NULL, &timeout);
2252     } while(rc < 0 && errno == EINTR && !got_exit_signal);
2253 
2254     if(got_exit_signal)
2255       goto sws_cleanup;
2256 
2257     if(rc < 0) {
2258       error = SOCKERRNO;
2259       logmsg("select() failed with error: (%d) %s",
2260              error, strerror(error));
2261       goto sws_cleanup;
2262     }
2263 
2264     if(rc == 0) {
2265       /* Timed out - try again */
2266       continue;
2267     }
2268     active = rc; /* a positive number */
2269 
2270     /* Check if the listening socket is ready to accept */
2271     if(FD_ISSET(all_sockets[0], &input)) {
2272       /* Service all queued connections */
2273       curl_socket_t msgsock;
2274       do {
2275         msgsock = accept_connection(sock);
2276         logmsg("accept_connection %d returned %d", sock, msgsock);
2277         if(CURL_SOCKET_BAD == msgsock)
2278           goto sws_cleanup;
2279       } while(msgsock > 0);
2280       active--;
2281     }
2282 
2283     /* Service all connections that are ready */
2284     for(socket_idx = 1; (socket_idx < num_sockets) && active; ++socket_idx) {
2285       if(FD_ISSET(all_sockets[socket_idx], &input)) {
2286         active--;
2287         if(got_exit_signal)
2288           goto sws_cleanup;
2289 
2290         /* Service this connection until it has nothing available */
2291         do {
2292           rc = service_connection(all_sockets[socket_idx], req, sock,
2293                                   connecthost);
2294           if(got_exit_signal)
2295             goto sws_cleanup;
2296 
2297           if(rc < 0) {
2298             logmsg("====> Client disconnect %d", req->connmon);
2299 
2300             if(req->connmon) {
2301               const char *keepopen = "[DISCONNECT]\n";
2302               storerequest(keepopen, strlen(keepopen));
2303             }
2304 
2305             if(!req->open)
2306               /* When instructed to close connection after server-reply we
2307                  wait a very small amount of time before doing so. If this
2308                  is not done client might get an ECONNRESET before reading
2309                  a single byte of server-reply. */
2310               wait_ms(50);
2311 
2312             if(all_sockets[socket_idx] != CURL_SOCKET_BAD) {
2313               sclose(all_sockets[socket_idx]);
2314               all_sockets[socket_idx] = CURL_SOCKET_BAD;
2315             }
2316 
2317             serverlogslocked -= 1;
2318             if(!serverlogslocked)
2319               clear_advisor_read_lock(SERVERLOGS_LOCK);
2320 
2321             if(req->testno == DOCNUMBER_QUIT)
2322               goto sws_cleanup;
2323           }
2324 
2325           /* Reset the request, unless we're still in the middle of reading */
2326           if(rc)
2327             init_httprequest(req);
2328         } while(rc > 0);
2329       }
2330     }
2331 
2332     if(got_exit_signal)
2333       goto sws_cleanup;
2334   }
2335 
2336 sws_cleanup:
2337 
2338   for(socket_idx = 1; socket_idx < num_sockets; ++socket_idx)
2339     if((all_sockets[socket_idx] != sock) &&
2340      (all_sockets[socket_idx] != CURL_SOCKET_BAD))
2341       sclose(all_sockets[socket_idx]);
2342 
2343   if(sock != CURL_SOCKET_BAD)
2344     sclose(sock);
2345 
2346 #ifdef USE_UNIX_SOCKETS
2347   if(unlink_socket && socket_domain == AF_UNIX) {
2348     rc = unlink(unix_socket);
2349     logmsg("unlink(%s) = %d (%s)", unix_socket, rc, strerror(rc));
2350   }
2351 #endif
2352 
2353   if(got_exit_signal)
2354     logmsg("signalled to die");
2355 
2356   if(wrotepidfile)
2357     unlink(pidname);
2358 
2359   if(serverlogslocked) {
2360     serverlogslocked = 0;
2361     clear_advisor_read_lock(SERVERLOGS_LOCK);
2362   }
2363 
2364   restore_signal_handlers(false);
2365 
2366   if(got_exit_signal) {
2367     logmsg("========> %s sws (%s pid: %ld) exits with signal (%d)",
2368            socket_type, location_str, pid, exit_signal);
2369     /*
2370      * To properly set the return status of the process we
2371      * must raise the same signal SIGINT or SIGTERM that we
2372      * caught and let the old handler take care of it.
2373      */
2374     raise(exit_signal);
2375   }
2376 
2377   logmsg("========> sws quits");
2378   return 0;
2379 }
2380