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