• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at https://curl.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * SPDX-License-Identifier: curl
22  *
23  ***************************************************************************/
24 #include "server_setup.h"
25 
26 /* sws.c: simple (silly?) web server
27 
28    This code was originally graciously donated to the project by Juergen
29    Wilke. Thanks a bunch!
30 
31  */
32 
33 #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 override the
88                                    requested part number to prevpartno + 1 when
89                                    prevtestno and current test are the same */
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   res = fclose(dump);
836   if(res)
837     logmsg("Error closing file %s error: %d %s",
838            dumpfile, errno, strerror(errno));
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 = {0};
904           timeout.tv_sec = 1; /* 1000 ms */
905 
906           logmsg("Got EAGAIN from sread");
907           FD_ZERO(&input);
908           FD_ZERO(&output);
909           got = 0;
910 #if defined(__DJGPP__)
911 #pragma GCC diagnostic push
912 #pragma GCC diagnostic ignored "-Warith-conversion"
913 #endif
914           FD_SET(sock, &input);
915 #if defined(__DJGPP__)
916 #pragma GCC diagnostic pop
917 #endif
918           do {
919             logmsg("Wait until readable");
920             rc = select((int)sock + 1, &input, &output, NULL, &timeout);
921           } while(rc < 0 && errno == EINTR && !got_exit_signal);
922           logmsg("readable %d", rc);
923           if(rc)
924             got = 1;
925         }
926       } while(got > 0);
927     }
928     else {
929       logmsg("NO wait for websocket traffic");
930     }
931     if(req->offset) {
932       logmsg("log the websocket traffic");
933       /* dump the incoming websocket traffic to the external file */
934       reqbuf[req->offset] = '\0';
935       storerequest(reqbuf, req->offset);
936       req->offset = 0;
937     }
938     init_httprequest(req);
939 
940     return -1;
941   }
942 
943   if(req->offset >= REQBUFSIZ-1) {
944     /* buffer is already full; do nothing */
945     overflow = 1;
946   }
947   else {
948     if(req->skip)
949       /* we are instructed to not read the entire thing, so we make sure to
950          only read what we're supposed to and NOT read the entire thing the
951          client wants to send! */
952       got = sread(sock, reqbuf + req->offset, req->cl);
953     else
954       got = sread(sock, reqbuf + req->offset, REQBUFSIZ-1 - req->offset);
955 
956     if(got_exit_signal)
957       return -1;
958     if(got == 0) {
959       logmsg("Connection closed by client");
960       fail = 1;
961     }
962     else if(got < 0) {
963       int error = SOCKERRNO;
964       if(EAGAIN == error || EWOULDBLOCK == error) {
965         /* nothing to read at the moment */
966         return 0;
967       }
968       logmsg("recv() returned error: (%d) %s", error, sstrerror(error));
969       fail = 1;
970     }
971     if(fail) {
972       /* dump the request received so far to the external file */
973       reqbuf[req->offset] = '\0';
974       storerequest(reqbuf, req->offset);
975       return -1;
976     }
977 
978     logmsg("Read %zd bytes", got);
979 
980     req->offset += (size_t)got;
981     reqbuf[req->offset] = '\0';
982 
983     req->done_processing = ProcessRequest(req);
984     if(got_exit_signal)
985       return -1;
986   }
987 
988   if(overflow || (req->offset == REQBUFSIZ-1 && got > 0)) {
989     logmsg("Request would overflow buffer, closing connection");
990     /* dump request received so far to external file anyway */
991     reqbuf[REQBUFSIZ-1] = '\0';
992     fail = 1;
993   }
994   else if(req->offset > REQBUFSIZ-1) {
995     logmsg("Request buffer overflow, closing connection");
996     /* dump request received so far to external file anyway */
997     reqbuf[REQBUFSIZ-1] = '\0';
998     fail = 1;
999   }
1000   else
1001     reqbuf[req->offset] = '\0';
1002 
1003   /* at the end of a request dump it to an external file */
1004   if(fail || req->done_processing)
1005     storerequest(reqbuf, req->offset);
1006   if(got_exit_signal)
1007     return -1;
1008 
1009   return fail ? -1 : 1;
1010 }
1011 
1012 /* returns -1 on failure */
send_doc(curl_socket_t sock,struct httprequest * req)1013 static int send_doc(curl_socket_t sock, struct httprequest *req)
1014 {
1015   ssize_t written;
1016   size_t count;
1017   const char *buffer;
1018   char *ptr = NULL;
1019   FILE *stream;
1020   char *cmd = NULL;
1021   size_t cmdsize = 0;
1022   FILE *dump;
1023   bool persistent = TRUE;
1024   bool sendfailure = FALSE;
1025   size_t responsesize;
1026   int error = 0;
1027   int res;
1028   static char weare[256];
1029   char responsedump[256];
1030 
1031   msnprintf(responsedump, sizeof(responsedump), "%s/%s",
1032             logdir, is_proxy ? RESPONSE_PROXY_DUMP : RESPONSE_DUMP);
1033 
1034   switch(req->rcmd) {
1035   default:
1036   case RCMD_NORMALREQ:
1037     break; /* continue with business as usual */
1038   case RCMD_STREAM:
1039 #define STREAMTHIS "a string to stream 01234567890\n"
1040     count = strlen(STREAMTHIS);
1041     for(;;) {
1042       written = swrite(sock, STREAMTHIS, count);
1043       if(got_exit_signal)
1044         return -1;
1045       if(written != (ssize_t)count) {
1046         logmsg("Stopped streaming");
1047         break;
1048       }
1049     }
1050     return -1;
1051   case RCMD_IDLE:
1052     /* Do nothing. Sit idle. Pretend it rains. */
1053     return 0;
1054   }
1055 
1056   req->open = FALSE;
1057 
1058   if(req->testno < 0) {
1059     size_t msglen;
1060     char msgbuf[64];
1061 
1062     switch(req->testno) {
1063     case DOCNUMBER_QUIT:
1064       logmsg("Replying to QUIT");
1065       buffer = docquit;
1066       break;
1067     case DOCNUMBER_WERULEZ:
1068       /* we got a "friends?" question, reply back that we sure are */
1069       logmsg("Identifying ourselves as friends");
1070       msnprintf(msgbuf, sizeof(msgbuf), "WE ROOLZ: %"
1071                 CURL_FORMAT_CURL_OFF_T "\r\n", our_getpid());
1072       msglen = strlen(msgbuf);
1073       if(use_gopher)
1074         msnprintf(weare, sizeof(weare), "%s", msgbuf);
1075       else
1076         msnprintf(weare, sizeof(weare),
1077                   "HTTP/1.1 200 OK\r\nContent-Length: %zu\r\n\r\n%s",
1078                   msglen, msgbuf);
1079       buffer = weare;
1080       break;
1081     case DOCNUMBER_404:
1082     default:
1083       logmsg("Replying to with a 404");
1084       buffer = doc404;
1085       break;
1086     }
1087 
1088     count = strlen(buffer);
1089   }
1090   else {
1091     char partbuf[80];
1092 
1093     /* select the <data> tag for "normal" requests and the <connect> one
1094        for CONNECT requests (within the <reply> section) */
1095     const char *section = req->connect_request ? "connect" : "data";
1096 
1097     if(req->partno)
1098       msnprintf(partbuf, sizeof(partbuf), "%s%ld", section, req->partno);
1099     else
1100       msnprintf(partbuf, sizeof(partbuf), "%s", section);
1101 
1102     logmsg("Send response test%ld section <%s>", req->testno, partbuf);
1103 
1104     stream = test2fopen(req->testno, logdir);
1105     if(!stream) {
1106       error = errno;
1107       logmsg("fopen() failed with error: %d %s", error, strerror(error));
1108       return 0;
1109     }
1110     else {
1111       error = getpart(&ptr, &count, "reply", partbuf, stream);
1112       fclose(stream);
1113       if(error) {
1114         logmsg("getpart() failed with error: %d", error);
1115         return 0;
1116       }
1117       buffer = ptr;
1118     }
1119 
1120     if(got_exit_signal) {
1121       free(ptr);
1122       return -1;
1123     }
1124 
1125     /* re-open the same file again */
1126     stream = test2fopen(req->testno, logdir);
1127     if(!stream) {
1128       error = errno;
1129       logmsg("fopen() failed with error: %d %s", error, strerror(error));
1130       free(ptr);
1131       return 0;
1132     }
1133     else {
1134       /* get the custom server control "commands" */
1135       error = getpart(&cmd, &cmdsize, "reply", "postcmd", stream);
1136       fclose(stream);
1137       if(error) {
1138         logmsg("getpart() failed with error: %d", error);
1139         free(ptr);
1140         return 0;
1141       }
1142     }
1143   }
1144 
1145   if(got_exit_signal) {
1146     free(ptr);
1147     free(cmd);
1148     return -1;
1149   }
1150 
1151   /* If the word 'swsclose' is present anywhere in the reply chunk, the
1152      connection will be closed after the data has been sent to the requesting
1153      client... */
1154   if(strstr(buffer, "swsclose") || !count || req->close) {
1155     persistent = FALSE;
1156     logmsg("connection close instruction \"swsclose\" found in response");
1157   }
1158   if(strstr(buffer, "swsbounce")) {
1159     prevbounce = TRUE;
1160     logmsg("enable \"swsbounce\" in the next request");
1161   }
1162   else
1163     prevbounce = FALSE;
1164 
1165   dump = fopen(responsedump, "ab");
1166   if(!dump) {
1167     error = errno;
1168     logmsg("fopen() failed with error: %d %s", error, strerror(error));
1169     logmsg("  [5] Error opening file: %s", responsedump);
1170     free(ptr);
1171     free(cmd);
1172     return -1;
1173   }
1174 
1175   responsesize = count;
1176   do {
1177     /* Ok, we send no more than N bytes at a time, just to make sure that
1178        larger chunks are split up so that the client will need to do multiple
1179        recv() calls to get it and thus we exercise that code better */
1180     size_t num = count;
1181     if(num > 20)
1182       num = 20;
1183 
1184 retry:
1185     written = swrite(sock, buffer, num);
1186     if(written < 0) {
1187       if((EWOULDBLOCK == SOCKERRNO) || (EAGAIN == SOCKERRNO)) {
1188         wait_ms(10);
1189         goto retry;
1190       }
1191       sendfailure = TRUE;
1192       break;
1193     }
1194 
1195     /* write to file as well */
1196     fwrite(buffer, 1, (size_t)written, dump);
1197 
1198     count -= written;
1199     buffer += written;
1200 
1201     if(req->writedelay) {
1202       int msecs_left = req->writedelay;
1203       int intervals = msecs_left / MAX_SLEEP_TIME_MS;
1204       if(msecs_left%MAX_SLEEP_TIME_MS)
1205         intervals++;
1206       logmsg("Pausing %d milliseconds after writing %zd bytes",
1207              msecs_left, written);
1208       while((intervals > 0) && !got_exit_signal) {
1209         int sleep_time = msecs_left > MAX_SLEEP_TIME_MS ?
1210           MAX_SLEEP_TIME_MS : msecs_left;
1211         intervals--;
1212         wait_ms(sleep_time);
1213         msecs_left -= sleep_time;
1214       }
1215     }
1216   } while((count > 0) && !got_exit_signal);
1217 
1218   res = fclose(dump);
1219   if(res)
1220     logmsg("Error closing file %s error: %d %s",
1221            responsedump, errno, strerror(errno));
1222 
1223   if(got_exit_signal) {
1224     free(ptr);
1225     free(cmd);
1226     return -1;
1227   }
1228 
1229   if(sendfailure) {
1230     logmsg("Sending response failed. Only (%zu bytes) of (%zu bytes) "
1231            "were sent",
1232            responsesize-count, responsesize);
1233     prevtestno = req->testno;
1234     prevpartno = req->partno;
1235     free(ptr);
1236     free(cmd);
1237     return -1;
1238   }
1239 
1240   logmsg("Response sent (%zu bytes) and written to %s",
1241          responsesize, responsedump);
1242   free(ptr);
1243 
1244   if(cmdsize > 0) {
1245     char command[32];
1246     int quarters;
1247     int num;
1248     ptr = cmd;
1249     do {
1250       if(2 == sscanf(ptr, "%31s %d", command, &num)) {
1251         if(!strcmp("wait", command)) {
1252           logmsg("Told to sleep for %d seconds", num);
1253           quarters = num * 4;
1254           while((quarters > 0) && !got_exit_signal) {
1255             quarters--;
1256             res = wait_ms(250);
1257             if(res) {
1258               /* should not happen */
1259               error = errno;
1260               logmsg("wait_ms() failed with error: (%d) %s",
1261                      error, strerror(error));
1262               break;
1263             }
1264           }
1265           if(!quarters)
1266             logmsg("Continuing after sleeping %d seconds", num);
1267         }
1268         else
1269           logmsg("Unknown command in reply command section");
1270       }
1271       ptr = strchr(ptr, '\n');
1272       if(ptr)
1273         ptr++;
1274       else
1275         ptr = NULL;
1276     } while(ptr && *ptr);
1277   }
1278   free(cmd);
1279   req->open = use_gopher ? FALSE : persistent;
1280 
1281   prevtestno = req->testno;
1282   prevpartno = req->partno;
1283 
1284   return 0;
1285 }
1286 
connect_to(const char * ipaddr,unsigned short port)1287 static curl_socket_t connect_to(const char *ipaddr, unsigned short port)
1288 {
1289   srvr_sockaddr_union_t serveraddr;
1290   curl_socket_t serverfd;
1291   int error;
1292   int rc = 0;
1293   const char *op_br = "";
1294   const char *cl_br = "";
1295 
1296 #ifdef USE_IPV6
1297   if(socket_domain == AF_INET6) {
1298     op_br = "[";
1299     cl_br = "]";
1300   }
1301 #endif
1302 
1303   if(!ipaddr)
1304     return CURL_SOCKET_BAD;
1305 
1306   logmsg("about to connect to %s%s%s:%hu",
1307          op_br, ipaddr, cl_br, port);
1308 
1309 
1310   serverfd = socket(socket_domain, SOCK_STREAM, 0);
1311   if(CURL_SOCKET_BAD == serverfd) {
1312     error = SOCKERRNO;
1313     logmsg("Error creating socket for server connection: (%d) %s",
1314            error, sstrerror(error));
1315     return CURL_SOCKET_BAD;
1316   }
1317 
1318 #ifdef TCP_NODELAY
1319   if(socket_domain_is_ip()) {
1320     /* Disable the Nagle algorithm */
1321     curl_socklen_t flag = 1;
1322     if(0 != setsockopt(serverfd, IPPROTO_TCP, TCP_NODELAY,
1323                        (void *)&flag, sizeof(flag)))
1324       logmsg("====> TCP_NODELAY for server connection failed");
1325   }
1326 #endif
1327 
1328   /* We want to do the connect() in a non-blocking mode, since
1329    * Windows has an internal retry logic that may lead to long
1330    * timeouts if the peer is not listening. */
1331   if(0 != curlx_nonblock(serverfd, TRUE)) {
1332     error = SOCKERRNO;
1333     logmsg("curlx_nonblock(TRUE) failed with error: (%d) %s",
1334            error, sstrerror(error));
1335     sclose(serverfd);
1336     return CURL_SOCKET_BAD;
1337   }
1338 
1339   switch(socket_domain) {
1340   case AF_INET:
1341     memset(&serveraddr.sa4, 0, sizeof(serveraddr.sa4));
1342     serveraddr.sa4.sin_family = AF_INET;
1343     serveraddr.sa4.sin_port = htons(port);
1344     if(Curl_inet_pton(AF_INET, ipaddr, &serveraddr.sa4.sin_addr) < 1) {
1345       logmsg("Error inet_pton failed AF_INET conversion of '%s'", ipaddr);
1346       sclose(serverfd);
1347       return CURL_SOCKET_BAD;
1348     }
1349 
1350     rc = connect(serverfd, &serveraddr.sa, sizeof(serveraddr.sa4));
1351     break;
1352 #ifdef USE_IPV6
1353   case AF_INET6:
1354     memset(&serveraddr.sa6, 0, sizeof(serveraddr.sa6));
1355     serveraddr.sa6.sin6_family = AF_INET6;
1356     serveraddr.sa6.sin6_port = htons(port);
1357     if(Curl_inet_pton(AF_INET6, ipaddr, &serveraddr.sa6.sin6_addr) < 1) {
1358       logmsg("Error inet_pton failed AF_INET6 conversion of '%s'", ipaddr);
1359       sclose(serverfd);
1360       return CURL_SOCKET_BAD;
1361     }
1362 
1363     rc = connect(serverfd, &serveraddr.sa, sizeof(serveraddr.sa6));
1364     break;
1365 #endif /* USE_IPV6 */
1366 #ifdef USE_UNIX_SOCKETS
1367   case AF_UNIX:
1368     logmsg("Proxying through Unix socket is not (yet?) supported.");
1369     return CURL_SOCKET_BAD;
1370 #endif /* USE_UNIX_SOCKETS */
1371   }
1372 
1373   if(got_exit_signal) {
1374     sclose(serverfd);
1375     return CURL_SOCKET_BAD;
1376   }
1377 
1378   if(rc) {
1379     error = SOCKERRNO;
1380     if((error == EINPROGRESS) || (error == EWOULDBLOCK)) {
1381       fd_set output;
1382       struct timeval timeout = {0};
1383       timeout.tv_sec = 1; /* 1000 ms */
1384 
1385       FD_ZERO(&output);
1386 #if defined(__DJGPP__)
1387 #pragma GCC diagnostic push
1388 #pragma GCC diagnostic ignored "-Warith-conversion"
1389 #endif
1390       FD_SET(serverfd, &output);
1391 #if defined(__DJGPP__)
1392 #pragma GCC diagnostic pop
1393 #endif
1394       while(1) {
1395         rc = select((int)serverfd + 1, NULL, &output, NULL, &timeout);
1396         if(rc < 0 && SOCKERRNO != EINTR)
1397           goto error;
1398         else if(rc > 0) {
1399           curl_socklen_t errSize = sizeof(error);
1400           if(0 != getsockopt(serverfd, SOL_SOCKET, SO_ERROR,
1401                              (void *)&error, &errSize))
1402             error = SOCKERRNO;
1403           if((0 == error) || (EISCONN == error))
1404             goto success;
1405           else if((error != EINPROGRESS) && (error != EWOULDBLOCK))
1406             goto error;
1407         }
1408         else if(!rc) {
1409           logmsg("Timeout connecting to server port %hu", port);
1410           sclose(serverfd);
1411           return CURL_SOCKET_BAD;
1412         }
1413       }
1414     }
1415 error:
1416     logmsg("Error connecting to server port %hu: (%d) %s",
1417            port, error, sstrerror(error));
1418     sclose(serverfd);
1419     return CURL_SOCKET_BAD;
1420   }
1421 success:
1422   logmsg("connected fine to %s%s%s:%hu, now tunnel",
1423          op_br, ipaddr, cl_br, port);
1424 
1425   if(0 != curlx_nonblock(serverfd, FALSE)) {
1426     error = SOCKERRNO;
1427     logmsg("curlx_nonblock(FALSE) failed with error: (%d) %s",
1428            error, sstrerror(error));
1429     sclose(serverfd);
1430     return CURL_SOCKET_BAD;
1431   }
1432 
1433   return serverfd;
1434 }
1435 
1436 /*
1437  * A CONNECT has been received, a CONNECT response has been sent.
1438  *
1439  * This function needs to connect to the server, and then pass data between
1440  * the client and the server back and forth until the connection is closed by
1441  * either end.
1442  *
1443  * When doing FTP through a CONNECT proxy, we expect that the data connection
1444  * will be setup while the first connect is still being kept up. Therefore we
1445  * must accept a new connection and deal with it appropriately.
1446  */
1447 
1448 #define data_or_ctrl(x) ((x)?"DATA":"CTRL")
1449 
1450 #define SWS_CTRL  0
1451 #define SWS_DATA  1
1452 
http_connect(curl_socket_t * infdp,curl_socket_t rootfd,const char * ipaddr,unsigned short ipport,int keepalive_secs)1453 static void http_connect(curl_socket_t *infdp,
1454                          curl_socket_t rootfd,
1455                          const char *ipaddr,
1456                          unsigned short ipport,
1457                          int keepalive_secs)
1458 {
1459   curl_socket_t serverfd[2] = {CURL_SOCKET_BAD, CURL_SOCKET_BAD};
1460   curl_socket_t clientfd[2] = {CURL_SOCKET_BAD, CURL_SOCKET_BAD};
1461   ssize_t toc[2] = {0, 0}; /* number of bytes to client */
1462   ssize_t tos[2] = {0, 0}; /* number of bytes to server */
1463   char readclient[2][256];
1464   char readserver[2][256];
1465   bool poll_client_rd[2] = { TRUE, TRUE };
1466   bool poll_server_rd[2] = { TRUE, TRUE };
1467   bool poll_client_wr[2] = { TRUE, TRUE };
1468   bool poll_server_wr[2] = { TRUE, TRUE };
1469   bool primary = FALSE;
1470   bool secondary = FALSE;
1471   int max_tunnel_idx; /* SWS_CTRL or SWS_DATA */
1472   int loop;
1473   int i;
1474   int timeout_count = 0;
1475 
1476   /* primary tunnel client endpoint already connected */
1477   clientfd[SWS_CTRL] = *infdp;
1478 
1479   /* Sleep here to make sure the client reads CONNECT response's
1480      'end of headers' separate from the server data that follows.
1481      This is done to prevent triggering libcurl known bug #39. */
1482   for(loop = 2; (loop > 0) && !got_exit_signal; loop--)
1483     wait_ms(250);
1484   if(got_exit_signal)
1485     goto http_connect_cleanup;
1486 
1487   serverfd[SWS_CTRL] = connect_to(ipaddr, ipport);
1488   if(serverfd[SWS_CTRL] == CURL_SOCKET_BAD)
1489     goto http_connect_cleanup;
1490 
1491   /* Primary tunnel socket endpoints are now connected. Tunnel data back and
1492      forth over the primary tunnel until client or server breaks the primary
1493      tunnel, simultaneously allowing establishment, operation and teardown of
1494      a secondary tunnel that may be used for passive FTP data connection. */
1495 
1496   max_tunnel_idx = SWS_CTRL;
1497   primary = TRUE;
1498 
1499   while(!got_exit_signal) {
1500 
1501     fd_set input;
1502     fd_set output;
1503     ssize_t rc;
1504     curl_socket_t maxfd = (curl_socket_t)-1;
1505     struct timeval timeout = {0};
1506     timeout.tv_sec = 1; /* 1000 ms */
1507 
1508     FD_ZERO(&input);
1509     FD_ZERO(&output);
1510 
1511     if((clientfd[SWS_DATA] == CURL_SOCKET_BAD) &&
1512        (serverfd[SWS_DATA] == CURL_SOCKET_BAD) &&
1513        poll_client_rd[SWS_CTRL] && poll_client_wr[SWS_CTRL] &&
1514        poll_server_rd[SWS_CTRL] && poll_server_wr[SWS_CTRL]) {
1515       /* listener socket is monitored to allow client to establish
1516          secondary tunnel only when this tunnel is not established
1517          and primary one is fully operational */
1518 #if defined(__DJGPP__)
1519 #pragma GCC diagnostic push
1520 #pragma GCC diagnostic ignored "-Warith-conversion"
1521 #endif
1522       FD_SET(rootfd, &input);
1523 #if defined(__DJGPP__)
1524 #pragma GCC diagnostic pop
1525 #endif
1526       maxfd = rootfd;
1527     }
1528 
1529     /* set tunnel sockets to wait for */
1530     for(i = 0; i <= max_tunnel_idx; i++) {
1531       /* client side socket monitoring */
1532       if(clientfd[i] != CURL_SOCKET_BAD) {
1533         if(poll_client_rd[i]) {
1534           /* unless told not to do so, monitor readability */
1535 #if defined(__DJGPP__)
1536 #pragma GCC diagnostic push
1537 #pragma GCC diagnostic ignored "-Warith-conversion"
1538 #endif
1539           FD_SET(clientfd[i], &input);
1540 #if defined(__DJGPP__)
1541 #pragma GCC diagnostic pop
1542 #endif
1543           if(clientfd[i] > maxfd)
1544             maxfd = clientfd[i];
1545         }
1546         if(poll_client_wr[i] && toc[i]) {
1547           /* unless told not to do so, monitor writability
1548              if there is data ready to be sent to client */
1549 #if defined(__DJGPP__)
1550 #pragma GCC diagnostic push
1551 #pragma GCC diagnostic ignored "-Warith-conversion"
1552 #endif
1553           FD_SET(clientfd[i], &output);
1554 #if defined(__DJGPP__)
1555 #pragma GCC diagnostic pop
1556 #endif
1557           if(clientfd[i] > maxfd)
1558             maxfd = clientfd[i];
1559         }
1560       }
1561       /* server side socket monitoring */
1562       if(serverfd[i] != CURL_SOCKET_BAD) {
1563         if(poll_server_rd[i]) {
1564           /* unless told not to do so, monitor readability */
1565 #if defined(__DJGPP__)
1566 #pragma GCC diagnostic push
1567 #pragma GCC diagnostic ignored "-Warith-conversion"
1568 #endif
1569           FD_SET(serverfd[i], &input);
1570 #if defined(__DJGPP__)
1571 #pragma GCC diagnostic pop
1572 #endif
1573           if(serverfd[i] > maxfd)
1574             maxfd = serverfd[i];
1575         }
1576         if(poll_server_wr[i] && tos[i]) {
1577           /* unless told not to do so, monitor writability
1578              if there is data ready to be sent to server */
1579 #if defined(__DJGPP__)
1580 #pragma GCC diagnostic push
1581 #pragma GCC diagnostic ignored "-Warith-conversion"
1582 #endif
1583           FD_SET(serverfd[i], &output);
1584 #if defined(__DJGPP__)
1585 #pragma GCC diagnostic pop
1586 #endif
1587           if(serverfd[i] > maxfd)
1588             maxfd = serverfd[i];
1589         }
1590       }
1591     }
1592     if(got_exit_signal)
1593       break;
1594 
1595     do {
1596       rc = select((int)maxfd + 1, &input, &output, NULL, &timeout);
1597     } while(rc < 0 && errno == EINTR && !got_exit_signal);
1598 
1599     if(got_exit_signal)
1600       break;
1601 
1602     if(rc > 0) {
1603       /* socket action */
1604       bool tcp_fin_wr = FALSE;
1605       timeout_count = 0;
1606 
1607       /* ---------------------------------------------------------- */
1608 
1609       /* passive mode FTP may establish a secondary tunnel */
1610       if((clientfd[SWS_DATA] == CURL_SOCKET_BAD) &&
1611          (serverfd[SWS_DATA] == CURL_SOCKET_BAD) && FD_ISSET(rootfd, &input)) {
1612         /* a new connection on listener socket (most likely from client) */
1613         curl_socket_t datafd = accept(rootfd, NULL, NULL);
1614         if(datafd != CURL_SOCKET_BAD) {
1615           static struct httprequest *req2;
1616           int err = 0;
1617           if(!req2) {
1618             req2 = malloc(sizeof(*req2));
1619             if(!req2)
1620               exit(1);
1621           }
1622           memset(req2, 0, sizeof(*req2));
1623           logmsg("====> Client connect DATA");
1624 #ifdef TCP_NODELAY
1625           if(socket_domain_is_ip()) {
1626             /* Disable the Nagle algorithm */
1627             curl_socklen_t flag = 1;
1628             if(0 != setsockopt(datafd, IPPROTO_TCP, TCP_NODELAY,
1629                                (void *)&flag, sizeof(flag)))
1630               logmsg("====> TCP_NODELAY for client DATA connection failed");
1631           }
1632 #endif
1633           init_httprequest(req2);
1634           while(!req2->done_processing) {
1635             err = get_request(datafd, req2);
1636             if(err < 0) {
1637               /* this socket must be closed, done or not */
1638               break;
1639             }
1640           }
1641 
1642           /* skip this and close the socket if err < 0 */
1643           if(err >= 0) {
1644             err = send_doc(datafd, req2);
1645             if(!err && req2->connect_request) {
1646               /* sleep to prevent triggering libcurl known bug #39. */
1647               for(loop = 2; (loop > 0) && !got_exit_signal; loop--)
1648                 wait_ms(250);
1649               if(!got_exit_signal) {
1650                 /* connect to the server */
1651                 serverfd[SWS_DATA] = connect_to(ipaddr, req2->connect_port);
1652                 if(serverfd[SWS_DATA] != CURL_SOCKET_BAD) {
1653                   /* secondary tunnel established, now we have two
1654                      connections */
1655                   poll_client_rd[SWS_DATA] = TRUE;
1656                   poll_client_wr[SWS_DATA] = TRUE;
1657                   poll_server_rd[SWS_DATA] = TRUE;
1658                   poll_server_wr[SWS_DATA] = TRUE;
1659                   max_tunnel_idx = SWS_DATA;
1660                   secondary = TRUE;
1661                   toc[SWS_DATA] = 0;
1662                   tos[SWS_DATA] = 0;
1663                   clientfd[SWS_DATA] = datafd;
1664                   datafd = CURL_SOCKET_BAD;
1665                 }
1666               }
1667             }
1668           }
1669           if(datafd != CURL_SOCKET_BAD) {
1670             /* secondary tunnel not established */
1671             shutdown(datafd, SHUT_RDWR);
1672             sclose(datafd);
1673           }
1674         }
1675         if(got_exit_signal)
1676           break;
1677       }
1678 
1679       /* ---------------------------------------------------------- */
1680 
1681       /* react to tunnel endpoint readable/writable notifications */
1682       for(i = 0; i <= max_tunnel_idx; i++) {
1683         size_t len;
1684         if(clientfd[i] != CURL_SOCKET_BAD) {
1685           len = sizeof(readclient[i]) - tos[i];
1686           if(len && FD_ISSET(clientfd[i], &input)) {
1687             /* read from client */
1688             rc = sread(clientfd[i], &readclient[i][tos[i]], len);
1689             if(rc <= 0) {
1690               logmsg("[%s] got %zd, STOP READING client", data_or_ctrl(i), rc);
1691               shutdown(clientfd[i], SHUT_RD);
1692               poll_client_rd[i] = FALSE;
1693             }
1694             else {
1695               logmsg("[%s] READ %zd bytes from client", data_or_ctrl(i), rc);
1696               logmsg("[%s] READ \"%s\"", data_or_ctrl(i),
1697                      data_to_hex(&readclient[i][tos[i]], rc));
1698               tos[i] += rc;
1699             }
1700           }
1701         }
1702         if(serverfd[i] != CURL_SOCKET_BAD) {
1703           len = sizeof(readserver[i])-toc[i];
1704           if(len && FD_ISSET(serverfd[i], &input)) {
1705             /* read from server */
1706             rc = sread(serverfd[i], &readserver[i][toc[i]], len);
1707             if(rc <= 0) {
1708               logmsg("[%s] got %zd, STOP READING server", data_or_ctrl(i), rc);
1709               shutdown(serverfd[i], SHUT_RD);
1710               poll_server_rd[i] = FALSE;
1711             }
1712             else {
1713               logmsg("[%s] READ %zd bytes from server", data_or_ctrl(i), rc);
1714               logmsg("[%s] READ \"%s\"", data_or_ctrl(i),
1715                      data_to_hex(&readserver[i][toc[i]], rc));
1716               toc[i] += rc;
1717             }
1718           }
1719         }
1720         if(clientfd[i] != CURL_SOCKET_BAD) {
1721           if(toc[i] && FD_ISSET(clientfd[i], &output)) {
1722             /* write to client */
1723             rc = swrite(clientfd[i], readserver[i], toc[i]);
1724             if(rc <= 0) {
1725               logmsg("[%s] got %zd, STOP WRITING client", data_or_ctrl(i), rc);
1726               shutdown(clientfd[i], SHUT_WR);
1727               poll_client_wr[i] = FALSE;
1728               tcp_fin_wr = TRUE;
1729             }
1730             else {
1731               logmsg("[%s] SENT %zd bytes to client", data_or_ctrl(i), rc);
1732               logmsg("[%s] SENT \"%s\"", data_or_ctrl(i),
1733                      data_to_hex(readserver[i], rc));
1734               if(toc[i] - rc)
1735                 memmove(&readserver[i][0], &readserver[i][rc], toc[i]-rc);
1736               toc[i] -= rc;
1737             }
1738           }
1739         }
1740         if(serverfd[i] != CURL_SOCKET_BAD) {
1741           if(tos[i] && FD_ISSET(serverfd[i], &output)) {
1742             /* write to server */
1743             rc = swrite(serverfd[i], readclient[i], tos[i]);
1744             if(rc <= 0) {
1745               logmsg("[%s] got %zd, STOP WRITING server", data_or_ctrl(i), rc);
1746               shutdown(serverfd[i], SHUT_WR);
1747               poll_server_wr[i] = FALSE;
1748               tcp_fin_wr = TRUE;
1749             }
1750             else {
1751               logmsg("[%s] SENT %zd bytes to server", data_or_ctrl(i), rc);
1752               logmsg("[%s] SENT \"%s\"", data_or_ctrl(i),
1753                      data_to_hex(readclient[i], rc));
1754               if(tos[i] - rc)
1755                 memmove(&readclient[i][0], &readclient[i][rc], tos[i]-rc);
1756               tos[i] -= rc;
1757             }
1758           }
1759         }
1760       }
1761       if(got_exit_signal)
1762         break;
1763 
1764       /* ---------------------------------------------------------- */
1765 
1766       /* endpoint read/write disabling, endpoint closing and tunnel teardown */
1767       for(i = 0; i <= max_tunnel_idx; i++) {
1768         for(loop = 2; loop > 0; loop--) {
1769           /* loop twice to satisfy condition interdependencies without
1770              having to await select timeout or another socket event */
1771           if(clientfd[i] != CURL_SOCKET_BAD) {
1772             if(poll_client_rd[i] && !poll_server_wr[i]) {
1773               logmsg("[%s] DISABLED READING client", data_or_ctrl(i));
1774               shutdown(clientfd[i], SHUT_RD);
1775               poll_client_rd[i] = FALSE;
1776             }
1777             if(poll_client_wr[i] && !poll_server_rd[i] && !toc[i]) {
1778               logmsg("[%s] DISABLED WRITING client", data_or_ctrl(i));
1779               shutdown(clientfd[i], SHUT_WR);
1780               poll_client_wr[i] = FALSE;
1781               tcp_fin_wr = TRUE;
1782             }
1783           }
1784           if(serverfd[i] != CURL_SOCKET_BAD) {
1785             if(poll_server_rd[i] && !poll_client_wr[i]) {
1786               logmsg("[%s] DISABLED READING server", data_or_ctrl(i));
1787               shutdown(serverfd[i], SHUT_RD);
1788               poll_server_rd[i] = FALSE;
1789             }
1790             if(poll_server_wr[i] && !poll_client_rd[i] && !tos[i]) {
1791               logmsg("[%s] DISABLED WRITING server", data_or_ctrl(i));
1792               shutdown(serverfd[i], SHUT_WR);
1793               poll_server_wr[i] = FALSE;
1794               tcp_fin_wr = TRUE;
1795             }
1796           }
1797         }
1798       }
1799 
1800       if(tcp_fin_wr)
1801         /* allow kernel to place FIN bit packet on the wire */
1802         wait_ms(250);
1803 
1804       /* socket clearing */
1805       for(i = 0; i <= max_tunnel_idx; i++) {
1806         for(loop = 2; loop > 0; loop--) {
1807           if(clientfd[i] != CURL_SOCKET_BAD) {
1808             if(!poll_client_wr[i] && !poll_client_rd[i]) {
1809               logmsg("[%s] CLOSING client socket", data_or_ctrl(i));
1810               sclose(clientfd[i]);
1811               clientfd[i] = CURL_SOCKET_BAD;
1812               if(serverfd[i] == CURL_SOCKET_BAD) {
1813                 logmsg("[%s] ENDING", data_or_ctrl(i));
1814                 if(i == SWS_DATA)
1815                   secondary = FALSE;
1816                 else
1817                   primary = FALSE;
1818               }
1819             }
1820           }
1821           if(serverfd[i] != CURL_SOCKET_BAD) {
1822             if(!poll_server_wr[i] && !poll_server_rd[i]) {
1823               logmsg("[%s] CLOSING server socket", data_or_ctrl(i));
1824               sclose(serverfd[i]);
1825               serverfd[i] = CURL_SOCKET_BAD;
1826               if(clientfd[i] == CURL_SOCKET_BAD) {
1827                 logmsg("[%s] ENDING", data_or_ctrl(i));
1828                 if(i == SWS_DATA)
1829                   secondary = FALSE;
1830                 else
1831                   primary = FALSE;
1832               }
1833             }
1834           }
1835         }
1836       }
1837 
1838       /* ---------------------------------------------------------- */
1839 
1840       max_tunnel_idx = secondary ? SWS_DATA : SWS_CTRL;
1841 
1842       if(!primary)
1843         /* exit loop upon primary tunnel teardown */
1844         break;
1845 
1846     } /* (rc > 0) */
1847     else {
1848       timeout_count++;
1849       if(timeout_count > keepalive_secs) {
1850         logmsg("CONNECT proxy timeout after %d idle seconds!", timeout_count);
1851         break;
1852       }
1853     }
1854   }
1855 
1856 http_connect_cleanup:
1857 
1858   for(i = SWS_DATA; i >= SWS_CTRL; i--) {
1859     if(serverfd[i] != CURL_SOCKET_BAD) {
1860       logmsg("[%s] CLOSING server socket (cleanup)", data_or_ctrl(i));
1861       shutdown(serverfd[i], SHUT_RDWR);
1862       sclose(serverfd[i]);
1863     }
1864     if(clientfd[i] != CURL_SOCKET_BAD) {
1865       logmsg("[%s] CLOSING client socket (cleanup)", data_or_ctrl(i));
1866       shutdown(clientfd[i], SHUT_RDWR);
1867       sclose(clientfd[i]);
1868     }
1869     if((serverfd[i] != CURL_SOCKET_BAD) ||
1870        (clientfd[i] != CURL_SOCKET_BAD)) {
1871       logmsg("[%s] ABORTING", data_or_ctrl(i));
1872     }
1873   }
1874 
1875   *infdp = CURL_SOCKET_BAD;
1876 }
1877 
http_upgrade(struct httprequest * req)1878 static void http_upgrade(struct httprequest *req)
1879 {
1880   (void)req;
1881   logmsg("Upgraded to ... %u", req->upgrade_request);
1882   /* left to implement */
1883 }
1884 
1885 
1886 /* returns a socket handle, or 0 if there are no more waiting sockets,
1887    or < 0 if there was an error */
accept_connection(curl_socket_t sock)1888 static curl_socket_t accept_connection(curl_socket_t sock)
1889 {
1890   curl_socket_t msgsock = CURL_SOCKET_BAD;
1891   int error;
1892   int flag = 1;
1893 
1894   if(MAX_SOCKETS == num_sockets) {
1895     logmsg("Too many open sockets!");
1896     return CURL_SOCKET_BAD;
1897   }
1898 
1899   msgsock = accept(sock, NULL, NULL);
1900 
1901   if(got_exit_signal) {
1902     if(CURL_SOCKET_BAD != msgsock)
1903       sclose(msgsock);
1904     return CURL_SOCKET_BAD;
1905   }
1906 
1907   if(CURL_SOCKET_BAD == msgsock) {
1908     error = SOCKERRNO;
1909     if(EAGAIN == error || EWOULDBLOCK == error) {
1910       /* nothing to accept */
1911       return 0;
1912     }
1913     logmsg("MAJOR ERROR: accept() failed with error: (%d) %s",
1914            error, sstrerror(error));
1915     return CURL_SOCKET_BAD;
1916   }
1917 
1918   if(0 != curlx_nonblock(msgsock, TRUE)) {
1919     error = SOCKERRNO;
1920     logmsg("curlx_nonblock failed with error: (%d) %s",
1921            error, sstrerror(error));
1922     sclose(msgsock);
1923     return CURL_SOCKET_BAD;
1924   }
1925 
1926   if(0 != setsockopt(msgsock, SOL_SOCKET, SO_KEEPALIVE,
1927                      (void *)&flag, sizeof(flag))) {
1928     error = SOCKERRNO;
1929     logmsg("setsockopt(SO_KEEPALIVE) failed with error: (%d) %s",
1930            error, sstrerror(error));
1931     sclose(msgsock);
1932     return CURL_SOCKET_BAD;
1933   }
1934 
1935   /*
1936   ** As soon as this server accepts a connection from the test harness it
1937   ** must set the server logs advisor read lock to indicate that server
1938   ** logs should not be read until this lock is removed by this server.
1939   */
1940 
1941   if(!serverlogslocked)
1942     set_advisor_read_lock(loglockfile);
1943   serverlogslocked += 1;
1944 
1945   logmsg("====> Client connect");
1946 
1947   all_sockets[num_sockets] = msgsock;
1948   num_sockets += 1;
1949 
1950 #ifdef TCP_NODELAY
1951   if(socket_domain_is_ip()) {
1952     /*
1953      * Disable the Nagle algorithm to make it easier to send out a large
1954      * response in many small segments to torture the clients more.
1955      */
1956     if(0 != setsockopt(msgsock, IPPROTO_TCP, TCP_NODELAY,
1957                        (void *)&flag, sizeof(flag)))
1958       logmsg("====> TCP_NODELAY failed");
1959   }
1960 #endif
1961 
1962   return msgsock;
1963 }
1964 
1965 /* returns 1 if the connection should be serviced again immediately, 0 if there
1966    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)1967 static int service_connection(curl_socket_t msgsock, struct httprequest *req,
1968                               curl_socket_t listensock,
1969                               const char *connecthost,
1970                               int keepalive_secs)
1971 {
1972   if(got_exit_signal)
1973     return -1;
1974 
1975   while(!req->done_processing) {
1976     int rc = get_request(msgsock, req);
1977     if(rc <= 0) {
1978       /* Nothing further to read now, possibly because the socket was closed */
1979       return rc;
1980     }
1981   }
1982 
1983   if(prevbounce) {
1984     /* bounce treatment requested */
1985     if(req->testno == prevtestno) {
1986       req->partno = prevpartno + 1;
1987       logmsg("BOUNCE part number to %ld", req->partno);
1988     }
1989     else {
1990       prevbounce = FALSE;
1991       prevtestno = -1;
1992       prevpartno = -1;
1993     }
1994   }
1995 
1996   send_doc(msgsock, req);
1997   if(got_exit_signal)
1998     return -1;
1999 
2000   if(req->testno < 0) {
2001     logmsg("special request received, no persistency");
2002     return -1;
2003   }
2004   if(!req->open) {
2005     logmsg("instructed to close connection after server-reply");
2006     return -1;
2007   }
2008 
2009   if(req->connect_request) {
2010     /* a CONNECT request, setup and talk the tunnel */
2011     if(!is_proxy) {
2012       logmsg("received CONNECT but isn't running as proxy!");
2013       return 1;
2014     }
2015     else {
2016       http_connect(&msgsock, listensock, connecthost, req->connect_port,
2017                    keepalive_secs);
2018       return -1;
2019     }
2020   }
2021 
2022   if(req->upgrade_request) {
2023     /* an upgrade request, switch to another protocol here */
2024     http_upgrade(req);
2025     return 1;
2026   }
2027 
2028   /* if we got a CONNECT, loop and get another request as well! */
2029 
2030   if(req->open) {
2031     logmsg("=> persistent connection request ended, awaits new request\n");
2032     return 1;
2033   }
2034   else {
2035     logmsg("=> NOT a persistent connection, close close CLOSE\n");
2036   }
2037 
2038   return -1;
2039 }
2040 
main(int argc,char * argv[])2041 int main(int argc, char *argv[])
2042 {
2043   srvr_sockaddr_union_t me;
2044   curl_socket_t sock = CURL_SOCKET_BAD;
2045   int wrotepidfile = 0;
2046   int wroteportfile = 0;
2047   int flag;
2048   unsigned short port = DEFAULT_PORT;
2049 #ifdef USE_UNIX_SOCKETS
2050   const char *unix_socket = NULL;
2051   bool unlink_socket = false;
2052 #endif
2053   const char *pidname = ".http.pid";
2054   const char *portname = ".http.port";
2055   struct httprequest *req = NULL;
2056   int rc = 0;
2057   int error;
2058   int arg = 1;
2059   const char *connecthost = "127.0.0.1";
2060   const char *socket_type = "IPv4";
2061   char port_str[11];
2062   const char *location_str = port_str;
2063   int keepalive_secs = 5;
2064   const char *protocol_type = "HTTP";
2065 
2066   /* a default CONNECT port is basically pointless but still ... */
2067   size_t socket_idx;
2068 
2069   while(argc > arg) {
2070     if(!strcmp("--version", argv[arg])) {
2071       puts("sws IPv4"
2072 #ifdef USE_IPV6
2073              "/IPv6"
2074 #endif
2075 #ifdef USE_UNIX_SOCKETS
2076              "/unix"
2077 #endif
2078           );
2079       return 0;
2080     }
2081     else if(!strcmp("--pidfile", argv[arg])) {
2082       arg++;
2083       if(argc > arg)
2084         pidname = argv[arg++];
2085     }
2086     else if(!strcmp("--portfile", argv[arg])) {
2087       arg++;
2088       if(argc > arg)
2089         portname = argv[arg++];
2090     }
2091     else if(!strcmp("--logfile", argv[arg])) {
2092       arg++;
2093       if(argc > arg)
2094         serverlogfile = argv[arg++];
2095     }
2096     else if(!strcmp("--logdir", argv[arg])) {
2097       arg++;
2098       if(argc > arg)
2099         logdir = argv[arg++];
2100     }
2101     else if(!strcmp("--cmdfile", argv[arg])) {
2102       arg++;
2103       if(argc > arg)
2104         cmdfile = argv[arg++];
2105     }
2106     else if(!strcmp("--gopher", argv[arg])) {
2107       arg++;
2108       use_gopher = TRUE;
2109       protocol_type = "GOPHER";
2110       end_of_headers = "\r\n"; /* gopher style is much simpler */
2111     }
2112     else if(!strcmp("--ipv4", argv[arg])) {
2113       socket_type = "IPv4";
2114       socket_domain = AF_INET;
2115       location_str = port_str;
2116       arg++;
2117     }
2118     else if(!strcmp("--ipv6", argv[arg])) {
2119 #ifdef USE_IPV6
2120       socket_type = "IPv6";
2121       socket_domain = AF_INET6;
2122       location_str = port_str;
2123 #endif
2124       arg++;
2125     }
2126     else if(!strcmp("--unix-socket", argv[arg])) {
2127       arg++;
2128       if(argc > arg) {
2129 #ifdef USE_UNIX_SOCKETS
2130         unix_socket = argv[arg];
2131         if(strlen(unix_socket) >= sizeof(me.sau.sun_path)) {
2132           fprintf(stderr,
2133                   "sws: socket path must be shorter than %zu chars: %s\n",
2134                   sizeof(me.sau.sun_path), unix_socket);
2135           return 0;
2136         }
2137         socket_type = "unix";
2138         socket_domain = AF_UNIX;
2139         location_str = unix_socket;
2140 #endif
2141         arg++;
2142       }
2143     }
2144     else if(!strcmp("--port", argv[arg])) {
2145       arg++;
2146       if(argc > arg) {
2147         char *endptr;
2148         unsigned long ulnum = strtoul(argv[arg], &endptr, 10);
2149         if((endptr != argv[arg] + strlen(argv[arg])) ||
2150            (ulnum && ((ulnum < 1025UL) || (ulnum > 65535UL)))) {
2151           fprintf(stderr, "sws: invalid --port argument (%s)\n",
2152                   argv[arg]);
2153           return 0;
2154         }
2155         port = curlx_ultous(ulnum);
2156         arg++;
2157       }
2158     }
2159     else if(!strcmp("--srcdir", argv[arg])) {
2160       arg++;
2161       if(argc > arg) {
2162         path = argv[arg];
2163         arg++;
2164       }
2165     }
2166     else if(!strcmp("--keepalive", argv[arg])) {
2167       arg++;
2168       if(argc > arg) {
2169         char *endptr;
2170         unsigned long ulnum = strtoul(argv[arg], &endptr, 10);
2171         if((endptr != argv[arg] + strlen(argv[arg])) ||
2172            (ulnum && (ulnum > 65535UL))) {
2173           fprintf(stderr, "sws: invalid --keepalive argument (%s), must "
2174                   "be number of seconds\n", argv[arg]);
2175           return 0;
2176         }
2177         keepalive_secs = curlx_ultous(ulnum);
2178         arg++;
2179       }
2180     }
2181     else if(!strcmp("--connect", argv[arg])) {
2182       /* The connect host IP number that the proxy will connect to no matter
2183          what the client asks for, but also use this as a hint that we run as
2184          a proxy and do a few different internal choices */
2185       arg++;
2186       if(argc > arg) {
2187         connecthost = argv[arg];
2188         arg++;
2189         is_proxy = TRUE;
2190         logmsg("Run as proxy, CONNECT to host %s", connecthost);
2191       }
2192     }
2193     else {
2194       puts("Usage: sws [option]\n"
2195            " --version\n"
2196            " --logfile [file]\n"
2197            " --logdir [directory]\n"
2198            " --pidfile [file]\n"
2199            " --portfile [file]\n"
2200            " --ipv4\n"
2201            " --ipv6\n"
2202            " --unix-socket [file]\n"
2203            " --port [port]\n"
2204            " --srcdir [path]\n"
2205            " --connect [ip4-addr]\n"
2206            " --gopher");
2207       return 0;
2208     }
2209   }
2210 
2211   msnprintf(loglockfile, sizeof(loglockfile), "%s/%s/sws-%s%s-%s.lock",
2212             logdir, SERVERLOGS_LOCKDIR, protocol_type,
2213             is_proxy ? "-proxy" : "", socket_type);
2214 
2215 #ifdef _WIN32
2216   win32_init();
2217   atexit(win32_cleanup);
2218 #endif
2219 
2220   install_signal_handlers(false);
2221 
2222   req = calloc(1, sizeof(*req));
2223   if(!req)
2224     goto sws_cleanup;
2225 
2226   sock = socket(socket_domain, SOCK_STREAM, 0);
2227 
2228   all_sockets[0] = sock;
2229   num_sockets = 1;
2230 
2231   if(CURL_SOCKET_BAD == sock) {
2232     error = SOCKERRNO;
2233     logmsg("Error creating socket: (%d) %s", error, sstrerror(error));
2234     goto sws_cleanup;
2235   }
2236 
2237   flag = 1;
2238   if(0 != setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
2239                      (void *)&flag, sizeof(flag))) {
2240     error = SOCKERRNO;
2241     logmsg("setsockopt(SO_REUSEADDR) failed with error: (%d) %s",
2242            error, sstrerror(error));
2243     goto sws_cleanup;
2244   }
2245   if(0 != curlx_nonblock(sock, TRUE)) {
2246     error = SOCKERRNO;
2247     logmsg("curlx_nonblock failed with error: (%d) %s",
2248            error, sstrerror(error));
2249     goto sws_cleanup;
2250   }
2251 
2252   switch(socket_domain) {
2253   case AF_INET:
2254     memset(&me.sa4, 0, sizeof(me.sa4));
2255     me.sa4.sin_family = AF_INET;
2256     me.sa4.sin_addr.s_addr = INADDR_ANY;
2257     me.sa4.sin_port = htons(port);
2258     rc = bind(sock, &me.sa, sizeof(me.sa4));
2259     break;
2260 #ifdef USE_IPV6
2261   case AF_INET6:
2262     memset(&me.sa6, 0, sizeof(me.sa6));
2263     me.sa6.sin6_family = AF_INET6;
2264     me.sa6.sin6_addr = in6addr_any;
2265     me.sa6.sin6_port = htons(port);
2266     rc = bind(sock, &me.sa, sizeof(me.sa6));
2267     break;
2268 #endif /* USE_IPV6 */
2269 #ifdef USE_UNIX_SOCKETS
2270   case AF_UNIX:
2271     rc = bind_unix_socket(sock, unix_socket, &me.sau);
2272 #endif /* USE_UNIX_SOCKETS */
2273   }
2274   if(0 != rc) {
2275     error = SOCKERRNO;
2276 #ifdef USE_UNIX_SOCKETS
2277     if(socket_domain == AF_UNIX)
2278       logmsg("Error binding socket on path %s: (%d) %s",
2279              unix_socket, error, sstrerror(error));
2280     else
2281 #endif
2282       logmsg("Error binding socket on port %hu: (%d) %s",
2283              port, error, sstrerror(error));
2284     goto sws_cleanup;
2285   }
2286 
2287   if(!port) {
2288     /* The system was supposed to choose a port number, figure out which
2289        port we actually got and update the listener port value with it. */
2290     curl_socklen_t la_size;
2291     srvr_sockaddr_union_t localaddr;
2292 #ifdef USE_IPV6
2293     if(socket_domain != AF_INET6)
2294 #endif
2295       la_size = sizeof(localaddr.sa4);
2296 #ifdef USE_IPV6
2297     else
2298       la_size = sizeof(localaddr.sa6);
2299 #endif
2300     memset(&localaddr.sa, 0, (size_t)la_size);
2301     if(getsockname(sock, &localaddr.sa, &la_size) < 0) {
2302       error = SOCKERRNO;
2303       logmsg("getsockname() failed with error: (%d) %s",
2304              error, sstrerror(error));
2305       sclose(sock);
2306       goto sws_cleanup;
2307     }
2308     switch(localaddr.sa.sa_family) {
2309     case AF_INET:
2310       port = ntohs(localaddr.sa4.sin_port);
2311       break;
2312 #ifdef USE_IPV6
2313     case AF_INET6:
2314       port = ntohs(localaddr.sa6.sin6_port);
2315       break;
2316 #endif
2317     default:
2318       break;
2319     }
2320     if(!port) {
2321       /* Real failure, listener port shall not be zero beyond this point. */
2322       logmsg("Apparently getsockname() succeeded, with listener port zero.");
2323       logmsg("A valid reason for this failure is a binary built without");
2324       logmsg("proper network library linkage. This might not be the only");
2325       logmsg("reason, but double check it before anything else.");
2326       sclose(sock);
2327       goto sws_cleanup;
2328     }
2329   }
2330 #ifdef USE_UNIX_SOCKETS
2331   if(socket_domain != AF_UNIX)
2332 #endif
2333     msnprintf(port_str, sizeof(port_str), "port %hu", port);
2334 
2335   logmsg("Running %s %s version on %s",
2336          protocol_type, socket_type, location_str);
2337 
2338   /* start accepting connections */
2339   rc = listen(sock, 50);
2340   if(0 != rc) {
2341     error = SOCKERRNO;
2342     logmsg("listen() failed with error: (%d) %s", error, sstrerror(error));
2343     goto sws_cleanup;
2344   }
2345 
2346 #ifdef USE_UNIX_SOCKETS
2347   /* listen succeeds, so let's assume a valid listening Unix socket */
2348   unlink_socket = true;
2349 #endif
2350 
2351   /*
2352   ** As soon as this server writes its pid file the test harness will
2353   ** attempt to connect to this server and initiate its verification.
2354   */
2355 
2356   wrotepidfile = write_pidfile(pidname);
2357   if(!wrotepidfile)
2358     goto sws_cleanup;
2359 
2360   wroteportfile = write_portfile(portname, port);
2361   if(!wroteportfile)
2362     goto sws_cleanup;
2363 
2364   /* initialization of httprequest struct is done before get_request(), but
2365      the pipelining struct field must be initialized previously to FALSE
2366      every time a new connection arrives. */
2367 
2368   init_httprequest(req);
2369 
2370   for(;;) {
2371     fd_set input;
2372     fd_set output;
2373     curl_socket_t maxfd = (curl_socket_t)-1;
2374     int active;
2375     struct timeval timeout = {0};
2376     timeout.tv_usec = 250000L; /* 250 ms */
2377 
2378     /* Clear out closed sockets */
2379     for(socket_idx = num_sockets - 1; socket_idx >= 1; --socket_idx) {
2380       if(CURL_SOCKET_BAD == all_sockets[socket_idx]) {
2381         char *dst = (char *) (all_sockets + socket_idx);
2382         char *src = (char *) (all_sockets + socket_idx + 1);
2383         char *end = (char *) (all_sockets + num_sockets);
2384         memmove(dst, src, end - src);
2385         num_sockets -= 1;
2386       }
2387     }
2388 
2389     if(got_exit_signal)
2390       goto sws_cleanup;
2391 
2392     /* Set up for select */
2393     FD_ZERO(&input);
2394     FD_ZERO(&output);
2395 
2396     for(socket_idx = 0; socket_idx < num_sockets; ++socket_idx) {
2397       /* Listen on all sockets */
2398 #if defined(__DJGPP__)
2399 #pragma GCC diagnostic push
2400 #pragma GCC diagnostic ignored "-Warith-conversion"
2401 #endif
2402       FD_SET(all_sockets[socket_idx], &input);
2403 #if defined(__DJGPP__)
2404 #pragma GCC diagnostic pop
2405 #endif
2406       if(all_sockets[socket_idx] > maxfd)
2407         maxfd = all_sockets[socket_idx];
2408     }
2409 
2410     if(got_exit_signal)
2411       goto sws_cleanup;
2412 
2413     do {
2414       rc = select((int)maxfd + 1, &input, &output, NULL, &timeout);
2415     } while(rc < 0 && errno == EINTR && !got_exit_signal);
2416 
2417     if(got_exit_signal)
2418       goto sws_cleanup;
2419 
2420     if(rc < 0) {
2421       error = SOCKERRNO;
2422       logmsg("select() failed with error: (%d) %s", error, sstrerror(error));
2423       goto sws_cleanup;
2424     }
2425 
2426     if(rc == 0) {
2427       /* Timed out - try again */
2428       continue;
2429     }
2430     active = rc; /* a positive number */
2431 
2432     /* Check if the listening socket is ready to accept */
2433     if(FD_ISSET(all_sockets[0], &input)) {
2434       /* Service all queued connections */
2435       curl_socket_t msgsock;
2436       do {
2437         msgsock = accept_connection(sock);
2438         logmsg("accept_connection %" FMT_SOCKET_T
2439                " returned %" FMT_SOCKET_T, sock, msgsock);
2440         if(CURL_SOCKET_BAD == msgsock)
2441           goto sws_cleanup;
2442         if(req->delay)
2443           wait_ms(req->delay);
2444       } while(msgsock > 0);
2445       active--;
2446     }
2447 
2448     /* Service all connections that are ready */
2449     for(socket_idx = 1; (socket_idx < num_sockets) && active; ++socket_idx) {
2450       if(FD_ISSET(all_sockets[socket_idx], &input)) {
2451         active--;
2452         if(got_exit_signal)
2453           goto sws_cleanup;
2454 
2455         /* Service this connection until it has nothing available */
2456         do {
2457           rc = service_connection(all_sockets[socket_idx], req, sock,
2458                                   connecthost, keepalive_secs);
2459           if(got_exit_signal)
2460             goto sws_cleanup;
2461 
2462           if(rc < 0) {
2463             logmsg("====> Client disconnect %d", req->connmon);
2464 
2465             if(req->connmon) {
2466               const char *keepopen = "[DISCONNECT]\n";
2467               storerequest(keepopen, strlen(keepopen));
2468             }
2469 
2470             if(!req->open)
2471               /* When instructed to close connection after server-reply we
2472                  wait a very small amount of time before doing so. If this
2473                  is not done client might get an ECONNRESET before reading
2474                  a single byte of server-reply. */
2475               wait_ms(50);
2476 
2477             if(all_sockets[socket_idx] != CURL_SOCKET_BAD) {
2478               sclose(all_sockets[socket_idx]);
2479               all_sockets[socket_idx] = CURL_SOCKET_BAD;
2480             }
2481 
2482             serverlogslocked -= 1;
2483             if(!serverlogslocked)
2484               clear_advisor_read_lock(loglockfile);
2485 
2486             if(req->testno == DOCNUMBER_QUIT)
2487               goto sws_cleanup;
2488           }
2489 
2490           /* Reset the request, unless we're still in the middle of reading */
2491           if(rc && !req->upgrade_request)
2492             /* Note: resetting the HTTP request here can cause problems if:
2493              * 1) req->skipall is TRUE,
2494              * 2) the socket is still open, and
2495              * 3) (stale) data is still available (or about to be available)
2496              *    on that socket
2497              * In that case, this loop will run once more and treat that stale
2498              * data (in service_connection()) as the first data received on
2499              * this new HTTP request and report "** Unusual request" (skipall
2500              * would have otherwise caused that data to be ignored). Normally,
2501              * that socket will be closed by the client and there won't be any
2502              * stale data to cause this, but stranger things have happened (see
2503              * issue #11678).
2504              */
2505             init_httprequest(req);
2506         } while(rc > 0);
2507       }
2508     }
2509 
2510     if(got_exit_signal)
2511       goto sws_cleanup;
2512   }
2513 
2514 sws_cleanup:
2515 
2516   for(socket_idx = 1; socket_idx < num_sockets; ++socket_idx)
2517     if((all_sockets[socket_idx] != sock) &&
2518      (all_sockets[socket_idx] != CURL_SOCKET_BAD))
2519       sclose(all_sockets[socket_idx]);
2520 
2521   if(sock != CURL_SOCKET_BAD)
2522     sclose(sock);
2523 
2524 #ifdef USE_UNIX_SOCKETS
2525   if(unlink_socket && socket_domain == AF_UNIX && unix_socket) {
2526     rc = unlink(unix_socket);
2527     logmsg("unlink(%s) = %d (%s)", unix_socket, rc, strerror(rc));
2528   }
2529 #endif
2530 
2531   free(req);
2532 
2533   if(got_exit_signal)
2534     logmsg("signalled to die");
2535 
2536   if(wrotepidfile)
2537     unlink(pidname);
2538   if(wroteportfile)
2539     unlink(portname);
2540 
2541   if(serverlogslocked) {
2542     serverlogslocked = 0;
2543     clear_advisor_read_lock(loglockfile);
2544   }
2545 
2546   restore_signal_handlers(false);
2547 
2548   if(got_exit_signal) {
2549     logmsg("========> %s sws (%s pid: %ld) exits with signal (%d)",
2550            socket_type, location_str, (long)Curl_getpid(), exit_signal);
2551     /*
2552      * To properly set the return status of the process we
2553      * must raise the same signal SIGINT or SIGTERM that we
2554      * caught and let the old handler take care of it.
2555      */
2556     raise(exit_signal);
2557   }
2558 
2559   logmsg("========> sws quits");
2560   return 0;
2561 }
2562