1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2019, 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 23 #include "curl_setup.h" 24 25 #ifndef CURL_DISABLE_RTSP 26 27 #include "urldata.h" 28 #include <curl/curl.h> 29 #include "transfer.h" 30 #include "sendf.h" 31 #include "multiif.h" 32 #include "http.h" 33 #include "url.h" 34 #include "progress.h" 35 #include "rtsp.h" 36 #include "strcase.h" 37 #include "select.h" 38 #include "connect.h" 39 #include "strdup.h" 40 /* The last 3 #include files should be in this order */ 41 #include "curl_printf.h" 42 #include "curl_memory.h" 43 #include "memdebug.h" 44 45 /* 46 * TODO (general) 47 * -incoming server requests 48 * -server CSeq counter 49 * -digest authentication 50 * -connect through proxy 51 * -pipelining? 52 */ 53 54 55 #define RTP_PKT_CHANNEL(p) ((int)((unsigned char)((p)[1]))) 56 57 #define RTP_PKT_LENGTH(p) ((((int)((unsigned char)((p)[2]))) << 8) | \ 58 ((int)((unsigned char)((p)[3])))) 59 60 /* protocol-specific functions set up to be called by the main engine */ 61 static CURLcode rtsp_do(struct connectdata *conn, bool *done); 62 static CURLcode rtsp_done(struct connectdata *conn, CURLcode, bool premature); 63 static CURLcode rtsp_connect(struct connectdata *conn, bool *done); 64 static CURLcode rtsp_disconnect(struct connectdata *conn, bool dead); 65 66 static int rtsp_getsock_do(struct connectdata *conn, 67 curl_socket_t *socks, 68 int numsocks); 69 70 /* 71 * Parse and write out any available RTP data. 72 * 73 * nread: amount of data left after k->str. will be modified if RTP 74 * data is parsed and k->str is moved up 75 * readmore: whether or not the RTP parser needs more data right away 76 */ 77 static CURLcode rtsp_rtp_readwrite(struct Curl_easy *data, 78 struct connectdata *conn, 79 ssize_t *nread, 80 bool *readmore); 81 82 static CURLcode rtsp_setup_connection(struct connectdata *conn); 83 static unsigned int rtsp_conncheck(struct connectdata *check, 84 unsigned int checks_to_perform); 85 86 /* this returns the socket to wait for in the DO and DOING state for the multi 87 interface and then we're always _sending_ a request and thus we wait for 88 the single socket to become writable only */ 89 static int rtsp_getsock_do(struct connectdata *conn, 90 curl_socket_t *socks, 91 int numsocks) 92 { 93 /* write mode */ 94 (void)numsocks; /* unused, we trust it to be at least 1 */ 95 socks[0] = conn->sock[FIRSTSOCKET]; 96 return GETSOCK_WRITESOCK(0); 97 } 98 99 static 100 CURLcode rtp_client_write(struct connectdata *conn, char *ptr, size_t len); 101 102 103 /* 104 * RTSP handler interface. 105 */ 106 const struct Curl_handler Curl_handler_rtsp = { 107 "RTSP", /* scheme */ 108 rtsp_setup_connection, /* setup_connection */ 109 rtsp_do, /* do_it */ 110 rtsp_done, /* done */ 111 ZERO_NULL, /* do_more */ 112 rtsp_connect, /* connect_it */ 113 ZERO_NULL, /* connecting */ 114 ZERO_NULL, /* doing */ 115 ZERO_NULL, /* proto_getsock */ 116 rtsp_getsock_do, /* doing_getsock */ 117 ZERO_NULL, /* domore_getsock */ 118 ZERO_NULL, /* perform_getsock */ 119 rtsp_disconnect, /* disconnect */ 120 rtsp_rtp_readwrite, /* readwrite */ 121 rtsp_conncheck, /* connection_check */ 122 PORT_RTSP, /* defport */ 123 CURLPROTO_RTSP, /* protocol */ 124 PROTOPT_NONE /* flags */ 125 }; 126 127 128 static CURLcode rtsp_setup_connection(struct connectdata *conn) 129 { 130 struct RTSP *rtsp; 131 132 conn->data->req.protop = rtsp = calloc(1, sizeof(struct RTSP)); 133 if(!rtsp) 134 return CURLE_OUT_OF_MEMORY; 135 136 return CURLE_OK; 137 } 138 139 140 /* 141 * The server may send us RTP data at any point, and RTSPREQ_RECEIVE does not 142 * want to block the application forever while receiving a stream. Therefore, 143 * we cannot assume that an RTSP socket is dead just because it is readable. 144 * 145 * Instead, if it is readable, run Curl_connalive() to peek at the socket 146 * and distinguish between closed and data. 147 */ 148 static bool rtsp_connisdead(struct connectdata *check) 149 { 150 int sval; 151 bool ret_val = TRUE; 152 153 sval = SOCKET_READABLE(check->sock[FIRSTSOCKET], 0); 154 if(sval == 0) { 155 /* timeout */ 156 ret_val = FALSE; 157 } 158 else if(sval & CURL_CSELECT_ERR) { 159 /* socket is in an error state */ 160 ret_val = TRUE; 161 } 162 else if(sval & CURL_CSELECT_IN) { 163 /* readable with no error. could still be closed */ 164 ret_val = !Curl_connalive(check); 165 } 166 167 return ret_val; 168 } 169 170 /* 171 * Function to check on various aspects of a connection. 172 */ 173 static unsigned int rtsp_conncheck(struct connectdata *check, 174 unsigned int checks_to_perform) 175 { 176 unsigned int ret_val = CONNRESULT_NONE; 177 178 if(checks_to_perform & CONNCHECK_ISDEAD) { 179 if(rtsp_connisdead(check)) 180 ret_val |= CONNRESULT_DEAD; 181 } 182 183 return ret_val; 184 } 185 186 187 static CURLcode rtsp_connect(struct connectdata *conn, bool *done) 188 { 189 CURLcode httpStatus; 190 struct Curl_easy *data = conn->data; 191 192 httpStatus = Curl_http_connect(conn, done); 193 194 /* Initialize the CSeq if not already done */ 195 if(data->state.rtsp_next_client_CSeq == 0) 196 data->state.rtsp_next_client_CSeq = 1; 197 if(data->state.rtsp_next_server_CSeq == 0) 198 data->state.rtsp_next_server_CSeq = 1; 199 200 conn->proto.rtspc.rtp_channel = -1; 201 202 return httpStatus; 203 } 204 205 static CURLcode rtsp_disconnect(struct connectdata *conn, bool dead) 206 { 207 (void) dead; 208 Curl_safefree(conn->proto.rtspc.rtp_buf); 209 return CURLE_OK; 210 } 211 212 213 static CURLcode rtsp_done(struct connectdata *conn, 214 CURLcode status, bool premature) 215 { 216 struct Curl_easy *data = conn->data; 217 struct RTSP *rtsp = data->req.protop; 218 CURLcode httpStatus; 219 220 /* Bypass HTTP empty-reply checks on receive */ 221 if(data->set.rtspreq == RTSPREQ_RECEIVE) 222 premature = TRUE; 223 224 httpStatus = Curl_http_done(conn, status, premature); 225 226 if(rtsp) { 227 /* Check the sequence numbers */ 228 long CSeq_sent = rtsp->CSeq_sent; 229 long CSeq_recv = rtsp->CSeq_recv; 230 if((data->set.rtspreq != RTSPREQ_RECEIVE) && (CSeq_sent != CSeq_recv)) { 231 failf(data, 232 "The CSeq of this request %ld did not match the response %ld", 233 CSeq_sent, CSeq_recv); 234 return CURLE_RTSP_CSEQ_ERROR; 235 } 236 if(data->set.rtspreq == RTSPREQ_RECEIVE && 237 (conn->proto.rtspc.rtp_channel == -1)) { 238 infof(data, "Got an RTP Receive with a CSeq of %ld\n", CSeq_recv); 239 /* TODO CPC: Server -> Client logic here */ 240 } 241 } 242 243 return httpStatus; 244 } 245 246 static CURLcode rtsp_do(struct connectdata *conn, bool *done) 247 { 248 struct Curl_easy *data = conn->data; 249 CURLcode result = CURLE_OK; 250 Curl_RtspReq rtspreq = data->set.rtspreq; 251 struct RTSP *rtsp = data->req.protop; 252 Curl_send_buffer *req_buffer; 253 curl_off_t postsize = 0; /* for ANNOUNCE and SET_PARAMETER */ 254 curl_off_t putsize = 0; /* for ANNOUNCE and SET_PARAMETER */ 255 256 const char *p_request = NULL; 257 const char *p_session_id = NULL; 258 const char *p_accept = NULL; 259 const char *p_accept_encoding = NULL; 260 const char *p_range = NULL; 261 const char *p_referrer = NULL; 262 const char *p_stream_uri = NULL; 263 const char *p_transport = NULL; 264 const char *p_uagent = NULL; 265 const char *p_proxyuserpwd = NULL; 266 const char *p_userpwd = NULL; 267 268 *done = TRUE; 269 270 rtsp->CSeq_sent = data->state.rtsp_next_client_CSeq; 271 rtsp->CSeq_recv = 0; 272 273 /* Setup the 'p_request' pointer to the proper p_request string 274 * Since all RTSP requests are included here, there is no need to 275 * support custom requests like HTTP. 276 **/ 277 data->set.opt_no_body = TRUE; /* most requests don't contain a body */ 278 switch(rtspreq) { 279 default: 280 failf(data, "Got invalid RTSP request"); 281 return CURLE_BAD_FUNCTION_ARGUMENT; 282 case RTSPREQ_OPTIONS: 283 p_request = "OPTIONS"; 284 break; 285 case RTSPREQ_DESCRIBE: 286 p_request = "DESCRIBE"; 287 data->set.opt_no_body = FALSE; 288 break; 289 case RTSPREQ_ANNOUNCE: 290 p_request = "ANNOUNCE"; 291 break; 292 case RTSPREQ_SETUP: 293 p_request = "SETUP"; 294 break; 295 case RTSPREQ_PLAY: 296 p_request = "PLAY"; 297 break; 298 case RTSPREQ_PAUSE: 299 p_request = "PAUSE"; 300 break; 301 case RTSPREQ_TEARDOWN: 302 p_request = "TEARDOWN"; 303 break; 304 case RTSPREQ_GET_PARAMETER: 305 /* GET_PARAMETER's no_body status is determined later */ 306 p_request = "GET_PARAMETER"; 307 data->set.opt_no_body = FALSE; 308 break; 309 case RTSPREQ_SET_PARAMETER: 310 p_request = "SET_PARAMETER"; 311 break; 312 case RTSPREQ_RECORD: 313 p_request = "RECORD"; 314 break; 315 case RTSPREQ_RECEIVE: 316 p_request = ""; 317 /* Treat interleaved RTP as body*/ 318 data->set.opt_no_body = FALSE; 319 break; 320 case RTSPREQ_LAST: 321 failf(data, "Got invalid RTSP request: RTSPREQ_LAST"); 322 return CURLE_BAD_FUNCTION_ARGUMENT; 323 } 324 325 if(rtspreq == RTSPREQ_RECEIVE) { 326 Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, -1); 327 328 return result; 329 } 330 331 p_session_id = data->set.str[STRING_RTSP_SESSION_ID]; 332 if(!p_session_id && 333 (rtspreq & ~(RTSPREQ_OPTIONS | RTSPREQ_DESCRIBE | RTSPREQ_SETUP))) { 334 failf(data, "Refusing to issue an RTSP request [%s] without a session ID.", 335 p_request); 336 return CURLE_BAD_FUNCTION_ARGUMENT; 337 } 338 339 /* TODO: proxy? */ 340 341 /* Stream URI. Default to server '*' if not specified */ 342 if(data->set.str[STRING_RTSP_STREAM_URI]) { 343 p_stream_uri = data->set.str[STRING_RTSP_STREAM_URI]; 344 } 345 else { 346 p_stream_uri = "*"; 347 } 348 349 /* Transport Header for SETUP requests */ 350 p_transport = Curl_checkheaders(conn, "Transport"); 351 if(rtspreq == RTSPREQ_SETUP && !p_transport) { 352 /* New Transport: setting? */ 353 if(data->set.str[STRING_RTSP_TRANSPORT]) { 354 Curl_safefree(conn->allocptr.rtsp_transport); 355 356 conn->allocptr.rtsp_transport = 357 aprintf("Transport: %s\r\n", 358 data->set.str[STRING_RTSP_TRANSPORT]); 359 if(!conn->allocptr.rtsp_transport) 360 return CURLE_OUT_OF_MEMORY; 361 } 362 else { 363 failf(data, 364 "Refusing to issue an RTSP SETUP without a Transport: header."); 365 return CURLE_BAD_FUNCTION_ARGUMENT; 366 } 367 368 p_transport = conn->allocptr.rtsp_transport; 369 } 370 371 /* Accept Headers for DESCRIBE requests */ 372 if(rtspreq == RTSPREQ_DESCRIBE) { 373 /* Accept Header */ 374 p_accept = Curl_checkheaders(conn, "Accept")? 375 NULL:"Accept: application/sdp\r\n"; 376 377 /* Accept-Encoding header */ 378 if(!Curl_checkheaders(conn, "Accept-Encoding") && 379 data->set.str[STRING_ENCODING]) { 380 Curl_safefree(conn->allocptr.accept_encoding); 381 conn->allocptr.accept_encoding = 382 aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]); 383 384 if(!conn->allocptr.accept_encoding) 385 return CURLE_OUT_OF_MEMORY; 386 387 p_accept_encoding = conn->allocptr.accept_encoding; 388 } 389 } 390 391 /* The User-Agent string might have been allocated in url.c already, because 392 it might have been used in the proxy connect, but if we have got a header 393 with the user-agent string specified, we erase the previously made string 394 here. */ 395 if(Curl_checkheaders(conn, "User-Agent") && conn->allocptr.uagent) { 396 Curl_safefree(conn->allocptr.uagent); 397 conn->allocptr.uagent = NULL; 398 } 399 else if(!Curl_checkheaders(conn, "User-Agent") && 400 data->set.str[STRING_USERAGENT]) { 401 p_uagent = conn->allocptr.uagent; 402 } 403 404 /* setup the authentication headers */ 405 result = Curl_http_output_auth(conn, p_request, p_stream_uri, FALSE); 406 if(result) 407 return result; 408 409 p_proxyuserpwd = conn->allocptr.proxyuserpwd; 410 p_userpwd = conn->allocptr.userpwd; 411 412 /* Referrer */ 413 Curl_safefree(conn->allocptr.ref); 414 if(data->change.referer && !Curl_checkheaders(conn, "Referer")) 415 conn->allocptr.ref = aprintf("Referer: %s\r\n", data->change.referer); 416 else 417 conn->allocptr.ref = NULL; 418 419 p_referrer = conn->allocptr.ref; 420 421 /* 422 * Range Header 423 * Only applies to PLAY, PAUSE, RECORD 424 * 425 * Go ahead and use the Range stuff supplied for HTTP 426 */ 427 if(data->state.use_range && 428 (rtspreq & (RTSPREQ_PLAY | RTSPREQ_PAUSE | RTSPREQ_RECORD))) { 429 430 /* Check to see if there is a range set in the custom headers */ 431 if(!Curl_checkheaders(conn, "Range") && data->state.range) { 432 Curl_safefree(conn->allocptr.rangeline); 433 conn->allocptr.rangeline = aprintf("Range: %s\r\n", data->state.range); 434 p_range = conn->allocptr.rangeline; 435 } 436 } 437 438 /* 439 * Sanity check the custom headers 440 */ 441 if(Curl_checkheaders(conn, "CSeq")) { 442 failf(data, "CSeq cannot be set as a custom header."); 443 return CURLE_RTSP_CSEQ_ERROR; 444 } 445 if(Curl_checkheaders(conn, "Session")) { 446 failf(data, "Session ID cannot be set as a custom header."); 447 return CURLE_BAD_FUNCTION_ARGUMENT; 448 } 449 450 /* Initialize a dynamic send buffer */ 451 req_buffer = Curl_add_buffer_init(); 452 453 if(!req_buffer) 454 return CURLE_OUT_OF_MEMORY; 455 456 result = 457 Curl_add_bufferf(&req_buffer, 458 "%s %s RTSP/1.0\r\n" /* Request Stream-URI RTSP/1.0 */ 459 "CSeq: %ld\r\n", /* CSeq */ 460 p_request, p_stream_uri, rtsp->CSeq_sent); 461 if(result) 462 return result; 463 464 /* 465 * Rather than do a normal alloc line, keep the session_id unformatted 466 * to make comparison easier 467 */ 468 if(p_session_id) { 469 result = Curl_add_bufferf(&req_buffer, "Session: %s\r\n", p_session_id); 470 if(result) 471 return result; 472 } 473 474 /* 475 * Shared HTTP-like options 476 */ 477 result = Curl_add_bufferf(&req_buffer, 478 "%s" /* transport */ 479 "%s" /* accept */ 480 "%s" /* accept-encoding */ 481 "%s" /* range */ 482 "%s" /* referrer */ 483 "%s" /* user-agent */ 484 "%s" /* proxyuserpwd */ 485 "%s" /* userpwd */ 486 , 487 p_transport ? p_transport : "", 488 p_accept ? p_accept : "", 489 p_accept_encoding ? p_accept_encoding : "", 490 p_range ? p_range : "", 491 p_referrer ? p_referrer : "", 492 p_uagent ? p_uagent : "", 493 p_proxyuserpwd ? p_proxyuserpwd : "", 494 p_userpwd ? p_userpwd : ""); 495 496 /* 497 * Free userpwd now --- cannot reuse this for Negotiate and possibly NTLM 498 * with basic and digest, it will be freed anyway by the next request 499 */ 500 Curl_safefree(conn->allocptr.userpwd); 501 conn->allocptr.userpwd = NULL; 502 503 if(result) 504 return result; 505 506 if((rtspreq == RTSPREQ_SETUP) || (rtspreq == RTSPREQ_DESCRIBE)) { 507 result = Curl_add_timecondition(data, req_buffer); 508 if(result) 509 return result; 510 } 511 512 result = Curl_add_custom_headers(conn, FALSE, req_buffer); 513 if(result) 514 return result; 515 516 if(rtspreq == RTSPREQ_ANNOUNCE || 517 rtspreq == RTSPREQ_SET_PARAMETER || 518 rtspreq == RTSPREQ_GET_PARAMETER) { 519 520 if(data->set.upload) { 521 putsize = data->state.infilesize; 522 data->set.httpreq = HTTPREQ_PUT; 523 524 } 525 else { 526 postsize = (data->state.infilesize != -1)? 527 data->state.infilesize: 528 (data->set.postfields? (curl_off_t)strlen(data->set.postfields):0); 529 data->set.httpreq = HTTPREQ_POST; 530 } 531 532 if(putsize > 0 || postsize > 0) { 533 /* As stated in the http comments, it is probably not wise to 534 * actually set a custom Content-Length in the headers */ 535 if(!Curl_checkheaders(conn, "Content-Length")) { 536 result = 537 Curl_add_bufferf(&req_buffer, 538 "Content-Length: %" CURL_FORMAT_CURL_OFF_T"\r\n", 539 (data->set.upload ? putsize : postsize)); 540 if(result) 541 return result; 542 } 543 544 if(rtspreq == RTSPREQ_SET_PARAMETER || 545 rtspreq == RTSPREQ_GET_PARAMETER) { 546 if(!Curl_checkheaders(conn, "Content-Type")) { 547 result = Curl_add_bufferf(&req_buffer, 548 "Content-Type: text/parameters\r\n"); 549 if(result) 550 return result; 551 } 552 } 553 554 if(rtspreq == RTSPREQ_ANNOUNCE) { 555 if(!Curl_checkheaders(conn, "Content-Type")) { 556 result = Curl_add_bufferf(&req_buffer, 557 "Content-Type: application/sdp\r\n"); 558 if(result) 559 return result; 560 } 561 } 562 563 data->state.expect100header = FALSE; /* RTSP posts are simple/small */ 564 } 565 else if(rtspreq == RTSPREQ_GET_PARAMETER) { 566 /* Check for an empty GET_PARAMETER (heartbeat) request */ 567 data->set.httpreq = HTTPREQ_HEAD; 568 data->set.opt_no_body = TRUE; 569 } 570 } 571 572 /* RTSP never allows chunked transfer */ 573 data->req.forbidchunk = TRUE; 574 /* Finish the request buffer */ 575 result = Curl_add_buffer(&req_buffer, "\r\n", 2); 576 if(result) 577 return result; 578 579 if(postsize > 0) { 580 result = Curl_add_buffer(&req_buffer, data->set.postfields, 581 (size_t)postsize); 582 if(result) 583 return result; 584 } 585 586 /* issue the request */ 587 result = Curl_add_buffer_send(&req_buffer, conn, 588 &data->info.request_size, 0, FIRSTSOCKET); 589 if(result) { 590 failf(data, "Failed sending RTSP request"); 591 return result; 592 } 593 594 Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, putsize?FIRSTSOCKET:-1); 595 596 /* Increment the CSeq on success */ 597 data->state.rtsp_next_client_CSeq++; 598 599 if(data->req.writebytecount) { 600 /* if a request-body has been sent off, we make sure this progress is 601 noted properly */ 602 Curl_pgrsSetUploadCounter(data, data->req.writebytecount); 603 if(Curl_pgrsUpdate(conn)) 604 result = CURLE_ABORTED_BY_CALLBACK; 605 } 606 607 return result; 608 } 609 610 611 static CURLcode rtsp_rtp_readwrite(struct Curl_easy *data, 612 struct connectdata *conn, 613 ssize_t *nread, 614 bool *readmore) { 615 struct SingleRequest *k = &data->req; 616 struct rtsp_conn *rtspc = &(conn->proto.rtspc); 617 618 char *rtp; /* moving pointer to rtp data */ 619 ssize_t rtp_dataleft; /* how much data left to parse in this round */ 620 char *scratch; 621 CURLcode result; 622 623 if(rtspc->rtp_buf) { 624 /* There was some leftover data the last time. Merge buffers */ 625 char *newptr = Curl_saferealloc(rtspc->rtp_buf, 626 rtspc->rtp_bufsize + *nread); 627 if(!newptr) { 628 rtspc->rtp_buf = NULL; 629 rtspc->rtp_bufsize = 0; 630 return CURLE_OUT_OF_MEMORY; 631 } 632 rtspc->rtp_buf = newptr; 633 memcpy(rtspc->rtp_buf + rtspc->rtp_bufsize, k->str, *nread); 634 rtspc->rtp_bufsize += *nread; 635 rtp = rtspc->rtp_buf; 636 rtp_dataleft = rtspc->rtp_bufsize; 637 } 638 else { 639 /* Just parse the request buffer directly */ 640 rtp = k->str; 641 rtp_dataleft = *nread; 642 } 643 644 while((rtp_dataleft > 0) && 645 (rtp[0] == '$')) { 646 if(rtp_dataleft > 4) { 647 int rtp_length; 648 649 /* Parse the header */ 650 /* The channel identifier immediately follows and is 1 byte */ 651 rtspc->rtp_channel = RTP_PKT_CHANNEL(rtp); 652 653 /* The length is two bytes */ 654 rtp_length = RTP_PKT_LENGTH(rtp); 655 656 if(rtp_dataleft < rtp_length + 4) { 657 /* Need more - incomplete payload*/ 658 *readmore = TRUE; 659 break; 660 } 661 /* We have the full RTP interleaved packet 662 * Write out the header including the leading '$' */ 663 DEBUGF(infof(data, "RTP write channel %d rtp_length %d\n", 664 rtspc->rtp_channel, rtp_length)); 665 result = rtp_client_write(conn, &rtp[0], rtp_length + 4); 666 if(result) { 667 failf(data, "Got an error writing an RTP packet"); 668 *readmore = FALSE; 669 Curl_safefree(rtspc->rtp_buf); 670 rtspc->rtp_buf = NULL; 671 rtspc->rtp_bufsize = 0; 672 return result; 673 } 674 675 /* Move forward in the buffer */ 676 rtp_dataleft -= rtp_length + 4; 677 rtp += rtp_length + 4; 678 679 if(data->set.rtspreq == RTSPREQ_RECEIVE) { 680 /* If we are in a passive receive, give control back 681 * to the app as often as we can. 682 */ 683 k->keepon &= ~KEEP_RECV; 684 } 685 } 686 else { 687 /* Need more - incomplete header */ 688 *readmore = TRUE; 689 break; 690 } 691 } 692 693 if(rtp_dataleft != 0 && rtp[0] == '$') { 694 DEBUGF(infof(data, "RTP Rewinding %zd %s\n", rtp_dataleft, 695 *readmore ? "(READMORE)" : "")); 696 697 /* Store the incomplete RTP packet for a "rewind" */ 698 scratch = malloc(rtp_dataleft); 699 if(!scratch) { 700 Curl_safefree(rtspc->rtp_buf); 701 rtspc->rtp_buf = NULL; 702 rtspc->rtp_bufsize = 0; 703 return CURLE_OUT_OF_MEMORY; 704 } 705 memcpy(scratch, rtp, rtp_dataleft); 706 Curl_safefree(rtspc->rtp_buf); 707 rtspc->rtp_buf = scratch; 708 rtspc->rtp_bufsize = rtp_dataleft; 709 710 /* As far as the transfer is concerned, this data is consumed */ 711 *nread = 0; 712 return CURLE_OK; 713 } 714 /* Fix up k->str to point just after the last RTP packet */ 715 k->str += *nread - rtp_dataleft; 716 717 /* either all of the data has been read or... 718 * rtp now points at the next byte to parse 719 */ 720 if(rtp_dataleft > 0) 721 DEBUGASSERT(k->str[0] == rtp[0]); 722 723 DEBUGASSERT(rtp_dataleft <= *nread); /* sanity check */ 724 725 *nread = rtp_dataleft; 726 727 /* If we get here, we have finished with the leftover/merge buffer */ 728 Curl_safefree(rtspc->rtp_buf); 729 rtspc->rtp_buf = NULL; 730 rtspc->rtp_bufsize = 0; 731 732 return CURLE_OK; 733 } 734 735 static 736 CURLcode rtp_client_write(struct connectdata *conn, char *ptr, size_t len) 737 { 738 struct Curl_easy *data = conn->data; 739 size_t wrote; 740 curl_write_callback writeit; 741 void *user_ptr; 742 743 if(len == 0) { 744 failf(data, "Cannot write a 0 size RTP packet."); 745 return CURLE_WRITE_ERROR; 746 } 747 748 /* If the user has configured CURLOPT_INTERLEAVEFUNCTION then use that 749 function and any configured CURLOPT_INTERLEAVEDATA to write out the RTP 750 data. Otherwise, use the CURLOPT_WRITEFUNCTION with the CURLOPT_WRITEDATA 751 pointer to write out the RTP data. */ 752 if(data->set.fwrite_rtp) { 753 writeit = data->set.fwrite_rtp; 754 user_ptr = data->set.rtp_out; 755 } 756 else { 757 writeit = data->set.fwrite_func; 758 user_ptr = data->set.out; 759 } 760 761 Curl_set_in_callback(data, true); 762 wrote = writeit(ptr, 1, len, user_ptr); 763 Curl_set_in_callback(data, false); 764 765 if(CURL_WRITEFUNC_PAUSE == wrote) { 766 failf(data, "Cannot pause RTP"); 767 return CURLE_WRITE_ERROR; 768 } 769 770 if(wrote != len) { 771 failf(data, "Failed writing RTP data"); 772 return CURLE_WRITE_ERROR; 773 } 774 775 return CURLE_OK; 776 } 777 778 CURLcode Curl_rtsp_parseheader(struct connectdata *conn, 779 char *header) 780 { 781 struct Curl_easy *data = conn->data; 782 long CSeq = 0; 783 784 if(checkprefix("CSeq:", header)) { 785 /* Store the received CSeq. Match is verified in rtsp_done */ 786 int nc = sscanf(&header[4], ": %ld", &CSeq); 787 if(nc == 1) { 788 struct RTSP *rtsp = data->req.protop; 789 rtsp->CSeq_recv = CSeq; /* mark the request */ 790 data->state.rtsp_CSeq_recv = CSeq; /* update the handle */ 791 } 792 else { 793 failf(data, "Unable to read the CSeq header: [%s]", header); 794 return CURLE_RTSP_CSEQ_ERROR; 795 } 796 } 797 else if(checkprefix("Session:", header)) { 798 char *start; 799 800 /* Find the first non-space letter */ 801 start = header + 8; 802 while(*start && ISSPACE(*start)) 803 start++; 804 805 if(!*start) { 806 failf(data, "Got a blank Session ID"); 807 } 808 else if(data->set.str[STRING_RTSP_SESSION_ID]) { 809 /* If the Session ID is set, then compare */ 810 if(strncmp(start, data->set.str[STRING_RTSP_SESSION_ID], 811 strlen(data->set.str[STRING_RTSP_SESSION_ID])) != 0) { 812 failf(data, "Got RTSP Session ID Line [%s], but wanted ID [%s]", 813 start, data->set.str[STRING_RTSP_SESSION_ID]); 814 return CURLE_RTSP_SESSION_ERROR; 815 } 816 } 817 else { 818 /* If the Session ID is not set, and we find it in a response, then set 819 * it. 820 * 821 * Allow any non whitespace content, up to the field separator or end of 822 * line. RFC 2326 isn't 100% clear on the session ID and for example 823 * gstreamer does url-encoded session ID's not covered by the standard. 824 */ 825 char *end = start; 826 while(*end && *end != ';' && !ISSPACE(*end)) 827 end++; 828 829 /* Copy the id substring into a new buffer */ 830 data->set.str[STRING_RTSP_SESSION_ID] = malloc(end - start + 1); 831 if(data->set.str[STRING_RTSP_SESSION_ID] == NULL) 832 return CURLE_OUT_OF_MEMORY; 833 memcpy(data->set.str[STRING_RTSP_SESSION_ID], start, end - start); 834 (data->set.str[STRING_RTSP_SESSION_ID])[end - start] = '\0'; 835 } 836 } 837 return CURLE_OK; 838 } 839 840 #endif /* CURL_DISABLE_RTSP */ 841