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