113498266Sopenharmony_ci/*************************************************************************** 213498266Sopenharmony_ci * _ _ ____ _ 313498266Sopenharmony_ci * Project ___| | | | _ \| | 413498266Sopenharmony_ci * / __| | | | |_) | | 513498266Sopenharmony_ci * | (__| |_| | _ <| |___ 613498266Sopenharmony_ci * \___|\___/|_| \_\_____| 713498266Sopenharmony_ci * 813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 913498266Sopenharmony_ci * 1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which 1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms 1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html. 1313498266Sopenharmony_ci * 1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell 1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is 1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file. 1713498266Sopenharmony_ci * 1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 1913498266Sopenharmony_ci * KIND, either express or implied. 2013498266Sopenharmony_ci * 2113498266Sopenharmony_ci * SPDX-License-Identifier: curl 2213498266Sopenharmony_ci * 2313498266Sopenharmony_ci ***************************************************************************/ 2413498266Sopenharmony_ci 2513498266Sopenharmony_ci#include "curl_setup.h" 2613498266Sopenharmony_ci 2713498266Sopenharmony_ci#if !defined(CURL_DISABLE_RTSP) && !defined(USE_HYPER) 2813498266Sopenharmony_ci 2913498266Sopenharmony_ci#include "urldata.h" 3013498266Sopenharmony_ci#include <curl/curl.h> 3113498266Sopenharmony_ci#include "transfer.h" 3213498266Sopenharmony_ci#include "sendf.h" 3313498266Sopenharmony_ci#include "multiif.h" 3413498266Sopenharmony_ci#include "http.h" 3513498266Sopenharmony_ci#include "url.h" 3613498266Sopenharmony_ci#include "progress.h" 3713498266Sopenharmony_ci#include "rtsp.h" 3813498266Sopenharmony_ci#include "strcase.h" 3913498266Sopenharmony_ci#include "select.h" 4013498266Sopenharmony_ci#include "connect.h" 4113498266Sopenharmony_ci#include "cfilters.h" 4213498266Sopenharmony_ci#include "strdup.h" 4313498266Sopenharmony_ci/* The last 3 #include files should be in this order */ 4413498266Sopenharmony_ci#include "curl_printf.h" 4513498266Sopenharmony_ci#include "curl_memory.h" 4613498266Sopenharmony_ci#include "memdebug.h" 4713498266Sopenharmony_ci 4813498266Sopenharmony_ci#define RTP_PKT_LENGTH(p) ((((unsigned int)((unsigned char)((p)[2]))) << 8) | \ 4913498266Sopenharmony_ci ((unsigned int)((unsigned char)((p)[3])))) 5013498266Sopenharmony_ci 5113498266Sopenharmony_ci/* protocol-specific functions set up to be called by the main engine */ 5213498266Sopenharmony_cistatic CURLcode rtsp_do(struct Curl_easy *data, bool *done); 5313498266Sopenharmony_cistatic CURLcode rtsp_done(struct Curl_easy *data, CURLcode, bool premature); 5413498266Sopenharmony_cistatic CURLcode rtsp_connect(struct Curl_easy *data, bool *done); 5513498266Sopenharmony_cistatic CURLcode rtsp_disconnect(struct Curl_easy *data, 5613498266Sopenharmony_ci struct connectdata *conn, bool dead); 5713498266Sopenharmony_cistatic int rtsp_getsock_do(struct Curl_easy *data, 5813498266Sopenharmony_ci struct connectdata *conn, curl_socket_t *socks); 5913498266Sopenharmony_ci 6013498266Sopenharmony_ci/* 6113498266Sopenharmony_ci * Parse and write out an RTSP response. 6213498266Sopenharmony_ci * @param data the transfer 6313498266Sopenharmony_ci * @param conn the connection 6413498266Sopenharmony_ci * @param buf data read from connection 6513498266Sopenharmony_ci * @param blen amount of data in buf 6613498266Sopenharmony_ci * @param is_eos TRUE iff this is the last write 6713498266Sopenharmony_ci * @param readmore out, TRUE iff complete buf was consumed and more data 6813498266Sopenharmony_ci * is needed 6913498266Sopenharmony_ci */ 7013498266Sopenharmony_cistatic CURLcode rtsp_rtp_write_resp(struct Curl_easy *data, 7113498266Sopenharmony_ci const char *buf, 7213498266Sopenharmony_ci size_t blen, 7313498266Sopenharmony_ci bool is_eos, 7413498266Sopenharmony_ci bool *done); 7513498266Sopenharmony_ci 7613498266Sopenharmony_cistatic CURLcode rtsp_setup_connection(struct Curl_easy *data, 7713498266Sopenharmony_ci struct connectdata *conn); 7813498266Sopenharmony_cistatic unsigned int rtsp_conncheck(struct Curl_easy *data, 7913498266Sopenharmony_ci struct connectdata *check, 8013498266Sopenharmony_ci unsigned int checks_to_perform); 8113498266Sopenharmony_ci 8213498266Sopenharmony_ci/* this returns the socket to wait for in the DO and DOING state for the multi 8313498266Sopenharmony_ci interface and then we're always _sending_ a request and thus we wait for 8413498266Sopenharmony_ci the single socket to become writable only */ 8513498266Sopenharmony_cistatic int rtsp_getsock_do(struct Curl_easy *data, struct connectdata *conn, 8613498266Sopenharmony_ci curl_socket_t *socks) 8713498266Sopenharmony_ci{ 8813498266Sopenharmony_ci /* write mode */ 8913498266Sopenharmony_ci (void)data; 9013498266Sopenharmony_ci socks[0] = conn->sock[FIRSTSOCKET]; 9113498266Sopenharmony_ci return GETSOCK_WRITESOCK(0); 9213498266Sopenharmony_ci} 9313498266Sopenharmony_ci 9413498266Sopenharmony_cistatic 9513498266Sopenharmony_ciCURLcode rtp_client_write(struct Curl_easy *data, const char *ptr, size_t len); 9613498266Sopenharmony_cistatic 9713498266Sopenharmony_ciCURLcode rtsp_parse_transport(struct Curl_easy *data, char *transport); 9813498266Sopenharmony_ci 9913498266Sopenharmony_ci 10013498266Sopenharmony_ci/* 10113498266Sopenharmony_ci * RTSP handler interface. 10213498266Sopenharmony_ci */ 10313498266Sopenharmony_ciconst struct Curl_handler Curl_handler_rtsp = { 10413498266Sopenharmony_ci "RTSP", /* scheme */ 10513498266Sopenharmony_ci rtsp_setup_connection, /* setup_connection */ 10613498266Sopenharmony_ci rtsp_do, /* do_it */ 10713498266Sopenharmony_ci rtsp_done, /* done */ 10813498266Sopenharmony_ci ZERO_NULL, /* do_more */ 10913498266Sopenharmony_ci rtsp_connect, /* connect_it */ 11013498266Sopenharmony_ci ZERO_NULL, /* connecting */ 11113498266Sopenharmony_ci ZERO_NULL, /* doing */ 11213498266Sopenharmony_ci ZERO_NULL, /* proto_getsock */ 11313498266Sopenharmony_ci rtsp_getsock_do, /* doing_getsock */ 11413498266Sopenharmony_ci ZERO_NULL, /* domore_getsock */ 11513498266Sopenharmony_ci ZERO_NULL, /* perform_getsock */ 11613498266Sopenharmony_ci rtsp_disconnect, /* disconnect */ 11713498266Sopenharmony_ci rtsp_rtp_write_resp, /* write_resp */ 11813498266Sopenharmony_ci rtsp_conncheck, /* connection_check */ 11913498266Sopenharmony_ci ZERO_NULL, /* attach connection */ 12013498266Sopenharmony_ci PORT_RTSP, /* defport */ 12113498266Sopenharmony_ci CURLPROTO_RTSP, /* protocol */ 12213498266Sopenharmony_ci CURLPROTO_RTSP, /* family */ 12313498266Sopenharmony_ci PROTOPT_NONE /* flags */ 12413498266Sopenharmony_ci}; 12513498266Sopenharmony_ci 12613498266Sopenharmony_ci#define MAX_RTP_BUFFERSIZE 1000000 /* arbitrary */ 12713498266Sopenharmony_ci 12813498266Sopenharmony_cistatic CURLcode rtsp_setup_connection(struct Curl_easy *data, 12913498266Sopenharmony_ci struct connectdata *conn) 13013498266Sopenharmony_ci{ 13113498266Sopenharmony_ci struct RTSP *rtsp; 13213498266Sopenharmony_ci (void)conn; 13313498266Sopenharmony_ci 13413498266Sopenharmony_ci data->req.p.rtsp = rtsp = calloc(1, sizeof(struct RTSP)); 13513498266Sopenharmony_ci if(!rtsp) 13613498266Sopenharmony_ci return CURLE_OUT_OF_MEMORY; 13713498266Sopenharmony_ci 13813498266Sopenharmony_ci Curl_dyn_init(&conn->proto.rtspc.buf, MAX_RTP_BUFFERSIZE); 13913498266Sopenharmony_ci return CURLE_OK; 14013498266Sopenharmony_ci} 14113498266Sopenharmony_ci 14213498266Sopenharmony_ci 14313498266Sopenharmony_ci/* 14413498266Sopenharmony_ci * Function to check on various aspects of a connection. 14513498266Sopenharmony_ci */ 14613498266Sopenharmony_cistatic unsigned int rtsp_conncheck(struct Curl_easy *data, 14713498266Sopenharmony_ci struct connectdata *conn, 14813498266Sopenharmony_ci unsigned int checks_to_perform) 14913498266Sopenharmony_ci{ 15013498266Sopenharmony_ci unsigned int ret_val = CONNRESULT_NONE; 15113498266Sopenharmony_ci (void)data; 15213498266Sopenharmony_ci 15313498266Sopenharmony_ci if(checks_to_perform & CONNCHECK_ISDEAD) { 15413498266Sopenharmony_ci bool input_pending; 15513498266Sopenharmony_ci if(!Curl_conn_is_alive(data, conn, &input_pending)) 15613498266Sopenharmony_ci ret_val |= CONNRESULT_DEAD; 15713498266Sopenharmony_ci } 15813498266Sopenharmony_ci 15913498266Sopenharmony_ci return ret_val; 16013498266Sopenharmony_ci} 16113498266Sopenharmony_ci 16213498266Sopenharmony_ci 16313498266Sopenharmony_cistatic CURLcode rtsp_connect(struct Curl_easy *data, bool *done) 16413498266Sopenharmony_ci{ 16513498266Sopenharmony_ci CURLcode httpStatus; 16613498266Sopenharmony_ci 16713498266Sopenharmony_ci httpStatus = Curl_http_connect(data, done); 16813498266Sopenharmony_ci 16913498266Sopenharmony_ci /* Initialize the CSeq if not already done */ 17013498266Sopenharmony_ci if(data->state.rtsp_next_client_CSeq == 0) 17113498266Sopenharmony_ci data->state.rtsp_next_client_CSeq = 1; 17213498266Sopenharmony_ci if(data->state.rtsp_next_server_CSeq == 0) 17313498266Sopenharmony_ci data->state.rtsp_next_server_CSeq = 1; 17413498266Sopenharmony_ci 17513498266Sopenharmony_ci data->conn->proto.rtspc.rtp_channel = -1; 17613498266Sopenharmony_ci 17713498266Sopenharmony_ci return httpStatus; 17813498266Sopenharmony_ci} 17913498266Sopenharmony_ci 18013498266Sopenharmony_cistatic CURLcode rtsp_disconnect(struct Curl_easy *data, 18113498266Sopenharmony_ci struct connectdata *conn, bool dead) 18213498266Sopenharmony_ci{ 18313498266Sopenharmony_ci (void) dead; 18413498266Sopenharmony_ci (void) data; 18513498266Sopenharmony_ci Curl_dyn_free(&conn->proto.rtspc.buf); 18613498266Sopenharmony_ci return CURLE_OK; 18713498266Sopenharmony_ci} 18813498266Sopenharmony_ci 18913498266Sopenharmony_ci 19013498266Sopenharmony_cistatic CURLcode rtsp_done(struct Curl_easy *data, 19113498266Sopenharmony_ci CURLcode status, bool premature) 19213498266Sopenharmony_ci{ 19313498266Sopenharmony_ci struct RTSP *rtsp = data->req.p.rtsp; 19413498266Sopenharmony_ci CURLcode httpStatus; 19513498266Sopenharmony_ci 19613498266Sopenharmony_ci /* Bypass HTTP empty-reply checks on receive */ 19713498266Sopenharmony_ci if(data->set.rtspreq == RTSPREQ_RECEIVE) 19813498266Sopenharmony_ci premature = TRUE; 19913498266Sopenharmony_ci 20013498266Sopenharmony_ci httpStatus = Curl_http_done(data, status, premature); 20113498266Sopenharmony_ci 20213498266Sopenharmony_ci if(rtsp && !status && !httpStatus) { 20313498266Sopenharmony_ci /* Check the sequence numbers */ 20413498266Sopenharmony_ci long CSeq_sent = rtsp->CSeq_sent; 20513498266Sopenharmony_ci long CSeq_recv = rtsp->CSeq_recv; 20613498266Sopenharmony_ci if((data->set.rtspreq != RTSPREQ_RECEIVE) && (CSeq_sent != CSeq_recv)) { 20713498266Sopenharmony_ci failf(data, 20813498266Sopenharmony_ci "The CSeq of this request %ld did not match the response %ld", 20913498266Sopenharmony_ci CSeq_sent, CSeq_recv); 21013498266Sopenharmony_ci return CURLE_RTSP_CSEQ_ERROR; 21113498266Sopenharmony_ci } 21213498266Sopenharmony_ci if(data->set.rtspreq == RTSPREQ_RECEIVE && 21313498266Sopenharmony_ci (data->conn->proto.rtspc.rtp_channel == -1)) { 21413498266Sopenharmony_ci infof(data, "Got an RTP Receive with a CSeq of %ld", CSeq_recv); 21513498266Sopenharmony_ci } 21613498266Sopenharmony_ci } 21713498266Sopenharmony_ci 21813498266Sopenharmony_ci return httpStatus; 21913498266Sopenharmony_ci} 22013498266Sopenharmony_ci 22113498266Sopenharmony_cistatic CURLcode rtsp_do(struct Curl_easy *data, bool *done) 22213498266Sopenharmony_ci{ 22313498266Sopenharmony_ci struct connectdata *conn = data->conn; 22413498266Sopenharmony_ci CURLcode result = CURLE_OK; 22513498266Sopenharmony_ci Curl_RtspReq rtspreq = data->set.rtspreq; 22613498266Sopenharmony_ci struct RTSP *rtsp = data->req.p.rtsp; 22713498266Sopenharmony_ci struct dynbuf req_buffer; 22813498266Sopenharmony_ci curl_off_t postsize = 0; /* for ANNOUNCE and SET_PARAMETER */ 22913498266Sopenharmony_ci curl_off_t putsize = 0; /* for ANNOUNCE and SET_PARAMETER */ 23013498266Sopenharmony_ci 23113498266Sopenharmony_ci const char *p_request = NULL; 23213498266Sopenharmony_ci const char *p_session_id = NULL; 23313498266Sopenharmony_ci const char *p_accept = NULL; 23413498266Sopenharmony_ci const char *p_accept_encoding = NULL; 23513498266Sopenharmony_ci const char *p_range = NULL; 23613498266Sopenharmony_ci const char *p_referrer = NULL; 23713498266Sopenharmony_ci const char *p_stream_uri = NULL; 23813498266Sopenharmony_ci const char *p_transport = NULL; 23913498266Sopenharmony_ci const char *p_uagent = NULL; 24013498266Sopenharmony_ci const char *p_proxyuserpwd = NULL; 24113498266Sopenharmony_ci const char *p_userpwd = NULL; 24213498266Sopenharmony_ci 24313498266Sopenharmony_ci *done = TRUE; 24413498266Sopenharmony_ci 24513498266Sopenharmony_ci rtsp->CSeq_sent = data->state.rtsp_next_client_CSeq; 24613498266Sopenharmony_ci rtsp->CSeq_recv = 0; 24713498266Sopenharmony_ci 24813498266Sopenharmony_ci /* Setup the first_* fields to allow auth details get sent 24913498266Sopenharmony_ci to this origin */ 25013498266Sopenharmony_ci 25113498266Sopenharmony_ci if(!data->state.first_host) { 25213498266Sopenharmony_ci data->state.first_host = strdup(conn->host.name); 25313498266Sopenharmony_ci if(!data->state.first_host) 25413498266Sopenharmony_ci return CURLE_OUT_OF_MEMORY; 25513498266Sopenharmony_ci 25613498266Sopenharmony_ci data->state.first_remote_port = conn->remote_port; 25713498266Sopenharmony_ci data->state.first_remote_protocol = conn->handler->protocol; 25813498266Sopenharmony_ci } 25913498266Sopenharmony_ci 26013498266Sopenharmony_ci /* Setup the 'p_request' pointer to the proper p_request string 26113498266Sopenharmony_ci * Since all RTSP requests are included here, there is no need to 26213498266Sopenharmony_ci * support custom requests like HTTP. 26313498266Sopenharmony_ci **/ 26413498266Sopenharmony_ci data->req.no_body = TRUE; /* most requests don't contain a body */ 26513498266Sopenharmony_ci switch(rtspreq) { 26613498266Sopenharmony_ci default: 26713498266Sopenharmony_ci failf(data, "Got invalid RTSP request"); 26813498266Sopenharmony_ci return CURLE_BAD_FUNCTION_ARGUMENT; 26913498266Sopenharmony_ci case RTSPREQ_OPTIONS: 27013498266Sopenharmony_ci p_request = "OPTIONS"; 27113498266Sopenharmony_ci break; 27213498266Sopenharmony_ci case RTSPREQ_DESCRIBE: 27313498266Sopenharmony_ci p_request = "DESCRIBE"; 27413498266Sopenharmony_ci data->req.no_body = FALSE; 27513498266Sopenharmony_ci break; 27613498266Sopenharmony_ci case RTSPREQ_ANNOUNCE: 27713498266Sopenharmony_ci p_request = "ANNOUNCE"; 27813498266Sopenharmony_ci break; 27913498266Sopenharmony_ci case RTSPREQ_SETUP: 28013498266Sopenharmony_ci p_request = "SETUP"; 28113498266Sopenharmony_ci break; 28213498266Sopenharmony_ci case RTSPREQ_PLAY: 28313498266Sopenharmony_ci p_request = "PLAY"; 28413498266Sopenharmony_ci break; 28513498266Sopenharmony_ci case RTSPREQ_PAUSE: 28613498266Sopenharmony_ci p_request = "PAUSE"; 28713498266Sopenharmony_ci break; 28813498266Sopenharmony_ci case RTSPREQ_TEARDOWN: 28913498266Sopenharmony_ci p_request = "TEARDOWN"; 29013498266Sopenharmony_ci break; 29113498266Sopenharmony_ci case RTSPREQ_GET_PARAMETER: 29213498266Sopenharmony_ci /* GET_PARAMETER's no_body status is determined later */ 29313498266Sopenharmony_ci p_request = "GET_PARAMETER"; 29413498266Sopenharmony_ci data->req.no_body = FALSE; 29513498266Sopenharmony_ci break; 29613498266Sopenharmony_ci case RTSPREQ_SET_PARAMETER: 29713498266Sopenharmony_ci p_request = "SET_PARAMETER"; 29813498266Sopenharmony_ci break; 29913498266Sopenharmony_ci case RTSPREQ_RECORD: 30013498266Sopenharmony_ci p_request = "RECORD"; 30113498266Sopenharmony_ci break; 30213498266Sopenharmony_ci case RTSPREQ_RECEIVE: 30313498266Sopenharmony_ci p_request = ""; 30413498266Sopenharmony_ci /* Treat interleaved RTP as body */ 30513498266Sopenharmony_ci data->req.no_body = FALSE; 30613498266Sopenharmony_ci break; 30713498266Sopenharmony_ci case RTSPREQ_LAST: 30813498266Sopenharmony_ci failf(data, "Got invalid RTSP request: RTSPREQ_LAST"); 30913498266Sopenharmony_ci return CURLE_BAD_FUNCTION_ARGUMENT; 31013498266Sopenharmony_ci } 31113498266Sopenharmony_ci 31213498266Sopenharmony_ci if(rtspreq == RTSPREQ_RECEIVE) { 31313498266Sopenharmony_ci Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, -1); 31413498266Sopenharmony_ci 31513498266Sopenharmony_ci return result; 31613498266Sopenharmony_ci } 31713498266Sopenharmony_ci 31813498266Sopenharmony_ci p_session_id = data->set.str[STRING_RTSP_SESSION_ID]; 31913498266Sopenharmony_ci if(!p_session_id && 32013498266Sopenharmony_ci (rtspreq & ~(RTSPREQ_OPTIONS | RTSPREQ_DESCRIBE | RTSPREQ_SETUP))) { 32113498266Sopenharmony_ci failf(data, "Refusing to issue an RTSP request [%s] without a session ID.", 32213498266Sopenharmony_ci p_request); 32313498266Sopenharmony_ci return CURLE_BAD_FUNCTION_ARGUMENT; 32413498266Sopenharmony_ci } 32513498266Sopenharmony_ci 32613498266Sopenharmony_ci /* Stream URI. Default to server '*' if not specified */ 32713498266Sopenharmony_ci if(data->set.str[STRING_RTSP_STREAM_URI]) { 32813498266Sopenharmony_ci p_stream_uri = data->set.str[STRING_RTSP_STREAM_URI]; 32913498266Sopenharmony_ci } 33013498266Sopenharmony_ci else { 33113498266Sopenharmony_ci p_stream_uri = "*"; 33213498266Sopenharmony_ci } 33313498266Sopenharmony_ci 33413498266Sopenharmony_ci /* Transport Header for SETUP requests */ 33513498266Sopenharmony_ci p_transport = Curl_checkheaders(data, STRCONST("Transport")); 33613498266Sopenharmony_ci if(rtspreq == RTSPREQ_SETUP && !p_transport) { 33713498266Sopenharmony_ci /* New Transport: setting? */ 33813498266Sopenharmony_ci if(data->set.str[STRING_RTSP_TRANSPORT]) { 33913498266Sopenharmony_ci Curl_safefree(data->state.aptr.rtsp_transport); 34013498266Sopenharmony_ci 34113498266Sopenharmony_ci data->state.aptr.rtsp_transport = 34213498266Sopenharmony_ci aprintf("Transport: %s\r\n", 34313498266Sopenharmony_ci data->set.str[STRING_RTSP_TRANSPORT]); 34413498266Sopenharmony_ci if(!data->state.aptr.rtsp_transport) 34513498266Sopenharmony_ci return CURLE_OUT_OF_MEMORY; 34613498266Sopenharmony_ci } 34713498266Sopenharmony_ci else { 34813498266Sopenharmony_ci failf(data, 34913498266Sopenharmony_ci "Refusing to issue an RTSP SETUP without a Transport: header."); 35013498266Sopenharmony_ci return CURLE_BAD_FUNCTION_ARGUMENT; 35113498266Sopenharmony_ci } 35213498266Sopenharmony_ci 35313498266Sopenharmony_ci p_transport = data->state.aptr.rtsp_transport; 35413498266Sopenharmony_ci } 35513498266Sopenharmony_ci 35613498266Sopenharmony_ci /* Accept Headers for DESCRIBE requests */ 35713498266Sopenharmony_ci if(rtspreq == RTSPREQ_DESCRIBE) { 35813498266Sopenharmony_ci /* Accept Header */ 35913498266Sopenharmony_ci p_accept = Curl_checkheaders(data, STRCONST("Accept"))? 36013498266Sopenharmony_ci NULL:"Accept: application/sdp\r\n"; 36113498266Sopenharmony_ci 36213498266Sopenharmony_ci /* Accept-Encoding header */ 36313498266Sopenharmony_ci if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) && 36413498266Sopenharmony_ci data->set.str[STRING_ENCODING]) { 36513498266Sopenharmony_ci Curl_safefree(data->state.aptr.accept_encoding); 36613498266Sopenharmony_ci data->state.aptr.accept_encoding = 36713498266Sopenharmony_ci aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]); 36813498266Sopenharmony_ci 36913498266Sopenharmony_ci if(!data->state.aptr.accept_encoding) 37013498266Sopenharmony_ci return CURLE_OUT_OF_MEMORY; 37113498266Sopenharmony_ci 37213498266Sopenharmony_ci p_accept_encoding = data->state.aptr.accept_encoding; 37313498266Sopenharmony_ci } 37413498266Sopenharmony_ci } 37513498266Sopenharmony_ci 37613498266Sopenharmony_ci /* The User-Agent string might have been allocated in url.c already, because 37713498266Sopenharmony_ci it might have been used in the proxy connect, but if we have got a header 37813498266Sopenharmony_ci with the user-agent string specified, we erase the previously made string 37913498266Sopenharmony_ci here. */ 38013498266Sopenharmony_ci if(Curl_checkheaders(data, STRCONST("User-Agent")) && 38113498266Sopenharmony_ci data->state.aptr.uagent) { 38213498266Sopenharmony_ci Curl_safefree(data->state.aptr.uagent); 38313498266Sopenharmony_ci } 38413498266Sopenharmony_ci else if(!Curl_checkheaders(data, STRCONST("User-Agent")) && 38513498266Sopenharmony_ci data->set.str[STRING_USERAGENT]) { 38613498266Sopenharmony_ci p_uagent = data->state.aptr.uagent; 38713498266Sopenharmony_ci } 38813498266Sopenharmony_ci 38913498266Sopenharmony_ci /* setup the authentication headers */ 39013498266Sopenharmony_ci result = Curl_http_output_auth(data, conn, p_request, HTTPREQ_GET, 39113498266Sopenharmony_ci p_stream_uri, FALSE); 39213498266Sopenharmony_ci if(result) 39313498266Sopenharmony_ci return result; 39413498266Sopenharmony_ci 39513498266Sopenharmony_ci p_proxyuserpwd = data->state.aptr.proxyuserpwd; 39613498266Sopenharmony_ci p_userpwd = data->state.aptr.userpwd; 39713498266Sopenharmony_ci 39813498266Sopenharmony_ci /* Referrer */ 39913498266Sopenharmony_ci Curl_safefree(data->state.aptr.ref); 40013498266Sopenharmony_ci if(data->state.referer && !Curl_checkheaders(data, STRCONST("Referer"))) 40113498266Sopenharmony_ci data->state.aptr.ref = aprintf("Referer: %s\r\n", data->state.referer); 40213498266Sopenharmony_ci 40313498266Sopenharmony_ci p_referrer = data->state.aptr.ref; 40413498266Sopenharmony_ci 40513498266Sopenharmony_ci /* 40613498266Sopenharmony_ci * Range Header 40713498266Sopenharmony_ci * Only applies to PLAY, PAUSE, RECORD 40813498266Sopenharmony_ci * 40913498266Sopenharmony_ci * Go ahead and use the Range stuff supplied for HTTP 41013498266Sopenharmony_ci */ 41113498266Sopenharmony_ci if(data->state.use_range && 41213498266Sopenharmony_ci (rtspreq & (RTSPREQ_PLAY | RTSPREQ_PAUSE | RTSPREQ_RECORD))) { 41313498266Sopenharmony_ci 41413498266Sopenharmony_ci /* Check to see if there is a range set in the custom headers */ 41513498266Sopenharmony_ci if(!Curl_checkheaders(data, STRCONST("Range")) && data->state.range) { 41613498266Sopenharmony_ci Curl_safefree(data->state.aptr.rangeline); 41713498266Sopenharmony_ci data->state.aptr.rangeline = aprintf("Range: %s\r\n", data->state.range); 41813498266Sopenharmony_ci p_range = data->state.aptr.rangeline; 41913498266Sopenharmony_ci } 42013498266Sopenharmony_ci } 42113498266Sopenharmony_ci 42213498266Sopenharmony_ci /* 42313498266Sopenharmony_ci * Sanity check the custom headers 42413498266Sopenharmony_ci */ 42513498266Sopenharmony_ci if(Curl_checkheaders(data, STRCONST("CSeq"))) { 42613498266Sopenharmony_ci failf(data, "CSeq cannot be set as a custom header."); 42713498266Sopenharmony_ci return CURLE_RTSP_CSEQ_ERROR; 42813498266Sopenharmony_ci } 42913498266Sopenharmony_ci if(Curl_checkheaders(data, STRCONST("Session"))) { 43013498266Sopenharmony_ci failf(data, "Session ID cannot be set as a custom header."); 43113498266Sopenharmony_ci return CURLE_BAD_FUNCTION_ARGUMENT; 43213498266Sopenharmony_ci } 43313498266Sopenharmony_ci 43413498266Sopenharmony_ci /* Initialize a dynamic send buffer */ 43513498266Sopenharmony_ci Curl_dyn_init(&req_buffer, DYN_RTSP_REQ_HEADER); 43613498266Sopenharmony_ci 43713498266Sopenharmony_ci result = 43813498266Sopenharmony_ci Curl_dyn_addf(&req_buffer, 43913498266Sopenharmony_ci "%s %s RTSP/1.0\r\n" /* Request Stream-URI RTSP/1.0 */ 44013498266Sopenharmony_ci "CSeq: %ld\r\n", /* CSeq */ 44113498266Sopenharmony_ci p_request, p_stream_uri, rtsp->CSeq_sent); 44213498266Sopenharmony_ci if(result) 44313498266Sopenharmony_ci return result; 44413498266Sopenharmony_ci 44513498266Sopenharmony_ci /* 44613498266Sopenharmony_ci * Rather than do a normal alloc line, keep the session_id unformatted 44713498266Sopenharmony_ci * to make comparison easier 44813498266Sopenharmony_ci */ 44913498266Sopenharmony_ci if(p_session_id) { 45013498266Sopenharmony_ci result = Curl_dyn_addf(&req_buffer, "Session: %s\r\n", p_session_id); 45113498266Sopenharmony_ci if(result) 45213498266Sopenharmony_ci return result; 45313498266Sopenharmony_ci } 45413498266Sopenharmony_ci 45513498266Sopenharmony_ci /* 45613498266Sopenharmony_ci * Shared HTTP-like options 45713498266Sopenharmony_ci */ 45813498266Sopenharmony_ci result = Curl_dyn_addf(&req_buffer, 45913498266Sopenharmony_ci "%s" /* transport */ 46013498266Sopenharmony_ci "%s" /* accept */ 46113498266Sopenharmony_ci "%s" /* accept-encoding */ 46213498266Sopenharmony_ci "%s" /* range */ 46313498266Sopenharmony_ci "%s" /* referrer */ 46413498266Sopenharmony_ci "%s" /* user-agent */ 46513498266Sopenharmony_ci "%s" /* proxyuserpwd */ 46613498266Sopenharmony_ci "%s" /* userpwd */ 46713498266Sopenharmony_ci , 46813498266Sopenharmony_ci p_transport ? p_transport : "", 46913498266Sopenharmony_ci p_accept ? p_accept : "", 47013498266Sopenharmony_ci p_accept_encoding ? p_accept_encoding : "", 47113498266Sopenharmony_ci p_range ? p_range : "", 47213498266Sopenharmony_ci p_referrer ? p_referrer : "", 47313498266Sopenharmony_ci p_uagent ? p_uagent : "", 47413498266Sopenharmony_ci p_proxyuserpwd ? p_proxyuserpwd : "", 47513498266Sopenharmony_ci p_userpwd ? p_userpwd : ""); 47613498266Sopenharmony_ci 47713498266Sopenharmony_ci /* 47813498266Sopenharmony_ci * Free userpwd now --- cannot reuse this for Negotiate and possibly NTLM 47913498266Sopenharmony_ci * with basic and digest, it will be freed anyway by the next request 48013498266Sopenharmony_ci */ 48113498266Sopenharmony_ci Curl_safefree(data->state.aptr.userpwd); 48213498266Sopenharmony_ci 48313498266Sopenharmony_ci if(result) 48413498266Sopenharmony_ci return result; 48513498266Sopenharmony_ci 48613498266Sopenharmony_ci if((rtspreq == RTSPREQ_SETUP) || (rtspreq == RTSPREQ_DESCRIBE)) { 48713498266Sopenharmony_ci result = Curl_add_timecondition(data, &req_buffer); 48813498266Sopenharmony_ci if(result) 48913498266Sopenharmony_ci return result; 49013498266Sopenharmony_ci } 49113498266Sopenharmony_ci 49213498266Sopenharmony_ci result = Curl_add_custom_headers(data, FALSE, &req_buffer); 49313498266Sopenharmony_ci if(result) 49413498266Sopenharmony_ci return result; 49513498266Sopenharmony_ci 49613498266Sopenharmony_ci if(rtspreq == RTSPREQ_ANNOUNCE || 49713498266Sopenharmony_ci rtspreq == RTSPREQ_SET_PARAMETER || 49813498266Sopenharmony_ci rtspreq == RTSPREQ_GET_PARAMETER) { 49913498266Sopenharmony_ci 50013498266Sopenharmony_ci if(data->state.upload) { 50113498266Sopenharmony_ci putsize = data->state.infilesize; 50213498266Sopenharmony_ci data->state.httpreq = HTTPREQ_PUT; 50313498266Sopenharmony_ci 50413498266Sopenharmony_ci } 50513498266Sopenharmony_ci else { 50613498266Sopenharmony_ci postsize = (data->state.infilesize != -1)? 50713498266Sopenharmony_ci data->state.infilesize: 50813498266Sopenharmony_ci (data->set.postfields? (curl_off_t)strlen(data->set.postfields):0); 50913498266Sopenharmony_ci data->state.httpreq = HTTPREQ_POST; 51013498266Sopenharmony_ci } 51113498266Sopenharmony_ci 51213498266Sopenharmony_ci if(putsize > 0 || postsize > 0) { 51313498266Sopenharmony_ci /* As stated in the http comments, it is probably not wise to 51413498266Sopenharmony_ci * actually set a custom Content-Length in the headers */ 51513498266Sopenharmony_ci if(!Curl_checkheaders(data, STRCONST("Content-Length"))) { 51613498266Sopenharmony_ci result = 51713498266Sopenharmony_ci Curl_dyn_addf(&req_buffer, 51813498266Sopenharmony_ci "Content-Length: %" CURL_FORMAT_CURL_OFF_T"\r\n", 51913498266Sopenharmony_ci (data->state.upload ? putsize : postsize)); 52013498266Sopenharmony_ci if(result) 52113498266Sopenharmony_ci return result; 52213498266Sopenharmony_ci } 52313498266Sopenharmony_ci 52413498266Sopenharmony_ci if(rtspreq == RTSPREQ_SET_PARAMETER || 52513498266Sopenharmony_ci rtspreq == RTSPREQ_GET_PARAMETER) { 52613498266Sopenharmony_ci if(!Curl_checkheaders(data, STRCONST("Content-Type"))) { 52713498266Sopenharmony_ci result = Curl_dyn_addn(&req_buffer, 52813498266Sopenharmony_ci STRCONST("Content-Type: " 52913498266Sopenharmony_ci "text/parameters\r\n")); 53013498266Sopenharmony_ci if(result) 53113498266Sopenharmony_ci return result; 53213498266Sopenharmony_ci } 53313498266Sopenharmony_ci } 53413498266Sopenharmony_ci 53513498266Sopenharmony_ci if(rtspreq == RTSPREQ_ANNOUNCE) { 53613498266Sopenharmony_ci if(!Curl_checkheaders(data, STRCONST("Content-Type"))) { 53713498266Sopenharmony_ci result = Curl_dyn_addn(&req_buffer, 53813498266Sopenharmony_ci STRCONST("Content-Type: " 53913498266Sopenharmony_ci "application/sdp\r\n")); 54013498266Sopenharmony_ci if(result) 54113498266Sopenharmony_ci return result; 54213498266Sopenharmony_ci } 54313498266Sopenharmony_ci } 54413498266Sopenharmony_ci 54513498266Sopenharmony_ci data->state.expect100header = FALSE; /* RTSP posts are simple/small */ 54613498266Sopenharmony_ci } 54713498266Sopenharmony_ci else if(rtspreq == RTSPREQ_GET_PARAMETER) { 54813498266Sopenharmony_ci /* Check for an empty GET_PARAMETER (heartbeat) request */ 54913498266Sopenharmony_ci data->state.httpreq = HTTPREQ_HEAD; 55013498266Sopenharmony_ci data->req.no_body = TRUE; 55113498266Sopenharmony_ci } 55213498266Sopenharmony_ci } 55313498266Sopenharmony_ci 55413498266Sopenharmony_ci /* RTSP never allows chunked transfer */ 55513498266Sopenharmony_ci data->req.forbidchunk = TRUE; 55613498266Sopenharmony_ci /* Finish the request buffer */ 55713498266Sopenharmony_ci result = Curl_dyn_addn(&req_buffer, STRCONST("\r\n")); 55813498266Sopenharmony_ci if(result) 55913498266Sopenharmony_ci return result; 56013498266Sopenharmony_ci 56113498266Sopenharmony_ci if(postsize > 0) { 56213498266Sopenharmony_ci result = Curl_dyn_addn(&req_buffer, data->set.postfields, 56313498266Sopenharmony_ci (size_t)postsize); 56413498266Sopenharmony_ci if(result) 56513498266Sopenharmony_ci return result; 56613498266Sopenharmony_ci } 56713498266Sopenharmony_ci 56813498266Sopenharmony_ci /* issue the request */ 56913498266Sopenharmony_ci result = Curl_buffer_send(&req_buffer, data, data->req.p.http, 57013498266Sopenharmony_ci &data->info.request_size, 0, FIRSTSOCKET); 57113498266Sopenharmony_ci if(result) { 57213498266Sopenharmony_ci failf(data, "Failed sending RTSP request"); 57313498266Sopenharmony_ci return result; 57413498266Sopenharmony_ci } 57513498266Sopenharmony_ci 57613498266Sopenharmony_ci Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, putsize?FIRSTSOCKET:-1); 57713498266Sopenharmony_ci 57813498266Sopenharmony_ci /* Increment the CSeq on success */ 57913498266Sopenharmony_ci data->state.rtsp_next_client_CSeq++; 58013498266Sopenharmony_ci 58113498266Sopenharmony_ci if(data->req.writebytecount) { 58213498266Sopenharmony_ci /* if a request-body has been sent off, we make sure this progress is 58313498266Sopenharmony_ci noted properly */ 58413498266Sopenharmony_ci Curl_pgrsSetUploadCounter(data, data->req.writebytecount); 58513498266Sopenharmony_ci if(Curl_pgrsUpdate(data)) 58613498266Sopenharmony_ci result = CURLE_ABORTED_BY_CALLBACK; 58713498266Sopenharmony_ci } 58813498266Sopenharmony_ci 58913498266Sopenharmony_ci return result; 59013498266Sopenharmony_ci} 59113498266Sopenharmony_ci 59213498266Sopenharmony_ci/** 59313498266Sopenharmony_ci * write any BODY bytes missing to the client, ignore the rest. 59413498266Sopenharmony_ci */ 59513498266Sopenharmony_cistatic CURLcode rtp_write_body_junk(struct Curl_easy *data, 59613498266Sopenharmony_ci const char *buf, 59713498266Sopenharmony_ci size_t blen) 59813498266Sopenharmony_ci{ 59913498266Sopenharmony_ci struct rtsp_conn *rtspc = &(data->conn->proto.rtspc); 60013498266Sopenharmony_ci curl_off_t body_remain; 60113498266Sopenharmony_ci bool in_body; 60213498266Sopenharmony_ci 60313498266Sopenharmony_ci in_body = (data->req.headerline && !rtspc->in_header) && 60413498266Sopenharmony_ci (data->req.size >= 0) && 60513498266Sopenharmony_ci (data->req.bytecount < data->req.size); 60613498266Sopenharmony_ci body_remain = in_body? (data->req.size - data->req.bytecount) : 0; 60713498266Sopenharmony_ci DEBUGASSERT(body_remain >= 0); 60813498266Sopenharmony_ci if(body_remain) { 60913498266Sopenharmony_ci if((curl_off_t)blen > body_remain) 61013498266Sopenharmony_ci blen = (size_t)body_remain; 61113498266Sopenharmony_ci return Curl_client_write(data, CLIENTWRITE_BODY, (char *)buf, blen); 61213498266Sopenharmony_ci } 61313498266Sopenharmony_ci return CURLE_OK; 61413498266Sopenharmony_ci} 61513498266Sopenharmony_ci 61613498266Sopenharmony_cistatic CURLcode rtsp_filter_rtp(struct Curl_easy *data, 61713498266Sopenharmony_ci const char *buf, 61813498266Sopenharmony_ci size_t blen, 61913498266Sopenharmony_ci size_t *pconsumed) 62013498266Sopenharmony_ci{ 62113498266Sopenharmony_ci struct rtsp_conn *rtspc = &(data->conn->proto.rtspc); 62213498266Sopenharmony_ci CURLcode result = CURLE_OK; 62313498266Sopenharmony_ci size_t skip_len = 0; 62413498266Sopenharmony_ci 62513498266Sopenharmony_ci *pconsumed = 0; 62613498266Sopenharmony_ci while(blen) { 62713498266Sopenharmony_ci bool in_body = (data->req.headerline && !rtspc->in_header) && 62813498266Sopenharmony_ci (data->req.size >= 0) && 62913498266Sopenharmony_ci (data->req.bytecount < data->req.size); 63013498266Sopenharmony_ci switch(rtspc->state) { 63113498266Sopenharmony_ci 63213498266Sopenharmony_ci case RTP_PARSE_SKIP: { 63313498266Sopenharmony_ci DEBUGASSERT(Curl_dyn_len(&rtspc->buf) == 0); 63413498266Sopenharmony_ci while(blen && buf[0] != '$') { 63513498266Sopenharmony_ci if(!in_body && buf[0] == 'R' && 63613498266Sopenharmony_ci data->set.rtspreq != RTSPREQ_RECEIVE) { 63713498266Sopenharmony_ci if(strncmp(buf, "RTSP/", (blen < 5) ? blen : 5) == 0) { 63813498266Sopenharmony_ci /* This could be the next response, no consume and return */ 63913498266Sopenharmony_ci if(*pconsumed) { 64013498266Sopenharmony_ci DEBUGF(infof(data, "RTP rtsp_filter_rtp[SKIP] RTSP/ prefix, " 64113498266Sopenharmony_ci "skipping %zd bytes of junk", *pconsumed)); 64213498266Sopenharmony_ci } 64313498266Sopenharmony_ci rtspc->state = RTP_PARSE_SKIP; 64413498266Sopenharmony_ci rtspc->in_header = TRUE; 64513498266Sopenharmony_ci goto out; 64613498266Sopenharmony_ci } 64713498266Sopenharmony_ci } 64813498266Sopenharmony_ci /* junk/BODY, consume without buffering */ 64913498266Sopenharmony_ci *pconsumed += 1; 65013498266Sopenharmony_ci ++buf; 65113498266Sopenharmony_ci --blen; 65213498266Sopenharmony_ci ++skip_len; 65313498266Sopenharmony_ci } 65413498266Sopenharmony_ci if(blen && buf[0] == '$') { 65513498266Sopenharmony_ci /* possible start of an RTP message, buffer */ 65613498266Sopenharmony_ci if(skip_len) { 65713498266Sopenharmony_ci /* end of junk/BODY bytes, flush */ 65813498266Sopenharmony_ci result = rtp_write_body_junk(data, 65913498266Sopenharmony_ci (char *)(buf - skip_len), skip_len); 66013498266Sopenharmony_ci skip_len = 0; 66113498266Sopenharmony_ci if(result) 66213498266Sopenharmony_ci goto out; 66313498266Sopenharmony_ci } 66413498266Sopenharmony_ci if(Curl_dyn_addn(&rtspc->buf, buf, 1)) { 66513498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 66613498266Sopenharmony_ci goto out; 66713498266Sopenharmony_ci } 66813498266Sopenharmony_ci *pconsumed += 1; 66913498266Sopenharmony_ci ++buf; 67013498266Sopenharmony_ci --blen; 67113498266Sopenharmony_ci rtspc->state = RTP_PARSE_CHANNEL; 67213498266Sopenharmony_ci } 67313498266Sopenharmony_ci break; 67413498266Sopenharmony_ci } 67513498266Sopenharmony_ci 67613498266Sopenharmony_ci case RTP_PARSE_CHANNEL: { 67713498266Sopenharmony_ci int idx = ((unsigned char)buf[0]) / 8; 67813498266Sopenharmony_ci int off = ((unsigned char)buf[0]) % 8; 67913498266Sopenharmony_ci DEBUGASSERT(Curl_dyn_len(&rtspc->buf) == 1); 68013498266Sopenharmony_ci if(!(data->state.rtp_channel_mask[idx] & (1 << off))) { 68113498266Sopenharmony_ci /* invalid channel number, junk or BODY data */ 68213498266Sopenharmony_ci rtspc->state = RTP_PARSE_SKIP; 68313498266Sopenharmony_ci DEBUGASSERT(skip_len == 0); 68413498266Sopenharmony_ci /* we do not consume this byte, it is BODY data */ 68513498266Sopenharmony_ci DEBUGF(infof(data, "RTSP: invalid RTP channel %d, skipping", idx)); 68613498266Sopenharmony_ci if(*pconsumed == 0) { 68713498266Sopenharmony_ci /* We did not consume the initial '$' in our buffer, but had 68813498266Sopenharmony_ci * it from an earlier call. We cannot un-consume it and have 68913498266Sopenharmony_ci * to write it directly as BODY data */ 69013498266Sopenharmony_ci result = rtp_write_body_junk(data, Curl_dyn_ptr(&rtspc->buf), 1); 69113498266Sopenharmony_ci if(result) 69213498266Sopenharmony_ci goto out; 69313498266Sopenharmony_ci } 69413498266Sopenharmony_ci else { 69513498266Sopenharmony_ci /* count the '$' as skip and continue */ 69613498266Sopenharmony_ci skip_len = 1; 69713498266Sopenharmony_ci } 69813498266Sopenharmony_ci Curl_dyn_free(&rtspc->buf); 69913498266Sopenharmony_ci break; 70013498266Sopenharmony_ci } 70113498266Sopenharmony_ci /* a valid channel, so we expect this to be a real RTP message */ 70213498266Sopenharmony_ci rtspc->rtp_channel = (unsigned char)buf[0]; 70313498266Sopenharmony_ci if(Curl_dyn_addn(&rtspc->buf, buf, 1)) { 70413498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 70513498266Sopenharmony_ci goto out; 70613498266Sopenharmony_ci } 70713498266Sopenharmony_ci *pconsumed += 1; 70813498266Sopenharmony_ci ++buf; 70913498266Sopenharmony_ci --blen; 71013498266Sopenharmony_ci rtspc->state = RTP_PARSE_LEN; 71113498266Sopenharmony_ci break; 71213498266Sopenharmony_ci } 71313498266Sopenharmony_ci 71413498266Sopenharmony_ci case RTP_PARSE_LEN: { 71513498266Sopenharmony_ci size_t rtp_len = Curl_dyn_len(&rtspc->buf); 71613498266Sopenharmony_ci const char *rtp_buf; 71713498266Sopenharmony_ci DEBUGASSERT(rtp_len >= 2 && rtp_len < 4); 71813498266Sopenharmony_ci if(Curl_dyn_addn(&rtspc->buf, buf, 1)) { 71913498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 72013498266Sopenharmony_ci goto out; 72113498266Sopenharmony_ci } 72213498266Sopenharmony_ci *pconsumed += 1; 72313498266Sopenharmony_ci ++buf; 72413498266Sopenharmony_ci --blen; 72513498266Sopenharmony_ci if(rtp_len == 2) 72613498266Sopenharmony_ci break; 72713498266Sopenharmony_ci rtp_buf = Curl_dyn_ptr(&rtspc->buf); 72813498266Sopenharmony_ci rtspc->rtp_len = RTP_PKT_LENGTH(rtp_buf) + 4; 72913498266Sopenharmony_ci rtspc->state = RTP_PARSE_DATA; 73013498266Sopenharmony_ci break; 73113498266Sopenharmony_ci } 73213498266Sopenharmony_ci 73313498266Sopenharmony_ci case RTP_PARSE_DATA: { 73413498266Sopenharmony_ci size_t rtp_len = Curl_dyn_len(&rtspc->buf); 73513498266Sopenharmony_ci size_t needed; 73613498266Sopenharmony_ci DEBUGASSERT(rtp_len < rtspc->rtp_len); 73713498266Sopenharmony_ci needed = rtspc->rtp_len - rtp_len; 73813498266Sopenharmony_ci if(needed <= blen) { 73913498266Sopenharmony_ci if(Curl_dyn_addn(&rtspc->buf, buf, needed)) { 74013498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 74113498266Sopenharmony_ci goto out; 74213498266Sopenharmony_ci } 74313498266Sopenharmony_ci *pconsumed += needed; 74413498266Sopenharmony_ci buf += needed; 74513498266Sopenharmony_ci blen -= needed; 74613498266Sopenharmony_ci /* complete RTP message in buffer */ 74713498266Sopenharmony_ci DEBUGF(infof(data, "RTP write channel %d rtp_len %zu", 74813498266Sopenharmony_ci rtspc->rtp_channel, rtspc->rtp_len)); 74913498266Sopenharmony_ci result = rtp_client_write(data, Curl_dyn_ptr(&rtspc->buf), 75013498266Sopenharmony_ci rtspc->rtp_len); 75113498266Sopenharmony_ci Curl_dyn_free(&rtspc->buf); 75213498266Sopenharmony_ci rtspc->state = RTP_PARSE_SKIP; 75313498266Sopenharmony_ci if(result) 75413498266Sopenharmony_ci goto out; 75513498266Sopenharmony_ci } 75613498266Sopenharmony_ci else { 75713498266Sopenharmony_ci if(Curl_dyn_addn(&rtspc->buf, buf, blen)) { 75813498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 75913498266Sopenharmony_ci goto out; 76013498266Sopenharmony_ci } 76113498266Sopenharmony_ci *pconsumed += blen; 76213498266Sopenharmony_ci buf += blen; 76313498266Sopenharmony_ci blen = 0; 76413498266Sopenharmony_ci } 76513498266Sopenharmony_ci break; 76613498266Sopenharmony_ci } 76713498266Sopenharmony_ci 76813498266Sopenharmony_ci default: 76913498266Sopenharmony_ci DEBUGASSERT(0); 77013498266Sopenharmony_ci return CURLE_RECV_ERROR; 77113498266Sopenharmony_ci } 77213498266Sopenharmony_ci } 77313498266Sopenharmony_ciout: 77413498266Sopenharmony_ci if(!result && skip_len) 77513498266Sopenharmony_ci result = rtp_write_body_junk(data, (char *)(buf - skip_len), skip_len); 77613498266Sopenharmony_ci return result; 77713498266Sopenharmony_ci} 77813498266Sopenharmony_ci 77913498266Sopenharmony_cistatic CURLcode rtsp_rtp_write_resp(struct Curl_easy *data, 78013498266Sopenharmony_ci const char *buf, 78113498266Sopenharmony_ci size_t blen, 78213498266Sopenharmony_ci bool is_eos, 78313498266Sopenharmony_ci bool *done) 78413498266Sopenharmony_ci{ 78513498266Sopenharmony_ci struct rtsp_conn *rtspc = &(data->conn->proto.rtspc); 78613498266Sopenharmony_ci CURLcode result = CURLE_OK; 78713498266Sopenharmony_ci size_t consumed = 0; 78813498266Sopenharmony_ci 78913498266Sopenharmony_ci if(!data->req.header) 79013498266Sopenharmony_ci rtspc->in_header = FALSE; 79113498266Sopenharmony_ci *done = FALSE; 79213498266Sopenharmony_ci if(!blen) { 79313498266Sopenharmony_ci goto out; 79413498266Sopenharmony_ci } 79513498266Sopenharmony_ci 79613498266Sopenharmony_ci DEBUGF(infof(data, "rtsp_rtp_write_resp(len=%zu, in_header=%d, eos=%d)", 79713498266Sopenharmony_ci blen, rtspc->in_header, is_eos)); 79813498266Sopenharmony_ci 79913498266Sopenharmony_ci /* If header parsing is not onging, extract RTP messages */ 80013498266Sopenharmony_ci if(!rtspc->in_header) { 80113498266Sopenharmony_ci result = rtsp_filter_rtp(data, buf, blen, &consumed); 80213498266Sopenharmony_ci if(result) 80313498266Sopenharmony_ci goto out; 80413498266Sopenharmony_ci buf += consumed; 80513498266Sopenharmony_ci blen -= consumed; 80613498266Sopenharmony_ci /* either we consumed all or are at the start of header parsing */ 80713498266Sopenharmony_ci if(blen && !data->req.header) 80813498266Sopenharmony_ci DEBUGF(infof(data, "RTSP: %zu bytes, possibly excess in response body", 80913498266Sopenharmony_ci blen)); 81013498266Sopenharmony_ci } 81113498266Sopenharmony_ci 81213498266Sopenharmony_ci /* we want to parse headers, do so */ 81313498266Sopenharmony_ci if(data->req.header && blen) { 81413498266Sopenharmony_ci rtspc->in_header = TRUE; 81513498266Sopenharmony_ci result = Curl_http_write_resp_hds(data, buf, blen, &consumed, done); 81613498266Sopenharmony_ci if(result) 81713498266Sopenharmony_ci goto out; 81813498266Sopenharmony_ci 81913498266Sopenharmony_ci buf += consumed; 82013498266Sopenharmony_ci blen -= consumed; 82113498266Sopenharmony_ci 82213498266Sopenharmony_ci if(!data->req.header) 82313498266Sopenharmony_ci rtspc->in_header = FALSE; 82413498266Sopenharmony_ci 82513498266Sopenharmony_ci if(!rtspc->in_header) { 82613498266Sopenharmony_ci /* If header parsing is done, extract interleaved RTP messages */ 82713498266Sopenharmony_ci if(data->req.size <= -1) { 82813498266Sopenharmony_ci /* Respect section 4.4 of rfc2326: If the Content-Length header is 82913498266Sopenharmony_ci absent, a length 0 must be assumed. */ 83013498266Sopenharmony_ci data->req.size = 0; 83113498266Sopenharmony_ci data->req.download_done = TRUE; 83213498266Sopenharmony_ci } 83313498266Sopenharmony_ci result = rtsp_filter_rtp(data, buf, blen, &consumed); 83413498266Sopenharmony_ci if(result) 83513498266Sopenharmony_ci goto out; 83613498266Sopenharmony_ci blen -= consumed; 83713498266Sopenharmony_ci } 83813498266Sopenharmony_ci } 83913498266Sopenharmony_ci 84013498266Sopenharmony_ci if(rtspc->state != RTP_PARSE_SKIP) 84113498266Sopenharmony_ci *done = FALSE; 84213498266Sopenharmony_ci /* we SHOULD have consumed all bytes, unless the response is borked. 84313498266Sopenharmony_ci * In which case we write out the left over bytes, letting the client 84413498266Sopenharmony_ci * writer deal with it (it will report EXCESS and fail the transfer). */ 84513498266Sopenharmony_ci DEBUGF(infof(data, "rtsp_rtp_write_resp(len=%zu, in_header=%d, done=%d " 84613498266Sopenharmony_ci " rtspc->state=%d, req.size=%" CURL_FORMAT_CURL_OFF_T ")", 84713498266Sopenharmony_ci blen, rtspc->in_header, *done, rtspc->state, data->req.size)); 84813498266Sopenharmony_ci if(!result && (is_eos || blen)) { 84913498266Sopenharmony_ci result = Curl_client_write(data, CLIENTWRITE_BODY| 85013498266Sopenharmony_ci (is_eos? CLIENTWRITE_EOS:0), 85113498266Sopenharmony_ci (char *)buf, blen); 85213498266Sopenharmony_ci } 85313498266Sopenharmony_ci 85413498266Sopenharmony_ciout: 85513498266Sopenharmony_ci if((data->set.rtspreq == RTSPREQ_RECEIVE) && 85613498266Sopenharmony_ci (rtspc->state == RTP_PARSE_SKIP)) { 85713498266Sopenharmony_ci /* In special mode RECEIVE, we just process one chunk of network 85813498266Sopenharmony_ci * data, so we stop the transfer here, if we have no incomplete 85913498266Sopenharmony_ci * RTP message pending. */ 86013498266Sopenharmony_ci data->req.download_done = TRUE; 86113498266Sopenharmony_ci } 86213498266Sopenharmony_ci return result; 86313498266Sopenharmony_ci} 86413498266Sopenharmony_ci 86513498266Sopenharmony_cistatic 86613498266Sopenharmony_ciCURLcode rtp_client_write(struct Curl_easy *data, const char *ptr, size_t len) 86713498266Sopenharmony_ci{ 86813498266Sopenharmony_ci size_t wrote; 86913498266Sopenharmony_ci curl_write_callback writeit; 87013498266Sopenharmony_ci void *user_ptr; 87113498266Sopenharmony_ci 87213498266Sopenharmony_ci if(len == 0) { 87313498266Sopenharmony_ci failf(data, "Cannot write a 0 size RTP packet."); 87413498266Sopenharmony_ci return CURLE_WRITE_ERROR; 87513498266Sopenharmony_ci } 87613498266Sopenharmony_ci 87713498266Sopenharmony_ci /* If the user has configured CURLOPT_INTERLEAVEFUNCTION then use that 87813498266Sopenharmony_ci function and any configured CURLOPT_INTERLEAVEDATA to write out the RTP 87913498266Sopenharmony_ci data. Otherwise, use the CURLOPT_WRITEFUNCTION with the CURLOPT_WRITEDATA 88013498266Sopenharmony_ci pointer to write out the RTP data. */ 88113498266Sopenharmony_ci if(data->set.fwrite_rtp) { 88213498266Sopenharmony_ci writeit = data->set.fwrite_rtp; 88313498266Sopenharmony_ci user_ptr = data->set.rtp_out; 88413498266Sopenharmony_ci } 88513498266Sopenharmony_ci else { 88613498266Sopenharmony_ci writeit = data->set.fwrite_func; 88713498266Sopenharmony_ci user_ptr = data->set.out; 88813498266Sopenharmony_ci } 88913498266Sopenharmony_ci 89013498266Sopenharmony_ci Curl_set_in_callback(data, true); 89113498266Sopenharmony_ci wrote = writeit((char *)ptr, 1, len, user_ptr); 89213498266Sopenharmony_ci Curl_set_in_callback(data, false); 89313498266Sopenharmony_ci 89413498266Sopenharmony_ci if(CURL_WRITEFUNC_PAUSE == wrote) { 89513498266Sopenharmony_ci failf(data, "Cannot pause RTP"); 89613498266Sopenharmony_ci return CURLE_WRITE_ERROR; 89713498266Sopenharmony_ci } 89813498266Sopenharmony_ci 89913498266Sopenharmony_ci if(wrote != len) { 90013498266Sopenharmony_ci failf(data, "Failed writing RTP data"); 90113498266Sopenharmony_ci return CURLE_WRITE_ERROR; 90213498266Sopenharmony_ci } 90313498266Sopenharmony_ci 90413498266Sopenharmony_ci return CURLE_OK; 90513498266Sopenharmony_ci} 90613498266Sopenharmony_ci 90713498266Sopenharmony_ciCURLcode Curl_rtsp_parseheader(struct Curl_easy *data, char *header) 90813498266Sopenharmony_ci{ 90913498266Sopenharmony_ci if(checkprefix("CSeq:", header)) { 91013498266Sopenharmony_ci long CSeq = 0; 91113498266Sopenharmony_ci char *endp; 91213498266Sopenharmony_ci char *p = &header[5]; 91313498266Sopenharmony_ci while(ISBLANK(*p)) 91413498266Sopenharmony_ci p++; 91513498266Sopenharmony_ci CSeq = strtol(p, &endp, 10); 91613498266Sopenharmony_ci if(p != endp) { 91713498266Sopenharmony_ci struct RTSP *rtsp = data->req.p.rtsp; 91813498266Sopenharmony_ci rtsp->CSeq_recv = CSeq; /* mark the request */ 91913498266Sopenharmony_ci data->state.rtsp_CSeq_recv = CSeq; /* update the handle */ 92013498266Sopenharmony_ci } 92113498266Sopenharmony_ci else { 92213498266Sopenharmony_ci failf(data, "Unable to read the CSeq header: [%s]", header); 92313498266Sopenharmony_ci return CURLE_RTSP_CSEQ_ERROR; 92413498266Sopenharmony_ci } 92513498266Sopenharmony_ci } 92613498266Sopenharmony_ci else if(checkprefix("Session:", header)) { 92713498266Sopenharmony_ci char *start; 92813498266Sopenharmony_ci char *end; 92913498266Sopenharmony_ci size_t idlen; 93013498266Sopenharmony_ci 93113498266Sopenharmony_ci /* Find the first non-space letter */ 93213498266Sopenharmony_ci start = header + 8; 93313498266Sopenharmony_ci while(*start && ISBLANK(*start)) 93413498266Sopenharmony_ci start++; 93513498266Sopenharmony_ci 93613498266Sopenharmony_ci if(!*start) { 93713498266Sopenharmony_ci failf(data, "Got a blank Session ID"); 93813498266Sopenharmony_ci return CURLE_RTSP_SESSION_ERROR; 93913498266Sopenharmony_ci } 94013498266Sopenharmony_ci 94113498266Sopenharmony_ci /* Find the end of Session ID 94213498266Sopenharmony_ci * 94313498266Sopenharmony_ci * Allow any non whitespace content, up to the field separator or end of 94413498266Sopenharmony_ci * line. RFC 2326 isn't 100% clear on the session ID and for example 94513498266Sopenharmony_ci * gstreamer does url-encoded session ID's not covered by the standard. 94613498266Sopenharmony_ci */ 94713498266Sopenharmony_ci end = start; 94813498266Sopenharmony_ci while(*end && *end != ';' && !ISSPACE(*end)) 94913498266Sopenharmony_ci end++; 95013498266Sopenharmony_ci idlen = end - start; 95113498266Sopenharmony_ci 95213498266Sopenharmony_ci if(data->set.str[STRING_RTSP_SESSION_ID]) { 95313498266Sopenharmony_ci 95413498266Sopenharmony_ci /* If the Session ID is set, then compare */ 95513498266Sopenharmony_ci if(strlen(data->set.str[STRING_RTSP_SESSION_ID]) != idlen || 95613498266Sopenharmony_ci strncmp(start, data->set.str[STRING_RTSP_SESSION_ID], idlen)) { 95713498266Sopenharmony_ci failf(data, "Got RTSP Session ID Line [%s], but wanted ID [%s]", 95813498266Sopenharmony_ci start, data->set.str[STRING_RTSP_SESSION_ID]); 95913498266Sopenharmony_ci return CURLE_RTSP_SESSION_ERROR; 96013498266Sopenharmony_ci } 96113498266Sopenharmony_ci } 96213498266Sopenharmony_ci else { 96313498266Sopenharmony_ci /* If the Session ID is not set, and we find it in a response, then set 96413498266Sopenharmony_ci * it. 96513498266Sopenharmony_ci */ 96613498266Sopenharmony_ci 96713498266Sopenharmony_ci /* Copy the id substring into a new buffer */ 96813498266Sopenharmony_ci data->set.str[STRING_RTSP_SESSION_ID] = Curl_memdup0(start, idlen); 96913498266Sopenharmony_ci if(!data->set.str[STRING_RTSP_SESSION_ID]) 97013498266Sopenharmony_ci return CURLE_OUT_OF_MEMORY; 97113498266Sopenharmony_ci } 97213498266Sopenharmony_ci } 97313498266Sopenharmony_ci else if(checkprefix("Transport:", header)) { 97413498266Sopenharmony_ci CURLcode result; 97513498266Sopenharmony_ci result = rtsp_parse_transport(data, header + 10); 97613498266Sopenharmony_ci if(result) 97713498266Sopenharmony_ci return result; 97813498266Sopenharmony_ci } 97913498266Sopenharmony_ci return CURLE_OK; 98013498266Sopenharmony_ci} 98113498266Sopenharmony_ci 98213498266Sopenharmony_cistatic 98313498266Sopenharmony_ciCURLcode rtsp_parse_transport(struct Curl_easy *data, char *transport) 98413498266Sopenharmony_ci{ 98513498266Sopenharmony_ci /* If we receive multiple Transport response-headers, the linterleaved 98613498266Sopenharmony_ci channels of each response header is recorded and used together for 98713498266Sopenharmony_ci subsequent data validity checks.*/ 98813498266Sopenharmony_ci /* e.g.: ' RTP/AVP/TCP;unicast;interleaved=5-6' */ 98913498266Sopenharmony_ci char *start; 99013498266Sopenharmony_ci char *end; 99113498266Sopenharmony_ci start = transport; 99213498266Sopenharmony_ci while(start && *start) { 99313498266Sopenharmony_ci while(*start && ISBLANK(*start) ) 99413498266Sopenharmony_ci start++; 99513498266Sopenharmony_ci end = strchr(start, ';'); 99613498266Sopenharmony_ci if(checkprefix("interleaved=", start)) { 99713498266Sopenharmony_ci long chan1, chan2, chan; 99813498266Sopenharmony_ci char *endp; 99913498266Sopenharmony_ci char *p = start + 12; 100013498266Sopenharmony_ci chan1 = strtol(p, &endp, 10); 100113498266Sopenharmony_ci if(p != endp && chan1 >= 0 && chan1 <= 255) { 100213498266Sopenharmony_ci unsigned char *rtp_channel_mask = data->state.rtp_channel_mask; 100313498266Sopenharmony_ci chan2 = chan1; 100413498266Sopenharmony_ci if(*endp == '-') { 100513498266Sopenharmony_ci p = endp + 1; 100613498266Sopenharmony_ci chan2 = strtol(p, &endp, 10); 100713498266Sopenharmony_ci if(p == endp || chan2 < 0 || chan2 > 255) { 100813498266Sopenharmony_ci infof(data, "Unable to read the interleaved parameter from " 100913498266Sopenharmony_ci "Transport header: [%s]", transport); 101013498266Sopenharmony_ci chan2 = chan1; 101113498266Sopenharmony_ci } 101213498266Sopenharmony_ci } 101313498266Sopenharmony_ci for(chan = chan1; chan <= chan2; chan++) { 101413498266Sopenharmony_ci long idx = chan / 8; 101513498266Sopenharmony_ci long off = chan % 8; 101613498266Sopenharmony_ci rtp_channel_mask[idx] |= (unsigned char)(1 << off); 101713498266Sopenharmony_ci } 101813498266Sopenharmony_ci } 101913498266Sopenharmony_ci else { 102013498266Sopenharmony_ci infof(data, "Unable to read the interleaved parameter from " 102113498266Sopenharmony_ci "Transport header: [%s]", transport); 102213498266Sopenharmony_ci } 102313498266Sopenharmony_ci break; 102413498266Sopenharmony_ci } 102513498266Sopenharmony_ci /* skip to next parameter */ 102613498266Sopenharmony_ci start = (!end) ? end : (end + 1); 102713498266Sopenharmony_ci } 102813498266Sopenharmony_ci return CURLE_OK; 102913498266Sopenharmony_ci} 103013498266Sopenharmony_ci 103113498266Sopenharmony_ci 103213498266Sopenharmony_ci#endif /* CURL_DISABLE_RTSP or using Hyper */ 1033