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