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