1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2011, 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 http://curl.haxx.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 ***************************************************************************/ 22 23/* sws.c: simple (silly?) web server 24 25 This code was originally graciously donated to the project by Juergen 26 Wilke. Thanks a bunch! 27 28 */ 29 30#define CURL_NO_OLDIES 31 32#include "setup.h" /* portability help from the lib directory */ 33 34#ifdef HAVE_SIGNAL_H 35#include <signal.h> 36#endif 37#ifdef HAVE_UNISTD_H 38#include <unistd.h> 39#endif 40#ifdef HAVE_SYS_SOCKET_H 41#include <sys/socket.h> 42#endif 43#ifdef HAVE_NETINET_IN_H 44#include <netinet/in.h> 45#endif 46#ifdef HAVE_ARPA_INET_H 47#include <arpa/inet.h> 48#endif 49#ifdef HAVE_NETDB_H 50#include <netdb.h> 51#endif 52#ifdef HAVE_NETINET_TCP_H 53#include <netinet/tcp.h> /* for TCP_NODELAY */ 54#endif 55 56#define ENABLE_CURLX_PRINTF 57/* make the curlx header define all printf() functions to use the curlx_* 58 versions instead */ 59#include "curlx.h" /* from the private lib dir */ 60#include "getpart.h" 61#include "util.h" 62#include "server_sockaddr.h" 63 64/* include memdebug.h last */ 65#include "memdebug.h" 66 67#if !defined(CURL_SWS_FORK_ENABLED) && defined(HAVE_FORK) 68/* 69 * The normal sws build for the plain standard curl test suite has no use for 70 * fork(), but if you feel wild and crazy and want to setup some more exotic 71 * tests. Define this and run... 72 */ 73#define CURL_SWS_FORK_ENABLED 74#endif 75 76#ifdef ENABLE_IPV6 77static bool use_ipv6 = FALSE; 78#endif 79static bool use_gopher = FALSE; 80static const char *ipv_inuse = "IPv4"; 81static int serverlogslocked = 0; 82 83#define REQBUFSIZ 150000 84#define REQBUFSIZ_TXT "149999" 85 86static long prevtestno=-1; /* previous test number we served */ 87static long prevpartno=-1; /* previous part number we served */ 88static bool prevbounce=FALSE; /* instructs the server to increase the part 89 number for a test in case the identical 90 testno+partno request shows up again */ 91 92#define RCMD_NORMALREQ 0 /* default request, use the tests file normally */ 93#define RCMD_IDLE 1 /* told to sit idle */ 94#define RCMD_STREAM 2 /* told to stream */ 95 96struct httprequest { 97 char reqbuf[REQBUFSIZ]; /* buffer area for the incoming request */ 98 size_t checkindex; /* where to start checking of the request */ 99 size_t offset; /* size of the incoming request */ 100 long testno; /* test number found in the request */ 101 long partno; /* part number found in the request */ 102 bool open; /* keep connection open info, as found in the request */ 103 bool auth_req; /* authentication required, don't wait for body unless 104 there's an Authorization header */ 105 bool auth; /* Authorization header present in the incoming request */ 106 size_t cl; /* Content-Length of the incoming request */ 107 bool digest; /* Authorization digest header found */ 108 bool ntlm; /* Authorization ntlm header found */ 109 int writedelay; /* if non-zero, delay this number of seconds between 110 writes in the response */ 111 int pipe; /* if non-zero, expect this many requests to do a "piped" 112 request/response */ 113 int skip; /* if non-zero, the server is instructed to not read this 114 many bytes from a PUT/POST request. Ie the client sends N 115 bytes said in Content-Length, but the server only reads N 116 - skip bytes. */ 117 int rcmd; /* doing a special command, see defines above */ 118 int prot_version; /* HTTP version * 10 */ 119 bool pipelining; /* true if request is pipelined */ 120 int callcount; /* times ProcessRequest() gets called */ 121}; 122 123static int ProcessRequest(struct httprequest *req); 124static void storerequest(char *reqbuf, size_t totalsize); 125 126#define DEFAULT_PORT 8999 127 128#ifndef DEFAULT_LOGFILE 129#define DEFAULT_LOGFILE "log/sws.log" 130#endif 131 132const char *serverlogfile = DEFAULT_LOGFILE; 133 134#define SWSVERSION "cURL test suite HTTP server/0.1" 135 136#define REQUEST_DUMP "log/server.input" 137#define RESPONSE_DUMP "log/server.response" 138 139/* very-big-path support */ 140#define MAXDOCNAMELEN 140000 141#define MAXDOCNAMELEN_TXT "139999" 142 143#define REQUEST_KEYWORD_SIZE 256 144#define REQUEST_KEYWORD_SIZE_TXT "255" 145 146#define CMD_AUTH_REQUIRED "auth_required" 147 148/* 'idle' means that it will accept the request fine but never respond 149 any data. Just keep the connection alive. */ 150#define CMD_IDLE "idle" 151 152/* 'stream' means to send a never-ending stream of data */ 153#define CMD_STREAM "stream" 154 155#define END_OF_HEADERS "\r\n\r\n" 156 157enum { 158 DOCNUMBER_NOTHING = -7, 159 DOCNUMBER_QUIT = -6, 160 DOCNUMBER_BADCONNECT = -5, 161 DOCNUMBER_INTERNAL= -4, 162 DOCNUMBER_CONNECT = -3, 163 DOCNUMBER_WERULEZ = -2, 164 DOCNUMBER_404 = -1 165}; 166 167static const char *end_of_headers = END_OF_HEADERS; 168 169/* sent as reply to a QUIT */ 170static const char *docquit = 171"HTTP/1.1 200 Goodbye" END_OF_HEADERS; 172 173/* sent as reply to a CONNECT */ 174static const char *docconnect = 175"HTTP/1.1 200 Mighty fine indeed" END_OF_HEADERS; 176 177/* sent as reply to a "bad" CONNECT */ 178static const char *docbadconnect = 179"HTTP/1.1 501 Forbidden you fool" END_OF_HEADERS; 180 181/* send back this on 404 file not found */ 182static const char *doc404 = "HTTP/1.1 404 Not Found\r\n" 183 "Server: " SWSVERSION "\r\n" 184 "Connection: close\r\n" 185 "Content-Type: text/html" 186 END_OF_HEADERS 187 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n" 188 "<HTML><HEAD>\n" 189 "<TITLE>404 Not Found</TITLE>\n" 190 "</HEAD><BODY>\n" 191 "<H1>Not Found</H1>\n" 192 "The requested URL was not found on this server.\n" 193 "<P><HR><ADDRESS>" SWSVERSION "</ADDRESS>\n" "</BODY></HTML>\n"; 194 195/* do-nothing macro replacement for systems which lack siginterrupt() */ 196 197#ifndef HAVE_SIGINTERRUPT 198#define siginterrupt(x,y) do {} while(0) 199#endif 200 201/* vars used to keep around previous signal handlers */ 202 203typedef RETSIGTYPE (*SIGHANDLER_T)(int); 204 205#ifdef SIGHUP 206static SIGHANDLER_T old_sighup_handler = SIG_ERR; 207#endif 208 209#ifdef SIGPIPE 210static SIGHANDLER_T old_sigpipe_handler = SIG_ERR; 211#endif 212 213#ifdef SIGALRM 214static SIGHANDLER_T old_sigalrm_handler = SIG_ERR; 215#endif 216 217#ifdef SIGINT 218static SIGHANDLER_T old_sigint_handler = SIG_ERR; 219#endif 220 221#ifdef SIGTERM 222static SIGHANDLER_T old_sigterm_handler = SIG_ERR; 223#endif 224 225/* var which if set indicates that the program should finish execution */ 226 227SIG_ATOMIC_T got_exit_signal = 0; 228 229/* if next is set indicates the first signal handled in exit_signal_handler */ 230 231static volatile int exit_signal = 0; 232 233/* signal handler that will be triggered to indicate that the program 234 should finish its execution in a controlled manner as soon as possible. 235 The first time this is called it will set got_exit_signal to one and 236 store in exit_signal the signal that triggered its execution. */ 237 238static RETSIGTYPE exit_signal_handler(int signum) 239{ 240 int old_errno = ERRNO; 241 if(got_exit_signal == 0) { 242 got_exit_signal = 1; 243 exit_signal = signum; 244 } 245 (void)signal(signum, exit_signal_handler); 246 SET_ERRNO(old_errno); 247} 248 249static void install_signal_handlers(void) 250{ 251#ifdef SIGHUP 252 /* ignore SIGHUP signal */ 253 if((old_sighup_handler = signal(SIGHUP, SIG_IGN)) == SIG_ERR) 254 logmsg("cannot install SIGHUP handler: %s", strerror(ERRNO)); 255#endif 256#ifdef SIGPIPE 257 /* ignore SIGPIPE signal */ 258 if((old_sigpipe_handler = signal(SIGPIPE, SIG_IGN)) == SIG_ERR) 259 logmsg("cannot install SIGPIPE handler: %s", strerror(ERRNO)); 260#endif 261#ifdef SIGALRM 262 /* ignore SIGALRM signal */ 263 if((old_sigalrm_handler = signal(SIGALRM, SIG_IGN)) == SIG_ERR) 264 logmsg("cannot install SIGALRM handler: %s", strerror(ERRNO)); 265#endif 266#ifdef SIGINT 267 /* handle SIGINT signal with our exit_signal_handler */ 268 if((old_sigint_handler = signal(SIGINT, exit_signal_handler)) == SIG_ERR) 269 logmsg("cannot install SIGINT handler: %s", strerror(ERRNO)); 270 else 271 siginterrupt(SIGINT, 1); 272#endif 273#ifdef SIGTERM 274 /* handle SIGTERM signal with our exit_signal_handler */ 275 if((old_sigterm_handler = signal(SIGTERM, exit_signal_handler)) == SIG_ERR) 276 logmsg("cannot install SIGTERM handler: %s", strerror(ERRNO)); 277 else 278 siginterrupt(SIGTERM, 1); 279#endif 280} 281 282static void restore_signal_handlers(void) 283{ 284#ifdef SIGHUP 285 if(SIG_ERR != old_sighup_handler) 286 (void)signal(SIGHUP, old_sighup_handler); 287#endif 288#ifdef SIGPIPE 289 if(SIG_ERR != old_sigpipe_handler) 290 (void)signal(SIGPIPE, old_sigpipe_handler); 291#endif 292#ifdef SIGALRM 293 if(SIG_ERR != old_sigalrm_handler) 294 (void)signal(SIGALRM, old_sigalrm_handler); 295#endif 296#ifdef SIGINT 297 if(SIG_ERR != old_sigint_handler) 298 (void)signal(SIGINT, old_sigint_handler); 299#endif 300#ifdef SIGTERM 301 if(SIG_ERR != old_sigterm_handler) 302 (void)signal(SIGTERM, old_sigterm_handler); 303#endif 304} 305 306static int ProcessRequest(struct httprequest *req) 307{ 308 char *line=&req->reqbuf[req->checkindex]; 309 bool chunked = FALSE; 310 static char request[REQUEST_KEYWORD_SIZE]; 311 static char doc[MAXDOCNAMELEN]; 312 char logbuf[456]; 313 int prot_major, prot_minor; 314 char *end; 315 int error; 316 end = strstr(line, end_of_headers); 317 318 req->callcount++; 319 320 logmsg("Process %d bytes request%s", req->offset, 321 req->callcount > 1?" [CONTINUED]":""); 322 323 /* try to figure out the request characteristics as soon as possible, but 324 only once! */ 325 326 if(use_gopher && 327 (req->testno == DOCNUMBER_NOTHING) && 328 !strncmp("/verifiedserver", line, 15)) { 329 logmsg("Are-we-friendly question received"); 330 req->testno = DOCNUMBER_WERULEZ; 331 return 1; /* done */ 332 } 333 334 else if((req->testno == DOCNUMBER_NOTHING) && 335 sscanf(line, 336 "%" REQUEST_KEYWORD_SIZE_TXT"s %" MAXDOCNAMELEN_TXT "s HTTP/%d.%d", 337 request, 338 doc, 339 &prot_major, 340 &prot_minor) == 4) { 341 char *ptr; 342 343 req->prot_version = prot_major*10 + prot_minor; 344 345 /* find the last slash */ 346 ptr = strrchr(doc, '/'); 347 348 /* get the number after it */ 349 if(ptr) { 350 FILE *stream; 351 char *filename; 352 353 if((strlen(doc) + strlen(request)) < 400) 354 sprintf(logbuf, "Got request: %s %s HTTP/%d.%d", 355 request, doc, prot_major, prot_minor); 356 else 357 sprintf(logbuf, "Got a *HUGE* request HTTP/%d.%d", 358 prot_major, prot_minor); 359 logmsg("%s", logbuf); 360 361 if(!strncmp("/verifiedserver", ptr, 15)) { 362 logmsg("Are-we-friendly question received"); 363 req->testno = DOCNUMBER_WERULEZ; 364 return 1; /* done */ 365 } 366 367 if(!strncmp("/quit", ptr, 5)) { 368 logmsg("Request-to-quit received"); 369 req->testno = DOCNUMBER_QUIT; 370 return 1; /* done */ 371 } 372 373 ptr++; /* skip the slash */ 374 375 /* skip all non-numericals following the slash */ 376 while(*ptr && !ISDIGIT(*ptr)) 377 ptr++; 378 379 req->testno = strtol(ptr, &ptr, 10); 380 381 if(req->testno > 10000) { 382 req->partno = req->testno % 10000; 383 req->testno /= 10000; 384 } 385 else 386 req->partno = 0; 387 388 sprintf(logbuf, "Requested test number %ld part %ld", 389 req->testno, req->partno); 390 logmsg("%s", logbuf); 391 392 filename = test2file(req->testno); 393 394 stream=fopen(filename, "rb"); 395 if(!stream) { 396 error = ERRNO; 397 logmsg("fopen() failed with error: %d %s", error, strerror(error)); 398 logmsg("Error opening file: %s", filename); 399 logmsg("Couldn't open test file %ld", req->testno); 400 req->open = FALSE; /* closes connection */ 401 return 1; /* done */ 402 } 403 else { 404 char *orgcmd = NULL; 405 char *cmd = NULL; 406 size_t cmdsize = 0; 407 int num=0; 408 409 /* get the custom server control "commands" */ 410 error = getpart(&orgcmd, &cmdsize, "reply", "servercmd", stream); 411 fclose(stream); 412 if(error) { 413 logmsg("getpart() failed with error: %d", error); 414 req->open = FALSE; /* closes connection */ 415 return 1; /* done */ 416 } 417 418 cmd = orgcmd; 419 while(cmd && cmdsize) { 420 char *check; 421 422 if(!strncmp(CMD_AUTH_REQUIRED, cmd, strlen(CMD_AUTH_REQUIRED))) { 423 logmsg("instructed to require authorization header"); 424 req->auth_req = TRUE; 425 } 426 else if(!strncmp(CMD_IDLE, cmd, strlen(CMD_IDLE))) { 427 logmsg("instructed to idle"); 428 req->rcmd = RCMD_IDLE; 429 req->open = TRUE; 430 } 431 else if(!strncmp(CMD_STREAM, cmd, strlen(CMD_STREAM))) { 432 logmsg("instructed to stream"); 433 req->rcmd = RCMD_STREAM; 434 } 435 else if(1 == sscanf(cmd, "pipe: %d", &num)) { 436 logmsg("instructed to allow a pipe size of %d", num); 437 if(num < 0) 438 logmsg("negative pipe size ignored"); 439 else if(num > 0) 440 req->pipe = num-1; /* decrease by one since we don't count the 441 first request in this number */ 442 } 443 else if(1 == sscanf(cmd, "skip: %d", &num)) { 444 logmsg("instructed to skip this number of bytes %d", num); 445 req->skip = num; 446 } 447 else if(1 == sscanf(cmd, "writedelay: %d", &num)) { 448 logmsg("instructed to delay %d secs between packets", num); 449 req->writedelay = num; 450 } 451 else { 452 logmsg("funny instruction found: %s", cmd); 453 } 454 /* try to deal with CRLF or just LF */ 455 check = strchr(cmd, '\r'); 456 if(!check) 457 check = strchr(cmd, '\n'); 458 459 if(check) { 460 /* get to the letter following the newline */ 461 while((*check == '\r') || (*check == '\n')) 462 check++; 463 464 if(!*check) 465 /* if we reached a zero, get out */ 466 break; 467 cmd = check; 468 } 469 else 470 break; 471 } 472 if(orgcmd) 473 free(orgcmd); 474 } 475 } 476 else { 477 if(sscanf(req->reqbuf, "CONNECT %" MAXDOCNAMELEN_TXT "s HTTP/%d.%d", 478 doc, &prot_major, &prot_minor) == 3) { 479 sprintf(logbuf, "Received a CONNECT %s HTTP/%d.%d request", 480 doc, prot_major, prot_minor); 481 logmsg("%s", logbuf); 482 483 if(req->prot_version == 10) 484 req->open = FALSE; /* HTTP 1.0 closes connection by default */ 485 486 if(!strncmp(doc, "bad", 3)) 487 /* if the host name starts with bad, we fake an error here */ 488 req->testno = DOCNUMBER_BADCONNECT; 489 else if(!strncmp(doc, "test", 4)) { 490 /* if the host name starts with test, the port number used in the 491 CONNECT line will be used as test number! */ 492 char *portp = strchr(doc, ':'); 493 if(portp && (*(portp+1) != '\0') && ISDIGIT(*(portp+1))) 494 req->testno = strtol(portp+1, NULL, 10); 495 else 496 req->testno = DOCNUMBER_CONNECT; 497 } 498 else 499 req->testno = DOCNUMBER_CONNECT; 500 } 501 else { 502 logmsg("Did not find test number in PATH"); 503 req->testno = DOCNUMBER_404; 504 } 505 } 506 } 507 else if((req->offset >= 3) && (req->testno == DOCNUMBER_NOTHING)) { 508 logmsg("** Unusual request. Starts with %02x %02x %02x", 509 line[0], line[1], line[2]); 510 } 511 512 if(!end) { 513 /* we don't have a complete request yet! */ 514 logmsg("request not complete yet"); 515 return 0; /* not complete yet */ 516 } 517 logmsg("- request found to be complete"); 518 519 if(use_gopher) { 520 /* when using gopher we cannot check the request until the entire 521 thing has been received */ 522 char *ptr; 523 524 /* find the last slash in the line */ 525 ptr = strrchr(line, '/'); 526 527 if(ptr) { 528 ptr++; /* skip the slash */ 529 530 /* skip all non-numericals following the slash */ 531 while(*ptr && !ISDIGIT(*ptr)) 532 ptr++; 533 534 req->testno = strtol(ptr, &ptr, 10); 535 536 if(req->testno > 10000) { 537 req->partno = req->testno % 10000; 538 req->testno /= 10000; 539 } 540 else 541 req->partno = 0; 542 543 sprintf(logbuf, "Requested GOPHER test number %ld part %ld", 544 req->testno, req->partno); 545 logmsg("%s", logbuf); 546 } 547 } 548 549 if(req->pipe) 550 /* we do have a full set, advance the checkindex to after the end of the 551 headers, for the pipelining case mostly */ 552 req->checkindex += (end - line) + strlen(end_of_headers); 553 554 /* **** Persistence **** 555 * 556 * If the request is a HTTP/1.0 one, we close the connection unconditionally 557 * when we're done. 558 * 559 * If the request is a HTTP/1.1 one, we MUST check for a "Connection:" 560 * header that might say "close". If it does, we close a connection when 561 * this request is processed. Otherwise, we keep the connection alive for X 562 * seconds. 563 */ 564 565 do { 566 if(got_exit_signal) 567 return 1; /* done */ 568 569 if((req->cl==0) && curlx_strnequal("Content-Length:", line, 15)) { 570 /* If we don't ignore content-length, we read it and we read the whole 571 request including the body before we return. If we've been told to 572 ignore the content-length, we will return as soon as all headers 573 have been received */ 574 char *endptr; 575 char *ptr = line + 15; 576 unsigned long clen = 0; 577 while(*ptr && ISSPACE(*ptr)) 578 ptr++; 579 endptr = ptr; 580 SET_ERRNO(0); 581 clen = strtoul(ptr, &endptr, 10); 582 if((ptr == endptr) || !ISSPACE(*endptr) || (ERANGE == ERRNO)) { 583 /* this assumes that a zero Content-Length is valid */ 584 logmsg("Found invalid Content-Length: (%s) in the request", ptr); 585 req->open = FALSE; /* closes connection */ 586 return 1; /* done */ 587 } 588 req->cl = clen - req->skip; 589 590 logmsg("Found Content-Length: %lu in the request", clen); 591 if(req->skip) 592 logmsg("... but will abort after %zu bytes", req->cl); 593 break; 594 } 595 else if(curlx_strnequal("Transfer-Encoding: chunked", line, 596 strlen("Transfer-Encoding: chunked"))) { 597 /* chunked data coming in */ 598 chunked = TRUE; 599 } 600 601 if(chunked) { 602 if(strstr(req->reqbuf, "\r\n0\r\n\r\n")) 603 /* end of chunks reached */ 604 return 1; /* done */ 605 else 606 return 0; /* not done */ 607 } 608 609 line = strchr(line, '\n'); 610 if(line) 611 line++; 612 613 } while(line); 614 615 if(!req->auth && strstr(req->reqbuf, "Authorization:")) { 616 req->auth = TRUE; /* Authorization: header present! */ 617 if(req->auth_req) 618 logmsg("Authorization header found, as required"); 619 } 620 621 if(!req->digest && strstr(req->reqbuf, "Authorization: Digest")) { 622 /* If the client is passing this Digest-header, we set the part number 623 to 1000. Not only to spice up the complexity of this, but to make 624 Digest stuff to work in the test suite. */ 625 req->partno += 1000; 626 req->digest = TRUE; /* header found */ 627 logmsg("Received Digest request, sending back data %ld", req->partno); 628 } 629 else if(!req->ntlm && 630 strstr(req->reqbuf, "Authorization: NTLM TlRMTVNTUAAD")) { 631 /* If the client is passing this type-3 NTLM header */ 632 req->partno += 1002; 633 req->ntlm = TRUE; /* NTLM found */ 634 logmsg("Received NTLM type-3, sending back data %ld", req->partno); 635 if(req->cl) { 636 logmsg(" Expecting %zu POSTed bytes", req->cl); 637 } 638 } 639 else if(!req->ntlm && 640 strstr(req->reqbuf, "Authorization: NTLM TlRMTVNTUAAB")) { 641 /* If the client is passing this type-1 NTLM header */ 642 req->partno += 1001; 643 req->ntlm = TRUE; /* NTLM found */ 644 logmsg("Received NTLM type-1, sending back data %ld", req->partno); 645 } 646 else if((req->partno >= 1000) && 647 strstr(req->reqbuf, "Authorization: Basic")) { 648 /* If the client is passing this Basic-header and the part number is 649 already >=1000, we add 1 to the part number. This allows simple Basic 650 authentication negotiation to work in the test suite. */ 651 req->partno += 1; 652 logmsg("Received Basic request, sending back data %ld", req->partno); 653 } 654 if(strstr(req->reqbuf, "Connection: close")) 655 req->open = FALSE; /* close connection after this request */ 656 657 if(!req->pipe && 658 req->open && 659 req->prot_version >= 11 && 660 end && 661 req->reqbuf + req->offset > end + strlen(end_of_headers) && 662 !req->cl && 663 (!strncmp(req->reqbuf, "GET", strlen("GET")) || 664 !strncmp(req->reqbuf, "HEAD", strlen("HEAD")))) { 665 /* If we have a persistent connection, HTTP version >= 1.1 666 and GET/HEAD request, enable pipelining. */ 667 req->checkindex = (end - req->reqbuf) + strlen(end_of_headers); 668 req->pipelining = TRUE; 669 } 670 671 while(req->pipe) { 672 if(got_exit_signal) 673 return 1; /* done */ 674 /* scan for more header ends within this chunk */ 675 line = &req->reqbuf[req->checkindex]; 676 end = strstr(line, end_of_headers); 677 if(!end) 678 break; 679 req->checkindex += (end - line) + strlen(end_of_headers); 680 req->pipe--; 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->cl > 0) { 693 if(req->cl <= req->offset - (end - req->reqbuf) - strlen(end_of_headers)) 694 return 1; /* done */ 695 else 696 return 0; /* not complete yet */ 697 } 698 699 return 1; /* done */ 700} 701 702/* store the entire request in a file */ 703static void storerequest(char *reqbuf, size_t totalsize) 704{ 705 int res; 706 int error = 0; 707 size_t written; 708 size_t writeleft; 709 FILE *dump; 710 711 if (reqbuf == NULL) 712 return; 713 if (totalsize == 0) 714 return; 715 716 do { 717 dump = fopen(REQUEST_DUMP, "ab"); 718 } while ((dump == NULL) && ((error = ERRNO) == EINTR)); 719 if (dump == NULL) { 720 logmsg("Error opening file %s error: %d %s", 721 REQUEST_DUMP, error, strerror(error)); 722 logmsg("Failed to write request input to " REQUEST_DUMP); 723 return; 724 } 725 726 writeleft = totalsize; 727 do { 728 written = fwrite(&reqbuf[totalsize-writeleft], 729 1, writeleft, dump); 730 if(got_exit_signal) 731 goto storerequest_cleanup; 732 if(written > 0) 733 writeleft -= written; 734 } while ((writeleft > 0) && ((error = ERRNO) == EINTR)); 735 736 if(writeleft == 0) 737 logmsg("Wrote request (%zu bytes) input to " REQUEST_DUMP, totalsize); 738 else if(writeleft > 0) { 739 logmsg("Error writing file %s error: %d %s", 740 REQUEST_DUMP, error, strerror(error)); 741 logmsg("Wrote only (%zu bytes) of (%zu bytes) request input to %s", 742 totalsize-writeleft, totalsize, REQUEST_DUMP); 743 } 744 745storerequest_cleanup: 746 747 do { 748 res = fclose(dump); 749 } while(res && ((error = ERRNO) == EINTR)); 750 if(res) 751 logmsg("Error closing file %s error: %d %s", 752 REQUEST_DUMP, error, strerror(error)); 753} 754 755/* return 0 on success, non-zero on failure */ 756static int get_request(curl_socket_t sock, struct httprequest *req) 757{ 758 int error; 759 int fail = 0; 760 int done_processing = 0; 761 char *reqbuf = req->reqbuf; 762 ssize_t got = 0; 763 764 char *pipereq = NULL; 765 size_t pipereq_length = 0; 766 767 if(req->pipelining) { 768 pipereq = reqbuf + req->checkindex; 769 pipereq_length = req->offset - req->checkindex; 770 } 771 772 /*** Init the httprequest structure properly for the upcoming request ***/ 773 774 req->checkindex = 0; 775 req->offset = 0; 776 req->testno = DOCNUMBER_NOTHING; 777 req->partno = 0; 778 req->open = TRUE; 779 req->auth_req = FALSE; 780 req->auth = FALSE; 781 req->cl = 0; 782 req->digest = FALSE; 783 req->ntlm = FALSE; 784 req->pipe = 0; 785 req->skip = 0; 786 req->writedelay = 0; 787 req->rcmd = RCMD_NORMALREQ; 788 req->prot_version = 0; 789 req->pipelining = FALSE; 790 req->callcount = 0; 791 792 /*** end of httprequest init ***/ 793 794 while(!done_processing && (req->offset < REQBUFSIZ-1)) { 795 if(pipereq_length && pipereq) { 796 memmove(reqbuf, pipereq, pipereq_length); 797 got = curlx_uztosz(pipereq_length); 798 pipereq_length = 0; 799 } 800 else { 801 if(req->skip) 802 /* we are instructed to not read the entire thing, so we make sure to 803 only read what we're supposed to and NOT read the enire thing the 804 client wants to send! */ 805 got = sread(sock, reqbuf + req->offset, req->cl); 806 else 807 got = sread(sock, reqbuf + req->offset, REQBUFSIZ-1 - req->offset); 808 } 809 if(got_exit_signal) 810 return 1; 811 if(got == 0) { 812 logmsg("Connection closed by client"); 813 fail = 1; 814 } 815 else if(got < 0) { 816 error = SOCKERRNO; 817 logmsg("recv() returned error: (%d) %s", error, strerror(error)); 818 fail = 1; 819 } 820 if(fail) { 821 /* dump the request received so far to the external file */ 822 reqbuf[req->offset] = '\0'; 823 storerequest(reqbuf, req->offset); 824 return 1; 825 } 826 827 logmsg("Read %zd bytes", got); 828 829 req->offset += (size_t)got; 830 reqbuf[req->offset] = '\0'; 831 832 done_processing = ProcessRequest(req); 833 if(got_exit_signal) 834 return 1; 835 if(done_processing && req->pipe) { 836 logmsg("Waiting for another piped request"); 837 done_processing = 0; 838 req->pipe--; 839 } 840 } 841 842 if((req->offset == REQBUFSIZ-1) && (got > 0)) { 843 logmsg("Request would overflow buffer, closing connection"); 844 /* dump request received so far to external file anyway */ 845 reqbuf[REQBUFSIZ-1] = '\0'; 846 fail = 1; 847 } 848 else if(req->offset > REQBUFSIZ-1) { 849 logmsg("Request buffer overflow, closing connection"); 850 /* dump request received so far to external file anyway */ 851 reqbuf[REQBUFSIZ-1] = '\0'; 852 fail = 1; 853 } 854 else 855 reqbuf[req->offset] = '\0'; 856 857 /* dump the request to an external file */ 858 storerequest(reqbuf, req->pipelining ? req->checkindex : req->offset); 859 if(got_exit_signal) 860 return 1; 861 862 return fail; /* return 0 on success */ 863} 864 865/* returns -1 on failure */ 866static int send_doc(curl_socket_t sock, struct httprequest *req) 867{ 868 ssize_t written; 869 size_t count; 870 const char *buffer; 871 char *ptr=NULL; 872 FILE *stream; 873 char *cmd=NULL; 874 size_t cmdsize=0; 875 FILE *dump; 876 bool persistant = TRUE; 877 bool sendfailure = FALSE; 878 size_t responsesize; 879 int error = 0; 880 int res; 881 882 static char weare[256]; 883 884 char partbuf[80]="data"; 885 886 logmsg("Send response test%ld section <data%ld>", req->testno, req->partno); 887 888 switch(req->rcmd) { 889 default: 890 case RCMD_NORMALREQ: 891 break; /* continue with business as usual */ 892 case RCMD_STREAM: 893#define STREAMTHIS "a string to stream 01234567890\n" 894 count = strlen(STREAMTHIS); 895 for (;;) { 896 written = swrite(sock, STREAMTHIS, count); 897 if(got_exit_signal) 898 return -1; 899 if(written != (ssize_t)count) { 900 logmsg("Stopped streaming"); 901 break; 902 } 903 } 904 return -1; 905 case RCMD_IDLE: 906 /* Do nothing. Sit idle. Pretend it rains. */ 907 return 0; 908 } 909 910 req->open = FALSE; 911 912 if(req->testno < 0) { 913 size_t msglen; 914 char msgbuf[64]; 915 916 switch(req->testno) { 917 case DOCNUMBER_QUIT: 918 logmsg("Replying to QUIT"); 919 buffer = docquit; 920 break; 921 case DOCNUMBER_WERULEZ: 922 /* we got a "friends?" question, reply back that we sure are */ 923 logmsg("Identifying ourselves as friends"); 924 sprintf(msgbuf, "WE ROOLZ: %ld\r\n", (long)getpid()); 925 msglen = strlen(msgbuf); 926 if(use_gopher) 927 sprintf(weare, "%s", msgbuf); 928 else 929 sprintf(weare, "HTTP/1.1 200 OK\r\nContent-Length: %zu\r\n\r\n%s", 930 msglen, msgbuf); 931 buffer = weare; 932 break; 933 case DOCNUMBER_INTERNAL: 934 logmsg("Bailing out due to internal error"); 935 return -1; 936 case DOCNUMBER_CONNECT: 937 logmsg("Replying to CONNECT"); 938 buffer = docconnect; 939 break; 940 case DOCNUMBER_BADCONNECT: 941 logmsg("Replying to a bad CONNECT"); 942 buffer = docbadconnect; 943 break; 944 case DOCNUMBER_404: 945 default: 946 logmsg("Replying to with a 404"); 947 buffer = doc404; 948 break; 949 } 950 951 count = strlen(buffer); 952 } 953 else { 954 char *filename = test2file(req->testno); 955 956 if(0 != req->partno) 957 sprintf(partbuf, "data%ld", req->partno); 958 959 stream=fopen(filename, "rb"); 960 if(!stream) { 961 error = ERRNO; 962 logmsg("fopen() failed with error: %d %s", error, strerror(error)); 963 logmsg("Error opening file: %s", filename); 964 logmsg("Couldn't open test file"); 965 return 0; 966 } 967 else { 968 error = getpart(&ptr, &count, "reply", partbuf, stream); 969 fclose(stream); 970 if(error) { 971 logmsg("getpart() failed with error: %d", error); 972 return 0; 973 } 974 buffer = ptr; 975 } 976 977 if(got_exit_signal) { 978 if(ptr) 979 free(ptr); 980 return -1; 981 } 982 983 /* re-open the same file again */ 984 stream=fopen(filename, "rb"); 985 if(!stream) { 986 error = ERRNO; 987 logmsg("fopen() failed with error: %d %s", error, strerror(error)); 988 logmsg("Error opening file: %s", filename); 989 logmsg("Couldn't open test file"); 990 if(ptr) 991 free(ptr); 992 return 0; 993 } 994 else { 995 /* get the custom server control "commands" */ 996 error = getpart(&cmd, &cmdsize, "reply", "postcmd", stream); 997 fclose(stream); 998 if(error) { 999 logmsg("getpart() failed with error: %d", error); 1000 if(ptr) 1001 free(ptr); 1002 return 0; 1003 } 1004 } 1005 } 1006 1007 if(got_exit_signal) { 1008 if(ptr) 1009 free(ptr); 1010 if(cmd) 1011 free(cmd); 1012 return -1; 1013 } 1014 1015 /* If the word 'swsclose' is present anywhere in the reply chunk, the 1016 connection will be closed after the data has been sent to the requesting 1017 client... */ 1018 if(strstr(buffer, "swsclose") || !count) { 1019 persistant = FALSE; 1020 logmsg("connection close instruction \"swsclose\" found in response"); 1021 } 1022 if(strstr(buffer, "swsbounce")) { 1023 prevbounce = TRUE; 1024 logmsg("enable \"swsbounce\" in the next request"); 1025 } 1026 else 1027 prevbounce = FALSE; 1028 1029 dump = fopen(RESPONSE_DUMP, "ab"); 1030 if(!dump) { 1031 error = ERRNO; 1032 logmsg("fopen() failed with error: %d %s", error, strerror(error)); 1033 logmsg("Error opening file: %s", RESPONSE_DUMP); 1034 logmsg("couldn't create logfile: " RESPONSE_DUMP); 1035 if(ptr) 1036 free(ptr); 1037 if(cmd) 1038 free(cmd); 1039 return -1; 1040 } 1041 1042 responsesize = count; 1043 do { 1044 /* Ok, we send no more than 200 bytes at a time, just to make sure that 1045 larger chunks are split up so that the client will need to do multiple 1046 recv() calls to get it and thus we exercise that code better */ 1047 size_t num = count; 1048 if(num > 200) 1049 num = 200; 1050 written = swrite(sock, buffer, num); 1051 if (written < 0) { 1052 sendfailure = TRUE; 1053 break; 1054 } 1055 else { 1056 logmsg("Sent off %zd bytes", written); 1057 } 1058 if (req->writedelay) { 1059 logmsg("Pausing %d seconds", req->writedelay); 1060 sleep(req->writedelay); 1061 } 1062 /* write to file as well */ 1063 fwrite(buffer, 1, (size_t)written, dump); 1064 if(got_exit_signal) 1065 break; 1066 1067 count -= written; 1068 buffer += written; 1069 } while(count>0); 1070 1071 do { 1072 res = fclose(dump); 1073 } while(res && ((error = ERRNO) == EINTR)); 1074 if(res) 1075 logmsg("Error closing file %s error: %d %s", 1076 RESPONSE_DUMP, error, strerror(error)); 1077 1078 if(got_exit_signal) { 1079 if(ptr) 1080 free(ptr); 1081 if(cmd) 1082 free(cmd); 1083 return -1; 1084 } 1085 1086 if(sendfailure) { 1087 logmsg("Sending response failed. Only (%zu bytes) of (%zu bytes) were sent", 1088 responsesize-count, responsesize); 1089 if(ptr) 1090 free(ptr); 1091 if(cmd) 1092 free(cmd); 1093 return -1; 1094 } 1095 1096 logmsg("Response sent (%zu bytes) and written to " RESPONSE_DUMP, 1097 responsesize); 1098 1099 if(ptr) 1100 free(ptr); 1101 1102 if(cmdsize > 0 ) { 1103 char command[32]; 1104 int quarters; 1105 int num; 1106 ptr=cmd; 1107 do { 1108 if(2 == sscanf(ptr, "%31s %d", command, &num)) { 1109 if(!strcmp("wait", command)) { 1110 logmsg("Told to sleep for %d seconds", num); 1111 quarters = num * 4; 1112 while(quarters > 0) { 1113 quarters--; 1114 res = wait_ms(250); 1115 if(got_exit_signal) 1116 break; 1117 if(res) { 1118 /* should not happen */ 1119 error = SOCKERRNO; 1120 logmsg("wait_ms() failed with error: (%d) %s", 1121 error, strerror(error)); 1122 break; 1123 } 1124 } 1125 if(!quarters) 1126 logmsg("Continuing after sleeping %d seconds", num); 1127 } 1128 else 1129 logmsg("Unknown command in reply command section"); 1130 } 1131 ptr = strchr(ptr, '\n'); 1132 if(ptr) 1133 ptr++; 1134 else 1135 ptr = NULL; 1136 } while(ptr && *ptr); 1137 } 1138 if(cmd) 1139 free(cmd); 1140 1141 req->open = use_gopher?FALSE:persistant; 1142 1143 prevtestno = req->testno; 1144 prevpartno = req->partno; 1145 1146 return 0; 1147} 1148 1149 1150int main(int argc, char *argv[]) 1151{ 1152 srvr_sockaddr_union_t me; 1153 curl_socket_t sock = CURL_SOCKET_BAD; 1154 curl_socket_t msgsock = CURL_SOCKET_BAD; 1155 int wrotepidfile = 0; 1156 int flag; 1157 unsigned short port = DEFAULT_PORT; 1158 char *pidname= (char *)".http.pid"; 1159 struct httprequest req; 1160 int rc; 1161 int error; 1162 int arg=1; 1163 long pid; 1164#ifdef CURL_SWS_FORK_ENABLED 1165 bool use_fork = FALSE; 1166#endif 1167 1168 while(argc>arg) { 1169 if(!strcmp("--version", argv[arg])) { 1170 printf("sws IPv4%s" 1171#ifdef CURL_SWS_FORK_ENABLED 1172 " FORK" 1173#endif 1174 "\n" 1175 , 1176#ifdef ENABLE_IPV6 1177 "/IPv6" 1178#else 1179 "" 1180#endif 1181 ); 1182 return 0; 1183 } 1184 else if(!strcmp("--pidfile", argv[arg])) { 1185 arg++; 1186 if(argc>arg) 1187 pidname = argv[arg++]; 1188 } 1189 else if(!strcmp("--logfile", argv[arg])) { 1190 arg++; 1191 if(argc>arg) 1192 serverlogfile = argv[arg++]; 1193 } 1194 else if(!strcmp("--gopher", argv[arg])) { 1195 arg++; 1196 use_gopher = TRUE; 1197 end_of_headers = "\r\n"; /* gopher style is much simpler */ 1198 } 1199 else if(!strcmp("--ipv4", argv[arg])) { 1200#ifdef ENABLE_IPV6 1201 ipv_inuse = "IPv4"; 1202 use_ipv6 = FALSE; 1203#endif 1204 arg++; 1205 } 1206 else if(!strcmp("--ipv6", argv[arg])) { 1207#ifdef ENABLE_IPV6 1208 ipv_inuse = "IPv6"; 1209 use_ipv6 = TRUE; 1210#endif 1211 arg++; 1212 } 1213#ifdef CURL_SWS_FORK_ENABLED 1214 else if(!strcmp("--fork", argv[arg])) { 1215 use_fork=TRUE; 1216 arg++; 1217 } 1218#endif 1219 else if(!strcmp("--port", argv[arg])) { 1220 arg++; 1221 if(argc>arg) { 1222 char *endptr; 1223 unsigned long ulnum = strtoul(argv[arg], &endptr, 10); 1224 if((endptr != argv[arg] + strlen(argv[arg])) || 1225 (ulnum < 1025UL) || (ulnum > 65535UL)) { 1226 fprintf(stderr, "sws: invalid --port argument (%s)\n", 1227 argv[arg]); 1228 return 0; 1229 } 1230 port = curlx_ultous(ulnum); 1231 arg++; 1232 } 1233 } 1234 else if(!strcmp("--srcdir", argv[arg])) { 1235 arg++; 1236 if(argc>arg) { 1237 path = argv[arg]; 1238 arg++; 1239 } 1240 } 1241 else { 1242 puts("Usage: sws [option]\n" 1243 " --version\n" 1244 " --logfile [file]\n" 1245 " --pidfile [file]\n" 1246 " --ipv4\n" 1247 " --ipv6\n" 1248 " --port [port]\n" 1249 " --srcdir [path]\n" 1250 " --gopher\n" 1251 " --fork"); 1252 return 0; 1253 } 1254 } 1255 1256#ifdef WIN32 1257 win32_init(); 1258 atexit(win32_cleanup); 1259#endif 1260 1261 install_signal_handlers(); 1262 1263 pid = (long)getpid(); 1264 1265#ifdef ENABLE_IPV6 1266 if(!use_ipv6) 1267#endif 1268 sock = socket(AF_INET, SOCK_STREAM, 0); 1269#ifdef ENABLE_IPV6 1270 else 1271 sock = socket(AF_INET6, SOCK_STREAM, 0); 1272#endif 1273 1274 if(CURL_SOCKET_BAD == sock) { 1275 error = SOCKERRNO; 1276 logmsg("Error creating socket: (%d) %s", 1277 error, strerror(error)); 1278 goto sws_cleanup; 1279 } 1280 1281 flag = 1; 1282 if (0 != setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 1283 (void *)&flag, sizeof(flag))) { 1284 error = SOCKERRNO; 1285 logmsg("setsockopt(SO_REUSEADDR) failed with error: (%d) %s", 1286 error, strerror(error)); 1287 goto sws_cleanup; 1288 } 1289 1290#ifdef ENABLE_IPV6 1291 if(!use_ipv6) { 1292#endif 1293 memset(&me.sa4, 0, sizeof(me.sa4)); 1294 me.sa4.sin_family = AF_INET; 1295 me.sa4.sin_addr.s_addr = INADDR_ANY; 1296 me.sa4.sin_port = htons(port); 1297 rc = bind(sock, &me.sa, sizeof(me.sa4)); 1298#ifdef ENABLE_IPV6 1299 } 1300 else { 1301 memset(&me.sa6, 0, sizeof(me.sa6)); 1302 me.sa6.sin6_family = AF_INET6; 1303 me.sa6.sin6_addr = in6addr_any; 1304 me.sa6.sin6_port = htons(port); 1305 rc = bind(sock, &me.sa, sizeof(me.sa6)); 1306 } 1307#endif /* ENABLE_IPV6 */ 1308 if(0 != rc) { 1309 error = SOCKERRNO; 1310 logmsg("Error binding socket on port %hu: (%d) %s", 1311 port, error, strerror(error)); 1312 goto sws_cleanup; 1313 } 1314 1315 logmsg("Running %s %s version on port %d", 1316 use_gopher?"GOPHER":"HTTP", ipv_inuse, (int)port); 1317 1318 /* start accepting connections */ 1319 rc = listen(sock, 5); 1320 if(0 != rc) { 1321 error = SOCKERRNO; 1322 logmsg("listen() failed with error: (%d) %s", 1323 error, strerror(error)); 1324 goto sws_cleanup; 1325 } 1326 1327 /* 1328 ** As soon as this server writes its pid file the test harness will 1329 ** attempt to connect to this server and initiate its verification. 1330 */ 1331 1332 wrotepidfile = write_pidfile(pidname); 1333 if(!wrotepidfile) 1334 goto sws_cleanup; 1335 1336 for (;;) { 1337 msgsock = accept(sock, NULL, NULL); 1338 1339 if(got_exit_signal) 1340 break; 1341 if (CURL_SOCKET_BAD == msgsock) { 1342 error = SOCKERRNO; 1343 logmsg("MAJOR ERROR: accept() failed with error: (%d) %s", 1344 error, strerror(error)); 1345 break; 1346 } 1347 1348 /* 1349 ** As soon as this server acepts a connection from the test harness it 1350 ** must set the server logs advisor read lock to indicate that server 1351 ** logs should not be read until this lock is removed by this server. 1352 */ 1353 1354 set_advisor_read_lock(SERVERLOGS_LOCK); 1355 serverlogslocked = 1; 1356 1357#ifdef CURL_SWS_FORK_ENABLED 1358 if(use_fork) { 1359 /* The fork enabled version just forks off the child and don't care 1360 about it anymore, so don't assume otherwise. Beware and don't do 1361 this at home. */ 1362 rc = fork(); 1363 if(-1 == rc) { 1364 printf("MAJOR ERROR: fork() failed!\n"); 1365 break; 1366 } 1367 } 1368 else 1369 /* not a fork, just set rc so the following proceeds nicely */ 1370 rc = 0; 1371 /* 0 is returned to the child */ 1372 if(0 == rc) { 1373#endif 1374 logmsg("====> Client connect"); 1375 1376#ifdef TCP_NODELAY 1377 /* 1378 * Disable the Nagle algorithm to make it easier to send out a large 1379 * response in many small segments to torture the clients more. 1380 */ 1381 flag = 1; 1382 if (setsockopt(msgsock, IPPROTO_TCP, TCP_NODELAY, 1383 (void *)&flag, sizeof(flag)) == -1) { 1384 logmsg("====> TCP_NODELAY failed"); 1385 } 1386#endif 1387 1388 /* initialization of httprequest struct is done in get_request(), but due 1389 to pipelining treatment the pipelining struct field must be initialized 1390 previously to FALSE every time a new connection arrives. */ 1391 1392 req.pipelining = FALSE; 1393 1394 do { 1395 if(got_exit_signal) 1396 break; 1397 1398 if(get_request(msgsock, &req)) 1399 /* non-zero means error, break out of loop */ 1400 break; 1401 1402 if(prevbounce) { 1403 /* bounce treatment requested */ 1404 if((req.testno == prevtestno) && 1405 (req.partno == prevpartno)) { 1406 req.partno++; 1407 logmsg("BOUNCE part number to %ld", req.partno); 1408 } 1409 else { 1410 prevbounce = FALSE; 1411 prevtestno = -1; 1412 prevpartno = -1; 1413 } 1414 } 1415 1416 send_doc(msgsock, &req); 1417 if(got_exit_signal) 1418 break; 1419 1420 if((req.testno < 0) && (req.testno != DOCNUMBER_CONNECT)) { 1421 logmsg("special request received, no persistency"); 1422 break; 1423 } 1424 if(!req.open) { 1425 logmsg("instructed to close connection after server-reply"); 1426 break; 1427 } 1428 1429 if(req.open) { 1430 logmsg("=> persistant connection request ended, awaits new request\n"); 1431 /* 1432 const char *keepopen="[KEEPING CONNECTION OPEN]"; 1433 storerequest((char *)keepopen, strlen(keepopen)); 1434 */ 1435 } 1436 /* if we got a CONNECT, loop and get another request as well! */ 1437 } while(req.open || (req.testno == DOCNUMBER_CONNECT)); 1438 1439 if(got_exit_signal) 1440 break; 1441 1442 logmsg("====> Client disconnect"); 1443 1444 if(!req.open) 1445 /* When instructed to close connection after server-reply we 1446 wait a very small amount of time before doing so. If this 1447 is not done client might get an ECONNRESET before reading 1448 a single byte of server-reply. */ 1449 wait_ms(50); 1450 1451 sclose(msgsock); 1452 msgsock = CURL_SOCKET_BAD; 1453 1454 if(serverlogslocked) { 1455 serverlogslocked = 0; 1456 clear_advisor_read_lock(SERVERLOGS_LOCK); 1457 } 1458 1459 if (req.testno == DOCNUMBER_QUIT) 1460 break; 1461#ifdef CURL_SWS_FORK_ENABLED 1462 } 1463#endif 1464 } 1465 1466sws_cleanup: 1467 1468 if((msgsock != sock) && (msgsock != CURL_SOCKET_BAD)) 1469 sclose(msgsock); 1470 1471 if(sock != CURL_SOCKET_BAD) 1472 sclose(sock); 1473 1474 if(got_exit_signal) 1475 logmsg("signalled to die"); 1476 1477 if(wrotepidfile) 1478 unlink(pidname); 1479 1480 if(serverlogslocked) { 1481 serverlogslocked = 0; 1482 clear_advisor_read_lock(SERVERLOGS_LOCK); 1483 } 1484 1485 restore_signal_handlers(); 1486 1487 if(got_exit_signal) { 1488 logmsg("========> %s sws (port: %d pid: %ld) exits with signal (%d)", 1489 ipv_inuse, (int)port, pid, exit_signal); 1490 /* 1491 * To properly set the return status of the process we 1492 * must raise the same signal SIGINT or SIGTERM that we 1493 * caught and let the old handler take care of it. 1494 */ 1495 raise(exit_signal); 1496 } 1497 1498 logmsg("========> sws quits"); 1499 return 0; 1500} 1501 1502