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