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