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