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