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