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