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