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