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