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