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