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