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