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