1*6236dae4SAndroid Build Coastguard Worker /***************************************************************************
2*6236dae4SAndroid Build Coastguard Worker * _ _ ____ _
3*6236dae4SAndroid Build Coastguard Worker * Project ___| | | | _ \| |
4*6236dae4SAndroid Build Coastguard Worker * / __| | | | |_) | |
5*6236dae4SAndroid Build Coastguard Worker * | (__| |_| | _ <| |___
6*6236dae4SAndroid Build Coastguard Worker * \___|\___/|_| \_\_____|
7*6236dae4SAndroid Build Coastguard Worker *
8*6236dae4SAndroid Build Coastguard Worker * Copyright (C) Daniel Stenberg, <[email protected]>, et al.
9*6236dae4SAndroid Build Coastguard Worker *
10*6236dae4SAndroid Build Coastguard Worker * This software is licensed as described in the file COPYING, which
11*6236dae4SAndroid Build Coastguard Worker * you should have received as part of this distribution. The terms
12*6236dae4SAndroid Build Coastguard Worker * are also available at https://curl.se/docs/copyright.html.
13*6236dae4SAndroid Build Coastguard Worker *
14*6236dae4SAndroid Build Coastguard Worker * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15*6236dae4SAndroid Build Coastguard Worker * copies of the Software, and permit persons to whom the Software is
16*6236dae4SAndroid Build Coastguard Worker * furnished to do so, under the terms of the COPYING file.
17*6236dae4SAndroid Build Coastguard Worker *
18*6236dae4SAndroid Build Coastguard Worker * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19*6236dae4SAndroid Build Coastguard Worker * KIND, either express or implied.
20*6236dae4SAndroid Build Coastguard Worker *
21*6236dae4SAndroid Build Coastguard Worker * SPDX-License-Identifier: curl
22*6236dae4SAndroid Build Coastguard Worker *
23*6236dae4SAndroid Build Coastguard Worker ***************************************************************************/
24*6236dae4SAndroid Build Coastguard Worker
25*6236dae4SAndroid Build Coastguard Worker #include "curl_setup.h"
26*6236dae4SAndroid Build Coastguard Worker
27*6236dae4SAndroid Build Coastguard Worker #if !defined(CURL_DISABLE_RTSP) && !defined(USE_HYPER)
28*6236dae4SAndroid Build Coastguard Worker
29*6236dae4SAndroid Build Coastguard Worker #include "urldata.h"
30*6236dae4SAndroid Build Coastguard Worker #include <curl/curl.h>
31*6236dae4SAndroid Build Coastguard Worker #include "transfer.h"
32*6236dae4SAndroid Build Coastguard Worker #include "sendf.h"
33*6236dae4SAndroid Build Coastguard Worker #include "multiif.h"
34*6236dae4SAndroid Build Coastguard Worker #include "http.h"
35*6236dae4SAndroid Build Coastguard Worker #include "url.h"
36*6236dae4SAndroid Build Coastguard Worker #include "progress.h"
37*6236dae4SAndroid Build Coastguard Worker #include "rtsp.h"
38*6236dae4SAndroid Build Coastguard Worker #include "strcase.h"
39*6236dae4SAndroid Build Coastguard Worker #include "select.h"
40*6236dae4SAndroid Build Coastguard Worker #include "connect.h"
41*6236dae4SAndroid Build Coastguard Worker #include "cfilters.h"
42*6236dae4SAndroid Build Coastguard Worker #include "strdup.h"
43*6236dae4SAndroid Build Coastguard Worker /* The last 3 #include files should be in this order */
44*6236dae4SAndroid Build Coastguard Worker #include "curl_printf.h"
45*6236dae4SAndroid Build Coastguard Worker #include "curl_memory.h"
46*6236dae4SAndroid Build Coastguard Worker #include "memdebug.h"
47*6236dae4SAndroid Build Coastguard Worker
48*6236dae4SAndroid Build Coastguard Worker #define RTP_PKT_LENGTH(p) ((((unsigned int)((unsigned char)((p)[2]))) << 8) | \
49*6236dae4SAndroid Build Coastguard Worker ((unsigned int)((unsigned char)((p)[3]))))
50*6236dae4SAndroid Build Coastguard Worker
51*6236dae4SAndroid Build Coastguard Worker /* protocol-specific functions set up to be called by the main engine */
52*6236dae4SAndroid Build Coastguard Worker static CURLcode rtsp_do(struct Curl_easy *data, bool *done);
53*6236dae4SAndroid Build Coastguard Worker static CURLcode rtsp_done(struct Curl_easy *data, CURLcode, bool premature);
54*6236dae4SAndroid Build Coastguard Worker static CURLcode rtsp_connect(struct Curl_easy *data, bool *done);
55*6236dae4SAndroid Build Coastguard Worker static CURLcode rtsp_disconnect(struct Curl_easy *data,
56*6236dae4SAndroid Build Coastguard Worker struct connectdata *conn, bool dead);
57*6236dae4SAndroid Build Coastguard Worker static int rtsp_getsock_do(struct Curl_easy *data,
58*6236dae4SAndroid Build Coastguard Worker struct connectdata *conn, curl_socket_t *socks);
59*6236dae4SAndroid Build Coastguard Worker
60*6236dae4SAndroid Build Coastguard Worker /*
61*6236dae4SAndroid Build Coastguard Worker * Parse and write out an RTSP response.
62*6236dae4SAndroid Build Coastguard Worker * @param data the transfer
63*6236dae4SAndroid Build Coastguard Worker * @param conn the connection
64*6236dae4SAndroid Build Coastguard Worker * @param buf data read from connection
65*6236dae4SAndroid Build Coastguard Worker * @param blen amount of data in buf
66*6236dae4SAndroid Build Coastguard Worker * @param is_eos TRUE iff this is the last write
67*6236dae4SAndroid Build Coastguard Worker * @param readmore out, TRUE iff complete buf was consumed and more data
68*6236dae4SAndroid Build Coastguard Worker * is needed
69*6236dae4SAndroid Build Coastguard Worker */
70*6236dae4SAndroid Build Coastguard Worker static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data,
71*6236dae4SAndroid Build Coastguard Worker const char *buf,
72*6236dae4SAndroid Build Coastguard Worker size_t blen,
73*6236dae4SAndroid Build Coastguard Worker bool is_eos);
74*6236dae4SAndroid Build Coastguard Worker
75*6236dae4SAndroid Build Coastguard Worker static CURLcode rtsp_setup_connection(struct Curl_easy *data,
76*6236dae4SAndroid Build Coastguard Worker struct connectdata *conn);
77*6236dae4SAndroid Build Coastguard Worker static unsigned int rtsp_conncheck(struct Curl_easy *data,
78*6236dae4SAndroid Build Coastguard Worker struct connectdata *check,
79*6236dae4SAndroid Build Coastguard Worker unsigned int checks_to_perform);
80*6236dae4SAndroid Build Coastguard Worker
81*6236dae4SAndroid Build Coastguard Worker /* this returns the socket to wait for in the DO and DOING state for the multi
82*6236dae4SAndroid Build Coastguard Worker interface and then we are always _sending_ a request and thus we wait for
83*6236dae4SAndroid Build Coastguard Worker the single socket to become writable only */
rtsp_getsock_do(struct Curl_easy * data,struct connectdata * conn,curl_socket_t * socks)84*6236dae4SAndroid Build Coastguard Worker static int rtsp_getsock_do(struct Curl_easy *data, struct connectdata *conn,
85*6236dae4SAndroid Build Coastguard Worker curl_socket_t *socks)
86*6236dae4SAndroid Build Coastguard Worker {
87*6236dae4SAndroid Build Coastguard Worker /* write mode */
88*6236dae4SAndroid Build Coastguard Worker (void)data;
89*6236dae4SAndroid Build Coastguard Worker socks[0] = conn->sock[FIRSTSOCKET];
90*6236dae4SAndroid Build Coastguard Worker return GETSOCK_WRITESOCK(0);
91*6236dae4SAndroid Build Coastguard Worker }
92*6236dae4SAndroid Build Coastguard Worker
93*6236dae4SAndroid Build Coastguard Worker static
94*6236dae4SAndroid Build Coastguard Worker CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr, size_t len);
95*6236dae4SAndroid Build Coastguard Worker static
96*6236dae4SAndroid Build Coastguard Worker CURLcode rtsp_parse_transport(struct Curl_easy *data, const char *transport);
97*6236dae4SAndroid Build Coastguard Worker
98*6236dae4SAndroid Build Coastguard Worker
99*6236dae4SAndroid Build Coastguard Worker /*
100*6236dae4SAndroid Build Coastguard Worker * RTSP handler interface.
101*6236dae4SAndroid Build Coastguard Worker */
102*6236dae4SAndroid Build Coastguard Worker const struct Curl_handler Curl_handler_rtsp = {
103*6236dae4SAndroid Build Coastguard Worker "rtsp", /* scheme */
104*6236dae4SAndroid Build Coastguard Worker rtsp_setup_connection, /* setup_connection */
105*6236dae4SAndroid Build Coastguard Worker rtsp_do, /* do_it */
106*6236dae4SAndroid Build Coastguard Worker rtsp_done, /* done */
107*6236dae4SAndroid Build Coastguard Worker ZERO_NULL, /* do_more */
108*6236dae4SAndroid Build Coastguard Worker rtsp_connect, /* connect_it */
109*6236dae4SAndroid Build Coastguard Worker ZERO_NULL, /* connecting */
110*6236dae4SAndroid Build Coastguard Worker ZERO_NULL, /* doing */
111*6236dae4SAndroid Build Coastguard Worker ZERO_NULL, /* proto_getsock */
112*6236dae4SAndroid Build Coastguard Worker rtsp_getsock_do, /* doing_getsock */
113*6236dae4SAndroid Build Coastguard Worker ZERO_NULL, /* domore_getsock */
114*6236dae4SAndroid Build Coastguard Worker ZERO_NULL, /* perform_getsock */
115*6236dae4SAndroid Build Coastguard Worker rtsp_disconnect, /* disconnect */
116*6236dae4SAndroid Build Coastguard Worker rtsp_rtp_write_resp, /* write_resp */
117*6236dae4SAndroid Build Coastguard Worker ZERO_NULL, /* write_resp_hd */
118*6236dae4SAndroid Build Coastguard Worker rtsp_conncheck, /* connection_check */
119*6236dae4SAndroid Build Coastguard Worker ZERO_NULL, /* attach connection */
120*6236dae4SAndroid Build Coastguard Worker PORT_RTSP, /* defport */
121*6236dae4SAndroid Build Coastguard Worker CURLPROTO_RTSP, /* protocol */
122*6236dae4SAndroid Build Coastguard Worker CURLPROTO_RTSP, /* family */
123*6236dae4SAndroid Build Coastguard Worker PROTOPT_NONE /* flags */
124*6236dae4SAndroid Build Coastguard Worker };
125*6236dae4SAndroid Build Coastguard Worker
126*6236dae4SAndroid Build Coastguard Worker #define MAX_RTP_BUFFERSIZE 1000000 /* arbitrary */
127*6236dae4SAndroid Build Coastguard Worker
rtsp_setup_connection(struct Curl_easy * data,struct connectdata * conn)128*6236dae4SAndroid Build Coastguard Worker static CURLcode rtsp_setup_connection(struct Curl_easy *data,
129*6236dae4SAndroid Build Coastguard Worker struct connectdata *conn)
130*6236dae4SAndroid Build Coastguard Worker {
131*6236dae4SAndroid Build Coastguard Worker struct RTSP *rtsp;
132*6236dae4SAndroid Build Coastguard Worker (void)conn;
133*6236dae4SAndroid Build Coastguard Worker
134*6236dae4SAndroid Build Coastguard Worker data->req.p.rtsp = rtsp = calloc(1, sizeof(struct RTSP));
135*6236dae4SAndroid Build Coastguard Worker if(!rtsp)
136*6236dae4SAndroid Build Coastguard Worker return CURLE_OUT_OF_MEMORY;
137*6236dae4SAndroid Build Coastguard Worker
138*6236dae4SAndroid Build Coastguard Worker Curl_dyn_init(&conn->proto.rtspc.buf, MAX_RTP_BUFFERSIZE);
139*6236dae4SAndroid Build Coastguard Worker return CURLE_OK;
140*6236dae4SAndroid Build Coastguard Worker }
141*6236dae4SAndroid Build Coastguard Worker
142*6236dae4SAndroid Build Coastguard Worker
143*6236dae4SAndroid Build Coastguard Worker /*
144*6236dae4SAndroid Build Coastguard Worker * Function to check on various aspects of a connection.
145*6236dae4SAndroid Build Coastguard Worker */
rtsp_conncheck(struct Curl_easy * data,struct connectdata * conn,unsigned int checks_to_perform)146*6236dae4SAndroid Build Coastguard Worker static unsigned int rtsp_conncheck(struct Curl_easy *data,
147*6236dae4SAndroid Build Coastguard Worker struct connectdata *conn,
148*6236dae4SAndroid Build Coastguard Worker unsigned int checks_to_perform)
149*6236dae4SAndroid Build Coastguard Worker {
150*6236dae4SAndroid Build Coastguard Worker unsigned int ret_val = CONNRESULT_NONE;
151*6236dae4SAndroid Build Coastguard Worker (void)data;
152*6236dae4SAndroid Build Coastguard Worker
153*6236dae4SAndroid Build Coastguard Worker if(checks_to_perform & CONNCHECK_ISDEAD) {
154*6236dae4SAndroid Build Coastguard Worker bool input_pending;
155*6236dae4SAndroid Build Coastguard Worker if(!Curl_conn_is_alive(data, conn, &input_pending))
156*6236dae4SAndroid Build Coastguard Worker ret_val |= CONNRESULT_DEAD;
157*6236dae4SAndroid Build Coastguard Worker }
158*6236dae4SAndroid Build Coastguard Worker
159*6236dae4SAndroid Build Coastguard Worker return ret_val;
160*6236dae4SAndroid Build Coastguard Worker }
161*6236dae4SAndroid Build Coastguard Worker
162*6236dae4SAndroid Build Coastguard Worker
rtsp_connect(struct Curl_easy * data,bool * done)163*6236dae4SAndroid Build Coastguard Worker static CURLcode rtsp_connect(struct Curl_easy *data, bool *done)
164*6236dae4SAndroid Build Coastguard Worker {
165*6236dae4SAndroid Build Coastguard Worker CURLcode httpStatus;
166*6236dae4SAndroid Build Coastguard Worker
167*6236dae4SAndroid Build Coastguard Worker httpStatus = Curl_http_connect(data, done);
168*6236dae4SAndroid Build Coastguard Worker
169*6236dae4SAndroid Build Coastguard Worker /* Initialize the CSeq if not already done */
170*6236dae4SAndroid Build Coastguard Worker if(data->state.rtsp_next_client_CSeq == 0)
171*6236dae4SAndroid Build Coastguard Worker data->state.rtsp_next_client_CSeq = 1;
172*6236dae4SAndroid Build Coastguard Worker if(data->state.rtsp_next_server_CSeq == 0)
173*6236dae4SAndroid Build Coastguard Worker data->state.rtsp_next_server_CSeq = 1;
174*6236dae4SAndroid Build Coastguard Worker
175*6236dae4SAndroid Build Coastguard Worker data->conn->proto.rtspc.rtp_channel = -1;
176*6236dae4SAndroid Build Coastguard Worker
177*6236dae4SAndroid Build Coastguard Worker return httpStatus;
178*6236dae4SAndroid Build Coastguard Worker }
179*6236dae4SAndroid Build Coastguard Worker
rtsp_disconnect(struct Curl_easy * data,struct connectdata * conn,bool dead)180*6236dae4SAndroid Build Coastguard Worker static CURLcode rtsp_disconnect(struct Curl_easy *data,
181*6236dae4SAndroid Build Coastguard Worker struct connectdata *conn, bool dead)
182*6236dae4SAndroid Build Coastguard Worker {
183*6236dae4SAndroid Build Coastguard Worker (void) dead;
184*6236dae4SAndroid Build Coastguard Worker (void) data;
185*6236dae4SAndroid Build Coastguard Worker Curl_dyn_free(&conn->proto.rtspc.buf);
186*6236dae4SAndroid Build Coastguard Worker return CURLE_OK;
187*6236dae4SAndroid Build Coastguard Worker }
188*6236dae4SAndroid Build Coastguard Worker
189*6236dae4SAndroid Build Coastguard Worker
rtsp_done(struct Curl_easy * data,CURLcode status,bool premature)190*6236dae4SAndroid Build Coastguard Worker static CURLcode rtsp_done(struct Curl_easy *data,
191*6236dae4SAndroid Build Coastguard Worker CURLcode status, bool premature)
192*6236dae4SAndroid Build Coastguard Worker {
193*6236dae4SAndroid Build Coastguard Worker struct RTSP *rtsp = data->req.p.rtsp;
194*6236dae4SAndroid Build Coastguard Worker CURLcode httpStatus;
195*6236dae4SAndroid Build Coastguard Worker
196*6236dae4SAndroid Build Coastguard Worker /* Bypass HTTP empty-reply checks on receive */
197*6236dae4SAndroid Build Coastguard Worker if(data->set.rtspreq == RTSPREQ_RECEIVE)
198*6236dae4SAndroid Build Coastguard Worker premature = TRUE;
199*6236dae4SAndroid Build Coastguard Worker
200*6236dae4SAndroid Build Coastguard Worker httpStatus = Curl_http_done(data, status, premature);
201*6236dae4SAndroid Build Coastguard Worker
202*6236dae4SAndroid Build Coastguard Worker if(rtsp && !status && !httpStatus) {
203*6236dae4SAndroid Build Coastguard Worker /* Check the sequence numbers */
204*6236dae4SAndroid Build Coastguard Worker long CSeq_sent = rtsp->CSeq_sent;
205*6236dae4SAndroid Build Coastguard Worker long CSeq_recv = rtsp->CSeq_recv;
206*6236dae4SAndroid Build Coastguard Worker if((data->set.rtspreq != RTSPREQ_RECEIVE) && (CSeq_sent != CSeq_recv)) {
207*6236dae4SAndroid Build Coastguard Worker failf(data,
208*6236dae4SAndroid Build Coastguard Worker "The CSeq of this request %ld did not match the response %ld",
209*6236dae4SAndroid Build Coastguard Worker CSeq_sent, CSeq_recv);
210*6236dae4SAndroid Build Coastguard Worker return CURLE_RTSP_CSEQ_ERROR;
211*6236dae4SAndroid Build Coastguard Worker }
212*6236dae4SAndroid Build Coastguard Worker if(data->set.rtspreq == RTSPREQ_RECEIVE &&
213*6236dae4SAndroid Build Coastguard Worker (data->conn->proto.rtspc.rtp_channel == -1)) {
214*6236dae4SAndroid Build Coastguard Worker infof(data, "Got an RTP Receive with a CSeq of %ld", CSeq_recv);
215*6236dae4SAndroid Build Coastguard Worker }
216*6236dae4SAndroid Build Coastguard Worker }
217*6236dae4SAndroid Build Coastguard Worker
218*6236dae4SAndroid Build Coastguard Worker return httpStatus;
219*6236dae4SAndroid Build Coastguard Worker }
220*6236dae4SAndroid Build Coastguard Worker
rtsp_do(struct Curl_easy * data,bool * done)221*6236dae4SAndroid Build Coastguard Worker static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
222*6236dae4SAndroid Build Coastguard Worker {
223*6236dae4SAndroid Build Coastguard Worker struct connectdata *conn = data->conn;
224*6236dae4SAndroid Build Coastguard Worker CURLcode result = CURLE_OK;
225*6236dae4SAndroid Build Coastguard Worker Curl_RtspReq rtspreq = data->set.rtspreq;
226*6236dae4SAndroid Build Coastguard Worker struct RTSP *rtsp = data->req.p.rtsp;
227*6236dae4SAndroid Build Coastguard Worker struct dynbuf req_buffer;
228*6236dae4SAndroid Build Coastguard Worker
229*6236dae4SAndroid Build Coastguard Worker const char *p_request = NULL;
230*6236dae4SAndroid Build Coastguard Worker const char *p_session_id = NULL;
231*6236dae4SAndroid Build Coastguard Worker const char *p_accept = NULL;
232*6236dae4SAndroid Build Coastguard Worker const char *p_accept_encoding = NULL;
233*6236dae4SAndroid Build Coastguard Worker const char *p_range = NULL;
234*6236dae4SAndroid Build Coastguard Worker const char *p_referrer = NULL;
235*6236dae4SAndroid Build Coastguard Worker const char *p_stream_uri = NULL;
236*6236dae4SAndroid Build Coastguard Worker const char *p_transport = NULL;
237*6236dae4SAndroid Build Coastguard Worker const char *p_uagent = NULL;
238*6236dae4SAndroid Build Coastguard Worker const char *p_proxyuserpwd = NULL;
239*6236dae4SAndroid Build Coastguard Worker const char *p_userpwd = NULL;
240*6236dae4SAndroid Build Coastguard Worker
241*6236dae4SAndroid Build Coastguard Worker *done = TRUE;
242*6236dae4SAndroid Build Coastguard Worker /* Initialize a dynamic send buffer */
243*6236dae4SAndroid Build Coastguard Worker Curl_dyn_init(&req_buffer, DYN_RTSP_REQ_HEADER);
244*6236dae4SAndroid Build Coastguard Worker
245*6236dae4SAndroid Build Coastguard Worker rtsp->CSeq_sent = data->state.rtsp_next_client_CSeq;
246*6236dae4SAndroid Build Coastguard Worker rtsp->CSeq_recv = 0;
247*6236dae4SAndroid Build Coastguard Worker
248*6236dae4SAndroid Build Coastguard Worker /* Setup the first_* fields to allow auth details get sent
249*6236dae4SAndroid Build Coastguard Worker to this origin */
250*6236dae4SAndroid Build Coastguard Worker
251*6236dae4SAndroid Build Coastguard Worker if(!data->state.first_host) {
252*6236dae4SAndroid Build Coastguard Worker data->state.first_host = strdup(conn->host.name);
253*6236dae4SAndroid Build Coastguard Worker if(!data->state.first_host)
254*6236dae4SAndroid Build Coastguard Worker return CURLE_OUT_OF_MEMORY;
255*6236dae4SAndroid Build Coastguard Worker
256*6236dae4SAndroid Build Coastguard Worker data->state.first_remote_port = conn->remote_port;
257*6236dae4SAndroid Build Coastguard Worker data->state.first_remote_protocol = conn->handler->protocol;
258*6236dae4SAndroid Build Coastguard Worker }
259*6236dae4SAndroid Build Coastguard Worker
260*6236dae4SAndroid Build Coastguard Worker /* Setup the 'p_request' pointer to the proper p_request string
261*6236dae4SAndroid Build Coastguard Worker * Since all RTSP requests are included here, there is no need to
262*6236dae4SAndroid Build Coastguard Worker * support custom requests like HTTP.
263*6236dae4SAndroid Build Coastguard Worker **/
264*6236dae4SAndroid Build Coastguard Worker data->req.no_body = TRUE; /* most requests do not contain a body */
265*6236dae4SAndroid Build Coastguard Worker switch(rtspreq) {
266*6236dae4SAndroid Build Coastguard Worker default:
267*6236dae4SAndroid Build Coastguard Worker failf(data, "Got invalid RTSP request");
268*6236dae4SAndroid Build Coastguard Worker return CURLE_BAD_FUNCTION_ARGUMENT;
269*6236dae4SAndroid Build Coastguard Worker case RTSPREQ_OPTIONS:
270*6236dae4SAndroid Build Coastguard Worker p_request = "OPTIONS";
271*6236dae4SAndroid Build Coastguard Worker break;
272*6236dae4SAndroid Build Coastguard Worker case RTSPREQ_DESCRIBE:
273*6236dae4SAndroid Build Coastguard Worker p_request = "DESCRIBE";
274*6236dae4SAndroid Build Coastguard Worker data->req.no_body = FALSE;
275*6236dae4SAndroid Build Coastguard Worker break;
276*6236dae4SAndroid Build Coastguard Worker case RTSPREQ_ANNOUNCE:
277*6236dae4SAndroid Build Coastguard Worker p_request = "ANNOUNCE";
278*6236dae4SAndroid Build Coastguard Worker break;
279*6236dae4SAndroid Build Coastguard Worker case RTSPREQ_SETUP:
280*6236dae4SAndroid Build Coastguard Worker p_request = "SETUP";
281*6236dae4SAndroid Build Coastguard Worker break;
282*6236dae4SAndroid Build Coastguard Worker case RTSPREQ_PLAY:
283*6236dae4SAndroid Build Coastguard Worker p_request = "PLAY";
284*6236dae4SAndroid Build Coastguard Worker break;
285*6236dae4SAndroid Build Coastguard Worker case RTSPREQ_PAUSE:
286*6236dae4SAndroid Build Coastguard Worker p_request = "PAUSE";
287*6236dae4SAndroid Build Coastguard Worker break;
288*6236dae4SAndroid Build Coastguard Worker case RTSPREQ_TEARDOWN:
289*6236dae4SAndroid Build Coastguard Worker p_request = "TEARDOWN";
290*6236dae4SAndroid Build Coastguard Worker break;
291*6236dae4SAndroid Build Coastguard Worker case RTSPREQ_GET_PARAMETER:
292*6236dae4SAndroid Build Coastguard Worker /* GET_PARAMETER's no_body status is determined later */
293*6236dae4SAndroid Build Coastguard Worker p_request = "GET_PARAMETER";
294*6236dae4SAndroid Build Coastguard Worker data->req.no_body = FALSE;
295*6236dae4SAndroid Build Coastguard Worker break;
296*6236dae4SAndroid Build Coastguard Worker case RTSPREQ_SET_PARAMETER:
297*6236dae4SAndroid Build Coastguard Worker p_request = "SET_PARAMETER";
298*6236dae4SAndroid Build Coastguard Worker break;
299*6236dae4SAndroid Build Coastguard Worker case RTSPREQ_RECORD:
300*6236dae4SAndroid Build Coastguard Worker p_request = "RECORD";
301*6236dae4SAndroid Build Coastguard Worker break;
302*6236dae4SAndroid Build Coastguard Worker case RTSPREQ_RECEIVE:
303*6236dae4SAndroid Build Coastguard Worker p_request = "";
304*6236dae4SAndroid Build Coastguard Worker /* Treat interleaved RTP as body */
305*6236dae4SAndroid Build Coastguard Worker data->req.no_body = FALSE;
306*6236dae4SAndroid Build Coastguard Worker break;
307*6236dae4SAndroid Build Coastguard Worker case RTSPREQ_LAST:
308*6236dae4SAndroid Build Coastguard Worker failf(data, "Got invalid RTSP request: RTSPREQ_LAST");
309*6236dae4SAndroid Build Coastguard Worker return CURLE_BAD_FUNCTION_ARGUMENT;
310*6236dae4SAndroid Build Coastguard Worker }
311*6236dae4SAndroid Build Coastguard Worker
312*6236dae4SAndroid Build Coastguard Worker if(rtspreq == RTSPREQ_RECEIVE) {
313*6236dae4SAndroid Build Coastguard Worker Curl_xfer_setup1(data, CURL_XFER_RECV, -1, TRUE);
314*6236dae4SAndroid Build Coastguard Worker goto out;
315*6236dae4SAndroid Build Coastguard Worker }
316*6236dae4SAndroid Build Coastguard Worker
317*6236dae4SAndroid Build Coastguard Worker p_session_id = data->set.str[STRING_RTSP_SESSION_ID];
318*6236dae4SAndroid Build Coastguard Worker if(!p_session_id &&
319*6236dae4SAndroid Build Coastguard Worker (rtspreq & ~(Curl_RtspReq)(RTSPREQ_OPTIONS |
320*6236dae4SAndroid Build Coastguard Worker RTSPREQ_DESCRIBE |
321*6236dae4SAndroid Build Coastguard Worker RTSPREQ_SETUP))) {
322*6236dae4SAndroid Build Coastguard Worker failf(data, "Refusing to issue an RTSP request [%s] without a session ID.",
323*6236dae4SAndroid Build Coastguard Worker p_request);
324*6236dae4SAndroid Build Coastguard Worker result = CURLE_BAD_FUNCTION_ARGUMENT;
325*6236dae4SAndroid Build Coastguard Worker goto out;
326*6236dae4SAndroid Build Coastguard Worker }
327*6236dae4SAndroid Build Coastguard Worker
328*6236dae4SAndroid Build Coastguard Worker /* Stream URI. Default to server '*' if not specified */
329*6236dae4SAndroid Build Coastguard Worker if(data->set.str[STRING_RTSP_STREAM_URI]) {
330*6236dae4SAndroid Build Coastguard Worker p_stream_uri = data->set.str[STRING_RTSP_STREAM_URI];
331*6236dae4SAndroid Build Coastguard Worker }
332*6236dae4SAndroid Build Coastguard Worker else {
333*6236dae4SAndroid Build Coastguard Worker p_stream_uri = "*";
334*6236dae4SAndroid Build Coastguard Worker }
335*6236dae4SAndroid Build Coastguard Worker
336*6236dae4SAndroid Build Coastguard Worker /* Transport Header for SETUP requests */
337*6236dae4SAndroid Build Coastguard Worker p_transport = Curl_checkheaders(data, STRCONST("Transport"));
338*6236dae4SAndroid Build Coastguard Worker if(rtspreq == RTSPREQ_SETUP && !p_transport) {
339*6236dae4SAndroid Build Coastguard Worker /* New Transport: setting? */
340*6236dae4SAndroid Build Coastguard Worker if(data->set.str[STRING_RTSP_TRANSPORT]) {
341*6236dae4SAndroid Build Coastguard Worker Curl_safefree(data->state.aptr.rtsp_transport);
342*6236dae4SAndroid Build Coastguard Worker
343*6236dae4SAndroid Build Coastguard Worker data->state.aptr.rtsp_transport =
344*6236dae4SAndroid Build Coastguard Worker aprintf("Transport: %s\r\n",
345*6236dae4SAndroid Build Coastguard Worker data->set.str[STRING_RTSP_TRANSPORT]);
346*6236dae4SAndroid Build Coastguard Worker if(!data->state.aptr.rtsp_transport)
347*6236dae4SAndroid Build Coastguard Worker return CURLE_OUT_OF_MEMORY;
348*6236dae4SAndroid Build Coastguard Worker }
349*6236dae4SAndroid Build Coastguard Worker else {
350*6236dae4SAndroid Build Coastguard Worker failf(data,
351*6236dae4SAndroid Build Coastguard Worker "Refusing to issue an RTSP SETUP without a Transport: header.");
352*6236dae4SAndroid Build Coastguard Worker result = CURLE_BAD_FUNCTION_ARGUMENT;
353*6236dae4SAndroid Build Coastguard Worker goto out;
354*6236dae4SAndroid Build Coastguard Worker }
355*6236dae4SAndroid Build Coastguard Worker
356*6236dae4SAndroid Build Coastguard Worker p_transport = data->state.aptr.rtsp_transport;
357*6236dae4SAndroid Build Coastguard Worker }
358*6236dae4SAndroid Build Coastguard Worker
359*6236dae4SAndroid Build Coastguard Worker /* Accept Headers for DESCRIBE requests */
360*6236dae4SAndroid Build Coastguard Worker if(rtspreq == RTSPREQ_DESCRIBE) {
361*6236dae4SAndroid Build Coastguard Worker /* Accept Header */
362*6236dae4SAndroid Build Coastguard Worker p_accept = Curl_checkheaders(data, STRCONST("Accept")) ?
363*6236dae4SAndroid Build Coastguard Worker NULL : "Accept: application/sdp\r\n";
364*6236dae4SAndroid Build Coastguard Worker
365*6236dae4SAndroid Build Coastguard Worker /* Accept-Encoding header */
366*6236dae4SAndroid Build Coastguard Worker if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) &&
367*6236dae4SAndroid Build Coastguard Worker data->set.str[STRING_ENCODING]) {
368*6236dae4SAndroid Build Coastguard Worker Curl_safefree(data->state.aptr.accept_encoding);
369*6236dae4SAndroid Build Coastguard Worker data->state.aptr.accept_encoding =
370*6236dae4SAndroid Build Coastguard Worker aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]);
371*6236dae4SAndroid Build Coastguard Worker
372*6236dae4SAndroid Build Coastguard Worker if(!data->state.aptr.accept_encoding) {
373*6236dae4SAndroid Build Coastguard Worker result = CURLE_OUT_OF_MEMORY;
374*6236dae4SAndroid Build Coastguard Worker goto out;
375*6236dae4SAndroid Build Coastguard Worker }
376*6236dae4SAndroid Build Coastguard Worker p_accept_encoding = data->state.aptr.accept_encoding;
377*6236dae4SAndroid Build Coastguard Worker }
378*6236dae4SAndroid Build Coastguard Worker }
379*6236dae4SAndroid Build Coastguard Worker
380*6236dae4SAndroid Build Coastguard Worker /* The User-Agent string might have been allocated in url.c already, because
381*6236dae4SAndroid Build Coastguard Worker it might have been used in the proxy connect, but if we have got a header
382*6236dae4SAndroid Build Coastguard Worker with the user-agent string specified, we erase the previously made string
383*6236dae4SAndroid Build Coastguard Worker here. */
384*6236dae4SAndroid Build Coastguard Worker if(Curl_checkheaders(data, STRCONST("User-Agent")) &&
385*6236dae4SAndroid Build Coastguard Worker data->state.aptr.uagent) {
386*6236dae4SAndroid Build Coastguard Worker Curl_safefree(data->state.aptr.uagent);
387*6236dae4SAndroid Build Coastguard Worker }
388*6236dae4SAndroid Build Coastguard Worker else if(!Curl_checkheaders(data, STRCONST("User-Agent")) &&
389*6236dae4SAndroid Build Coastguard Worker data->set.str[STRING_USERAGENT]) {
390*6236dae4SAndroid Build Coastguard Worker p_uagent = data->state.aptr.uagent;
391*6236dae4SAndroid Build Coastguard Worker }
392*6236dae4SAndroid Build Coastguard Worker
393*6236dae4SAndroid Build Coastguard Worker /* setup the authentication headers */
394*6236dae4SAndroid Build Coastguard Worker result = Curl_http_output_auth(data, conn, p_request, HTTPREQ_GET,
395*6236dae4SAndroid Build Coastguard Worker p_stream_uri, FALSE);
396*6236dae4SAndroid Build Coastguard Worker if(result)
397*6236dae4SAndroid Build Coastguard Worker goto out;
398*6236dae4SAndroid Build Coastguard Worker
399*6236dae4SAndroid Build Coastguard Worker #ifndef CURL_DISABLE_PROXY
400*6236dae4SAndroid Build Coastguard Worker p_proxyuserpwd = data->state.aptr.proxyuserpwd;
401*6236dae4SAndroid Build Coastguard Worker #endif
402*6236dae4SAndroid Build Coastguard Worker p_userpwd = data->state.aptr.userpwd;
403*6236dae4SAndroid Build Coastguard Worker
404*6236dae4SAndroid Build Coastguard Worker /* Referrer */
405*6236dae4SAndroid Build Coastguard Worker Curl_safefree(data->state.aptr.ref);
406*6236dae4SAndroid Build Coastguard Worker if(data->state.referer && !Curl_checkheaders(data, STRCONST("Referer")))
407*6236dae4SAndroid Build Coastguard Worker data->state.aptr.ref = aprintf("Referer: %s\r\n", data->state.referer);
408*6236dae4SAndroid Build Coastguard Worker
409*6236dae4SAndroid Build Coastguard Worker p_referrer = data->state.aptr.ref;
410*6236dae4SAndroid Build Coastguard Worker
411*6236dae4SAndroid Build Coastguard Worker /*
412*6236dae4SAndroid Build Coastguard Worker * Range Header
413*6236dae4SAndroid Build Coastguard Worker * Only applies to PLAY, PAUSE, RECORD
414*6236dae4SAndroid Build Coastguard Worker *
415*6236dae4SAndroid Build Coastguard Worker * Go ahead and use the Range stuff supplied for HTTP
416*6236dae4SAndroid Build Coastguard Worker */
417*6236dae4SAndroid Build Coastguard Worker if(data->state.use_range &&
418*6236dae4SAndroid Build Coastguard Worker (rtspreq & (RTSPREQ_PLAY | RTSPREQ_PAUSE | RTSPREQ_RECORD))) {
419*6236dae4SAndroid Build Coastguard Worker
420*6236dae4SAndroid Build Coastguard Worker /* Check to see if there is a range set in the custom headers */
421*6236dae4SAndroid Build Coastguard Worker if(!Curl_checkheaders(data, STRCONST("Range")) && data->state.range) {
422*6236dae4SAndroid Build Coastguard Worker Curl_safefree(data->state.aptr.rangeline);
423*6236dae4SAndroid Build Coastguard Worker data->state.aptr.rangeline = aprintf("Range: %s\r\n", data->state.range);
424*6236dae4SAndroid Build Coastguard Worker p_range = data->state.aptr.rangeline;
425*6236dae4SAndroid Build Coastguard Worker }
426*6236dae4SAndroid Build Coastguard Worker }
427*6236dae4SAndroid Build Coastguard Worker
428*6236dae4SAndroid Build Coastguard Worker /*
429*6236dae4SAndroid Build Coastguard Worker * Sanity check the custom headers
430*6236dae4SAndroid Build Coastguard Worker */
431*6236dae4SAndroid Build Coastguard Worker if(Curl_checkheaders(data, STRCONST("CSeq"))) {
432*6236dae4SAndroid Build Coastguard Worker failf(data, "CSeq cannot be set as a custom header.");
433*6236dae4SAndroid Build Coastguard Worker result = CURLE_RTSP_CSEQ_ERROR;
434*6236dae4SAndroid Build Coastguard Worker goto out;
435*6236dae4SAndroid Build Coastguard Worker }
436*6236dae4SAndroid Build Coastguard Worker if(Curl_checkheaders(data, STRCONST("Session"))) {
437*6236dae4SAndroid Build Coastguard Worker failf(data, "Session ID cannot be set as a custom header.");
438*6236dae4SAndroid Build Coastguard Worker result = CURLE_BAD_FUNCTION_ARGUMENT;
439*6236dae4SAndroid Build Coastguard Worker goto out;
440*6236dae4SAndroid Build Coastguard Worker }
441*6236dae4SAndroid Build Coastguard Worker
442*6236dae4SAndroid Build Coastguard Worker result =
443*6236dae4SAndroid Build Coastguard Worker Curl_dyn_addf(&req_buffer,
444*6236dae4SAndroid Build Coastguard Worker "%s %s RTSP/1.0\r\n" /* Request Stream-URI RTSP/1.0 */
445*6236dae4SAndroid Build Coastguard Worker "CSeq: %ld\r\n", /* CSeq */
446*6236dae4SAndroid Build Coastguard Worker p_request, p_stream_uri, rtsp->CSeq_sent);
447*6236dae4SAndroid Build Coastguard Worker if(result)
448*6236dae4SAndroid Build Coastguard Worker goto out;
449*6236dae4SAndroid Build Coastguard Worker
450*6236dae4SAndroid Build Coastguard Worker /*
451*6236dae4SAndroid Build Coastguard Worker * Rather than do a normal alloc line, keep the session_id unformatted
452*6236dae4SAndroid Build Coastguard Worker * to make comparison easier
453*6236dae4SAndroid Build Coastguard Worker */
454*6236dae4SAndroid Build Coastguard Worker if(p_session_id) {
455*6236dae4SAndroid Build Coastguard Worker result = Curl_dyn_addf(&req_buffer, "Session: %s\r\n", p_session_id);
456*6236dae4SAndroid Build Coastguard Worker if(result)
457*6236dae4SAndroid Build Coastguard Worker goto out;
458*6236dae4SAndroid Build Coastguard Worker }
459*6236dae4SAndroid Build Coastguard Worker
460*6236dae4SAndroid Build Coastguard Worker /*
461*6236dae4SAndroid Build Coastguard Worker * Shared HTTP-like options
462*6236dae4SAndroid Build Coastguard Worker */
463*6236dae4SAndroid Build Coastguard Worker result = Curl_dyn_addf(&req_buffer,
464*6236dae4SAndroid Build Coastguard Worker "%s" /* transport */
465*6236dae4SAndroid Build Coastguard Worker "%s" /* accept */
466*6236dae4SAndroid Build Coastguard Worker "%s" /* accept-encoding */
467*6236dae4SAndroid Build Coastguard Worker "%s" /* range */
468*6236dae4SAndroid Build Coastguard Worker "%s" /* referrer */
469*6236dae4SAndroid Build Coastguard Worker "%s" /* user-agent */
470*6236dae4SAndroid Build Coastguard Worker "%s" /* proxyuserpwd */
471*6236dae4SAndroid Build Coastguard Worker "%s" /* userpwd */
472*6236dae4SAndroid Build Coastguard Worker ,
473*6236dae4SAndroid Build Coastguard Worker p_transport ? p_transport : "",
474*6236dae4SAndroid Build Coastguard Worker p_accept ? p_accept : "",
475*6236dae4SAndroid Build Coastguard Worker p_accept_encoding ? p_accept_encoding : "",
476*6236dae4SAndroid Build Coastguard Worker p_range ? p_range : "",
477*6236dae4SAndroid Build Coastguard Worker p_referrer ? p_referrer : "",
478*6236dae4SAndroid Build Coastguard Worker p_uagent ? p_uagent : "",
479*6236dae4SAndroid Build Coastguard Worker p_proxyuserpwd ? p_proxyuserpwd : "",
480*6236dae4SAndroid Build Coastguard Worker p_userpwd ? p_userpwd : "");
481*6236dae4SAndroid Build Coastguard Worker
482*6236dae4SAndroid Build Coastguard Worker /*
483*6236dae4SAndroid Build Coastguard Worker * Free userpwd now --- cannot reuse this for Negotiate and possibly NTLM
484*6236dae4SAndroid Build Coastguard Worker * with basic and digest, it will be freed anyway by the next request
485*6236dae4SAndroid Build Coastguard Worker */
486*6236dae4SAndroid Build Coastguard Worker Curl_safefree(data->state.aptr.userpwd);
487*6236dae4SAndroid Build Coastguard Worker
488*6236dae4SAndroid Build Coastguard Worker if(result)
489*6236dae4SAndroid Build Coastguard Worker goto out;
490*6236dae4SAndroid Build Coastguard Worker
491*6236dae4SAndroid Build Coastguard Worker if((rtspreq == RTSPREQ_SETUP) || (rtspreq == RTSPREQ_DESCRIBE)) {
492*6236dae4SAndroid Build Coastguard Worker result = Curl_add_timecondition(data, &req_buffer);
493*6236dae4SAndroid Build Coastguard Worker if(result)
494*6236dae4SAndroid Build Coastguard Worker goto out;
495*6236dae4SAndroid Build Coastguard Worker }
496*6236dae4SAndroid Build Coastguard Worker
497*6236dae4SAndroid Build Coastguard Worker result = Curl_add_custom_headers(data, FALSE, &req_buffer);
498*6236dae4SAndroid Build Coastguard Worker if(result)
499*6236dae4SAndroid Build Coastguard Worker goto out;
500*6236dae4SAndroid Build Coastguard Worker
501*6236dae4SAndroid Build Coastguard Worker if(rtspreq == RTSPREQ_ANNOUNCE ||
502*6236dae4SAndroid Build Coastguard Worker rtspreq == RTSPREQ_SET_PARAMETER ||
503*6236dae4SAndroid Build Coastguard Worker rtspreq == RTSPREQ_GET_PARAMETER) {
504*6236dae4SAndroid Build Coastguard Worker curl_off_t req_clen; /* request content length */
505*6236dae4SAndroid Build Coastguard Worker
506*6236dae4SAndroid Build Coastguard Worker if(data->state.upload) {
507*6236dae4SAndroid Build Coastguard Worker req_clen = data->state.infilesize;
508*6236dae4SAndroid Build Coastguard Worker data->state.httpreq = HTTPREQ_PUT;
509*6236dae4SAndroid Build Coastguard Worker result = Curl_creader_set_fread(data, req_clen);
510*6236dae4SAndroid Build Coastguard Worker if(result)
511*6236dae4SAndroid Build Coastguard Worker goto out;
512*6236dae4SAndroid Build Coastguard Worker }
513*6236dae4SAndroid Build Coastguard Worker else {
514*6236dae4SAndroid Build Coastguard Worker if(data->set.postfields) {
515*6236dae4SAndroid Build Coastguard Worker size_t plen = strlen(data->set.postfields);
516*6236dae4SAndroid Build Coastguard Worker req_clen = (curl_off_t)plen;
517*6236dae4SAndroid Build Coastguard Worker result = Curl_creader_set_buf(data, data->set.postfields, plen);
518*6236dae4SAndroid Build Coastguard Worker }
519*6236dae4SAndroid Build Coastguard Worker else if(data->state.infilesize >= 0) {
520*6236dae4SAndroid Build Coastguard Worker req_clen = data->state.infilesize;
521*6236dae4SAndroid Build Coastguard Worker result = Curl_creader_set_fread(data, req_clen);
522*6236dae4SAndroid Build Coastguard Worker }
523*6236dae4SAndroid Build Coastguard Worker else {
524*6236dae4SAndroid Build Coastguard Worker req_clen = 0;
525*6236dae4SAndroid Build Coastguard Worker result = Curl_creader_set_null(data);
526*6236dae4SAndroid Build Coastguard Worker }
527*6236dae4SAndroid Build Coastguard Worker if(result)
528*6236dae4SAndroid Build Coastguard Worker goto out;
529*6236dae4SAndroid Build Coastguard Worker }
530*6236dae4SAndroid Build Coastguard Worker
531*6236dae4SAndroid Build Coastguard Worker if(req_clen > 0) {
532*6236dae4SAndroid Build Coastguard Worker /* As stated in the http comments, it is probably not wise to
533*6236dae4SAndroid Build Coastguard Worker * actually set a custom Content-Length in the headers */
534*6236dae4SAndroid Build Coastguard Worker if(!Curl_checkheaders(data, STRCONST("Content-Length"))) {
535*6236dae4SAndroid Build Coastguard Worker result =
536*6236dae4SAndroid Build Coastguard Worker Curl_dyn_addf(&req_buffer, "Content-Length: %" FMT_OFF_T"\r\n",
537*6236dae4SAndroid Build Coastguard Worker req_clen);
538*6236dae4SAndroid Build Coastguard Worker if(result)
539*6236dae4SAndroid Build Coastguard Worker goto out;
540*6236dae4SAndroid Build Coastguard Worker }
541*6236dae4SAndroid Build Coastguard Worker
542*6236dae4SAndroid Build Coastguard Worker if(rtspreq == RTSPREQ_SET_PARAMETER ||
543*6236dae4SAndroid Build Coastguard Worker rtspreq == RTSPREQ_GET_PARAMETER) {
544*6236dae4SAndroid Build Coastguard Worker if(!Curl_checkheaders(data, STRCONST("Content-Type"))) {
545*6236dae4SAndroid Build Coastguard Worker result = Curl_dyn_addn(&req_buffer,
546*6236dae4SAndroid Build Coastguard Worker STRCONST("Content-Type: "
547*6236dae4SAndroid Build Coastguard Worker "text/parameters\r\n"));
548*6236dae4SAndroid Build Coastguard Worker if(result)
549*6236dae4SAndroid Build Coastguard Worker goto out;
550*6236dae4SAndroid Build Coastguard Worker }
551*6236dae4SAndroid Build Coastguard Worker }
552*6236dae4SAndroid Build Coastguard Worker
553*6236dae4SAndroid Build Coastguard Worker if(rtspreq == RTSPREQ_ANNOUNCE) {
554*6236dae4SAndroid Build Coastguard Worker if(!Curl_checkheaders(data, STRCONST("Content-Type"))) {
555*6236dae4SAndroid Build Coastguard Worker result = Curl_dyn_addn(&req_buffer,
556*6236dae4SAndroid Build Coastguard Worker STRCONST("Content-Type: "
557*6236dae4SAndroid Build Coastguard Worker "application/sdp\r\n"));
558*6236dae4SAndroid Build Coastguard Worker if(result)
559*6236dae4SAndroid Build Coastguard Worker goto out;
560*6236dae4SAndroid Build Coastguard Worker }
561*6236dae4SAndroid Build Coastguard Worker }
562*6236dae4SAndroid Build Coastguard Worker }
563*6236dae4SAndroid Build Coastguard Worker else if(rtspreq == RTSPREQ_GET_PARAMETER) {
564*6236dae4SAndroid Build Coastguard Worker /* Check for an empty GET_PARAMETER (heartbeat) request */
565*6236dae4SAndroid Build Coastguard Worker data->state.httpreq = HTTPREQ_HEAD;
566*6236dae4SAndroid Build Coastguard Worker data->req.no_body = TRUE;
567*6236dae4SAndroid Build Coastguard Worker }
568*6236dae4SAndroid Build Coastguard Worker }
569*6236dae4SAndroid Build Coastguard Worker else {
570*6236dae4SAndroid Build Coastguard Worker result = Curl_creader_set_null(data);
571*6236dae4SAndroid Build Coastguard Worker if(result)
572*6236dae4SAndroid Build Coastguard Worker goto out;
573*6236dae4SAndroid Build Coastguard Worker }
574*6236dae4SAndroid Build Coastguard Worker
575*6236dae4SAndroid Build Coastguard Worker /* Finish the request buffer */
576*6236dae4SAndroid Build Coastguard Worker result = Curl_dyn_addn(&req_buffer, STRCONST("\r\n"));
577*6236dae4SAndroid Build Coastguard Worker if(result)
578*6236dae4SAndroid Build Coastguard Worker goto out;
579*6236dae4SAndroid Build Coastguard Worker
580*6236dae4SAndroid Build Coastguard Worker Curl_xfer_setup1(data, CURL_XFER_SENDRECV, -1, TRUE);
581*6236dae4SAndroid Build Coastguard Worker
582*6236dae4SAndroid Build Coastguard Worker /* issue the request */
583*6236dae4SAndroid Build Coastguard Worker result = Curl_req_send(data, &req_buffer);
584*6236dae4SAndroid Build Coastguard Worker if(result) {
585*6236dae4SAndroid Build Coastguard Worker failf(data, "Failed sending RTSP request");
586*6236dae4SAndroid Build Coastguard Worker goto out;
587*6236dae4SAndroid Build Coastguard Worker }
588*6236dae4SAndroid Build Coastguard Worker
589*6236dae4SAndroid Build Coastguard Worker /* Increment the CSeq on success */
590*6236dae4SAndroid Build Coastguard Worker data->state.rtsp_next_client_CSeq++;
591*6236dae4SAndroid Build Coastguard Worker
592*6236dae4SAndroid Build Coastguard Worker if(data->req.writebytecount) {
593*6236dae4SAndroid Build Coastguard Worker /* if a request-body has been sent off, we make sure this progress is
594*6236dae4SAndroid Build Coastguard Worker noted properly */
595*6236dae4SAndroid Build Coastguard Worker Curl_pgrsSetUploadCounter(data, data->req.writebytecount);
596*6236dae4SAndroid Build Coastguard Worker if(Curl_pgrsUpdate(data))
597*6236dae4SAndroid Build Coastguard Worker result = CURLE_ABORTED_BY_CALLBACK;
598*6236dae4SAndroid Build Coastguard Worker }
599*6236dae4SAndroid Build Coastguard Worker out:
600*6236dae4SAndroid Build Coastguard Worker Curl_dyn_free(&req_buffer);
601*6236dae4SAndroid Build Coastguard Worker return result;
602*6236dae4SAndroid Build Coastguard Worker }
603*6236dae4SAndroid Build Coastguard Worker
604*6236dae4SAndroid Build Coastguard Worker /**
605*6236dae4SAndroid Build Coastguard Worker * write any BODY bytes missing to the client, ignore the rest.
606*6236dae4SAndroid Build Coastguard Worker */
rtp_write_body_junk(struct Curl_easy * data,const char * buf,size_t blen)607*6236dae4SAndroid Build Coastguard Worker static CURLcode rtp_write_body_junk(struct Curl_easy *data,
608*6236dae4SAndroid Build Coastguard Worker const char *buf,
609*6236dae4SAndroid Build Coastguard Worker size_t blen)
610*6236dae4SAndroid Build Coastguard Worker {
611*6236dae4SAndroid Build Coastguard Worker struct rtsp_conn *rtspc = &(data->conn->proto.rtspc);
612*6236dae4SAndroid Build Coastguard Worker curl_off_t body_remain;
613*6236dae4SAndroid Build Coastguard Worker bool in_body;
614*6236dae4SAndroid Build Coastguard Worker
615*6236dae4SAndroid Build Coastguard Worker in_body = (data->req.headerline && !rtspc->in_header) &&
616*6236dae4SAndroid Build Coastguard Worker (data->req.size >= 0) &&
617*6236dae4SAndroid Build Coastguard Worker (data->req.bytecount < data->req.size);
618*6236dae4SAndroid Build Coastguard Worker body_remain = in_body ? (data->req.size - data->req.bytecount) : 0;
619*6236dae4SAndroid Build Coastguard Worker DEBUGASSERT(body_remain >= 0);
620*6236dae4SAndroid Build Coastguard Worker if(body_remain) {
621*6236dae4SAndroid Build Coastguard Worker if((curl_off_t)blen > body_remain)
622*6236dae4SAndroid Build Coastguard Worker blen = (size_t)body_remain;
623*6236dae4SAndroid Build Coastguard Worker return Curl_client_write(data, CLIENTWRITE_BODY, (char *)buf, blen);
624*6236dae4SAndroid Build Coastguard Worker }
625*6236dae4SAndroid Build Coastguard Worker return CURLE_OK;
626*6236dae4SAndroid Build Coastguard Worker }
627*6236dae4SAndroid Build Coastguard Worker
rtsp_filter_rtp(struct Curl_easy * data,const char * buf,size_t blen,size_t * pconsumed)628*6236dae4SAndroid Build Coastguard Worker static CURLcode rtsp_filter_rtp(struct Curl_easy *data,
629*6236dae4SAndroid Build Coastguard Worker const char *buf,
630*6236dae4SAndroid Build Coastguard Worker size_t blen,
631*6236dae4SAndroid Build Coastguard Worker size_t *pconsumed)
632*6236dae4SAndroid Build Coastguard Worker {
633*6236dae4SAndroid Build Coastguard Worker struct rtsp_conn *rtspc = &(data->conn->proto.rtspc);
634*6236dae4SAndroid Build Coastguard Worker CURLcode result = CURLE_OK;
635*6236dae4SAndroid Build Coastguard Worker size_t skip_len = 0;
636*6236dae4SAndroid Build Coastguard Worker
637*6236dae4SAndroid Build Coastguard Worker *pconsumed = 0;
638*6236dae4SAndroid Build Coastguard Worker while(blen) {
639*6236dae4SAndroid Build Coastguard Worker bool in_body = (data->req.headerline && !rtspc->in_header) &&
640*6236dae4SAndroid Build Coastguard Worker (data->req.size >= 0) &&
641*6236dae4SAndroid Build Coastguard Worker (data->req.bytecount < data->req.size);
642*6236dae4SAndroid Build Coastguard Worker switch(rtspc->state) {
643*6236dae4SAndroid Build Coastguard Worker
644*6236dae4SAndroid Build Coastguard Worker case RTP_PARSE_SKIP: {
645*6236dae4SAndroid Build Coastguard Worker DEBUGASSERT(Curl_dyn_len(&rtspc->buf) == 0);
646*6236dae4SAndroid Build Coastguard Worker while(blen && buf[0] != '$') {
647*6236dae4SAndroid Build Coastguard Worker if(!in_body && buf[0] == 'R' &&
648*6236dae4SAndroid Build Coastguard Worker data->set.rtspreq != RTSPREQ_RECEIVE) {
649*6236dae4SAndroid Build Coastguard Worker if(strncmp(buf, "RTSP/", (blen < 5) ? blen : 5) == 0) {
650*6236dae4SAndroid Build Coastguard Worker /* This could be the next response, no consume and return */
651*6236dae4SAndroid Build Coastguard Worker if(*pconsumed) {
652*6236dae4SAndroid Build Coastguard Worker DEBUGF(infof(data, "RTP rtsp_filter_rtp[SKIP] RTSP/ prefix, "
653*6236dae4SAndroid Build Coastguard Worker "skipping %zd bytes of junk", *pconsumed));
654*6236dae4SAndroid Build Coastguard Worker }
655*6236dae4SAndroid Build Coastguard Worker rtspc->state = RTP_PARSE_SKIP;
656*6236dae4SAndroid Build Coastguard Worker rtspc->in_header = TRUE;
657*6236dae4SAndroid Build Coastguard Worker goto out;
658*6236dae4SAndroid Build Coastguard Worker }
659*6236dae4SAndroid Build Coastguard Worker }
660*6236dae4SAndroid Build Coastguard Worker /* junk/BODY, consume without buffering */
661*6236dae4SAndroid Build Coastguard Worker *pconsumed += 1;
662*6236dae4SAndroid Build Coastguard Worker ++buf;
663*6236dae4SAndroid Build Coastguard Worker --blen;
664*6236dae4SAndroid Build Coastguard Worker ++skip_len;
665*6236dae4SAndroid Build Coastguard Worker }
666*6236dae4SAndroid Build Coastguard Worker if(blen && buf[0] == '$') {
667*6236dae4SAndroid Build Coastguard Worker /* possible start of an RTP message, buffer */
668*6236dae4SAndroid Build Coastguard Worker if(skip_len) {
669*6236dae4SAndroid Build Coastguard Worker /* end of junk/BODY bytes, flush */
670*6236dae4SAndroid Build Coastguard Worker result = rtp_write_body_junk(data,
671*6236dae4SAndroid Build Coastguard Worker (char *)(buf - skip_len), skip_len);
672*6236dae4SAndroid Build Coastguard Worker skip_len = 0;
673*6236dae4SAndroid Build Coastguard Worker if(result)
674*6236dae4SAndroid Build Coastguard Worker goto out;
675*6236dae4SAndroid Build Coastguard Worker }
676*6236dae4SAndroid Build Coastguard Worker if(Curl_dyn_addn(&rtspc->buf, buf, 1)) {
677*6236dae4SAndroid Build Coastguard Worker result = CURLE_OUT_OF_MEMORY;
678*6236dae4SAndroid Build Coastguard Worker goto out;
679*6236dae4SAndroid Build Coastguard Worker }
680*6236dae4SAndroid Build Coastguard Worker *pconsumed += 1;
681*6236dae4SAndroid Build Coastguard Worker ++buf;
682*6236dae4SAndroid Build Coastguard Worker --blen;
683*6236dae4SAndroid Build Coastguard Worker rtspc->state = RTP_PARSE_CHANNEL;
684*6236dae4SAndroid Build Coastguard Worker }
685*6236dae4SAndroid Build Coastguard Worker break;
686*6236dae4SAndroid Build Coastguard Worker }
687*6236dae4SAndroid Build Coastguard Worker
688*6236dae4SAndroid Build Coastguard Worker case RTP_PARSE_CHANNEL: {
689*6236dae4SAndroid Build Coastguard Worker int idx = ((unsigned char)buf[0]) / 8;
690*6236dae4SAndroid Build Coastguard Worker int off = ((unsigned char)buf[0]) % 8;
691*6236dae4SAndroid Build Coastguard Worker DEBUGASSERT(Curl_dyn_len(&rtspc->buf) == 1);
692*6236dae4SAndroid Build Coastguard Worker if(!(data->state.rtp_channel_mask[idx] & (1 << off))) {
693*6236dae4SAndroid Build Coastguard Worker /* invalid channel number, junk or BODY data */
694*6236dae4SAndroid Build Coastguard Worker rtspc->state = RTP_PARSE_SKIP;
695*6236dae4SAndroid Build Coastguard Worker DEBUGASSERT(skip_len == 0);
696*6236dae4SAndroid Build Coastguard Worker /* we do not consume this byte, it is BODY data */
697*6236dae4SAndroid Build Coastguard Worker DEBUGF(infof(data, "RTSP: invalid RTP channel %d, skipping", idx));
698*6236dae4SAndroid Build Coastguard Worker if(*pconsumed == 0) {
699*6236dae4SAndroid Build Coastguard Worker /* We did not consume the initial '$' in our buffer, but had
700*6236dae4SAndroid Build Coastguard Worker * it from an earlier call. We cannot un-consume it and have
701*6236dae4SAndroid Build Coastguard Worker * to write it directly as BODY data */
702*6236dae4SAndroid Build Coastguard Worker result = rtp_write_body_junk(data, Curl_dyn_ptr(&rtspc->buf), 1);
703*6236dae4SAndroid Build Coastguard Worker if(result)
704*6236dae4SAndroid Build Coastguard Worker goto out;
705*6236dae4SAndroid Build Coastguard Worker }
706*6236dae4SAndroid Build Coastguard Worker else {
707*6236dae4SAndroid Build Coastguard Worker /* count the '$' as skip and continue */
708*6236dae4SAndroid Build Coastguard Worker skip_len = 1;
709*6236dae4SAndroid Build Coastguard Worker }
710*6236dae4SAndroid Build Coastguard Worker Curl_dyn_free(&rtspc->buf);
711*6236dae4SAndroid Build Coastguard Worker break;
712*6236dae4SAndroid Build Coastguard Worker }
713*6236dae4SAndroid Build Coastguard Worker /* a valid channel, so we expect this to be a real RTP message */
714*6236dae4SAndroid Build Coastguard Worker rtspc->rtp_channel = (unsigned char)buf[0];
715*6236dae4SAndroid Build Coastguard Worker if(Curl_dyn_addn(&rtspc->buf, buf, 1)) {
716*6236dae4SAndroid Build Coastguard Worker result = CURLE_OUT_OF_MEMORY;
717*6236dae4SAndroid Build Coastguard Worker goto out;
718*6236dae4SAndroid Build Coastguard Worker }
719*6236dae4SAndroid Build Coastguard Worker *pconsumed += 1;
720*6236dae4SAndroid Build Coastguard Worker ++buf;
721*6236dae4SAndroid Build Coastguard Worker --blen;
722*6236dae4SAndroid Build Coastguard Worker rtspc->state = RTP_PARSE_LEN;
723*6236dae4SAndroid Build Coastguard Worker break;
724*6236dae4SAndroid Build Coastguard Worker }
725*6236dae4SAndroid Build Coastguard Worker
726*6236dae4SAndroid Build Coastguard Worker case RTP_PARSE_LEN: {
727*6236dae4SAndroid Build Coastguard Worker size_t rtp_len = Curl_dyn_len(&rtspc->buf);
728*6236dae4SAndroid Build Coastguard Worker const char *rtp_buf;
729*6236dae4SAndroid Build Coastguard Worker DEBUGASSERT(rtp_len >= 2 && rtp_len < 4);
730*6236dae4SAndroid Build Coastguard Worker if(Curl_dyn_addn(&rtspc->buf, buf, 1)) {
731*6236dae4SAndroid Build Coastguard Worker result = CURLE_OUT_OF_MEMORY;
732*6236dae4SAndroid Build Coastguard Worker goto out;
733*6236dae4SAndroid Build Coastguard Worker }
734*6236dae4SAndroid Build Coastguard Worker *pconsumed += 1;
735*6236dae4SAndroid Build Coastguard Worker ++buf;
736*6236dae4SAndroid Build Coastguard Worker --blen;
737*6236dae4SAndroid Build Coastguard Worker if(rtp_len == 2)
738*6236dae4SAndroid Build Coastguard Worker break;
739*6236dae4SAndroid Build Coastguard Worker rtp_buf = Curl_dyn_ptr(&rtspc->buf);
740*6236dae4SAndroid Build Coastguard Worker rtspc->rtp_len = RTP_PKT_LENGTH(rtp_buf) + 4;
741*6236dae4SAndroid Build Coastguard Worker rtspc->state = RTP_PARSE_DATA;
742*6236dae4SAndroid Build Coastguard Worker break;
743*6236dae4SAndroid Build Coastguard Worker }
744*6236dae4SAndroid Build Coastguard Worker
745*6236dae4SAndroid Build Coastguard Worker case RTP_PARSE_DATA: {
746*6236dae4SAndroid Build Coastguard Worker size_t rtp_len = Curl_dyn_len(&rtspc->buf);
747*6236dae4SAndroid Build Coastguard Worker size_t needed;
748*6236dae4SAndroid Build Coastguard Worker DEBUGASSERT(rtp_len < rtspc->rtp_len);
749*6236dae4SAndroid Build Coastguard Worker needed = rtspc->rtp_len - rtp_len;
750*6236dae4SAndroid Build Coastguard Worker if(needed <= blen) {
751*6236dae4SAndroid Build Coastguard Worker if(Curl_dyn_addn(&rtspc->buf, buf, needed)) {
752*6236dae4SAndroid Build Coastguard Worker result = CURLE_OUT_OF_MEMORY;
753*6236dae4SAndroid Build Coastguard Worker goto out;
754*6236dae4SAndroid Build Coastguard Worker }
755*6236dae4SAndroid Build Coastguard Worker *pconsumed += needed;
756*6236dae4SAndroid Build Coastguard Worker buf += needed;
757*6236dae4SAndroid Build Coastguard Worker blen -= needed;
758*6236dae4SAndroid Build Coastguard Worker /* complete RTP message in buffer */
759*6236dae4SAndroid Build Coastguard Worker DEBUGF(infof(data, "RTP write channel %d rtp_len %zu",
760*6236dae4SAndroid Build Coastguard Worker rtspc->rtp_channel, rtspc->rtp_len));
761*6236dae4SAndroid Build Coastguard Worker result = rtp_client_write(data, Curl_dyn_ptr(&rtspc->buf),
762*6236dae4SAndroid Build Coastguard Worker rtspc->rtp_len);
763*6236dae4SAndroid Build Coastguard Worker Curl_dyn_free(&rtspc->buf);
764*6236dae4SAndroid Build Coastguard Worker rtspc->state = RTP_PARSE_SKIP;
765*6236dae4SAndroid Build Coastguard Worker if(result)
766*6236dae4SAndroid Build Coastguard Worker goto out;
767*6236dae4SAndroid Build Coastguard Worker }
768*6236dae4SAndroid Build Coastguard Worker else {
769*6236dae4SAndroid Build Coastguard Worker if(Curl_dyn_addn(&rtspc->buf, buf, blen)) {
770*6236dae4SAndroid Build Coastguard Worker result = CURLE_OUT_OF_MEMORY;
771*6236dae4SAndroid Build Coastguard Worker goto out;
772*6236dae4SAndroid Build Coastguard Worker }
773*6236dae4SAndroid Build Coastguard Worker *pconsumed += blen;
774*6236dae4SAndroid Build Coastguard Worker buf += blen;
775*6236dae4SAndroid Build Coastguard Worker blen = 0;
776*6236dae4SAndroid Build Coastguard Worker }
777*6236dae4SAndroid Build Coastguard Worker break;
778*6236dae4SAndroid Build Coastguard Worker }
779*6236dae4SAndroid Build Coastguard Worker
780*6236dae4SAndroid Build Coastguard Worker default:
781*6236dae4SAndroid Build Coastguard Worker DEBUGASSERT(0);
782*6236dae4SAndroid Build Coastguard Worker return CURLE_RECV_ERROR;
783*6236dae4SAndroid Build Coastguard Worker }
784*6236dae4SAndroid Build Coastguard Worker }
785*6236dae4SAndroid Build Coastguard Worker out:
786*6236dae4SAndroid Build Coastguard Worker if(!result && skip_len)
787*6236dae4SAndroid Build Coastguard Worker result = rtp_write_body_junk(data, (char *)(buf - skip_len), skip_len);
788*6236dae4SAndroid Build Coastguard Worker return result;
789*6236dae4SAndroid Build Coastguard Worker }
790*6236dae4SAndroid Build Coastguard Worker
rtsp_rtp_write_resp(struct Curl_easy * data,const char * buf,size_t blen,bool is_eos)791*6236dae4SAndroid Build Coastguard Worker static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data,
792*6236dae4SAndroid Build Coastguard Worker const char *buf,
793*6236dae4SAndroid Build Coastguard Worker size_t blen,
794*6236dae4SAndroid Build Coastguard Worker bool is_eos)
795*6236dae4SAndroid Build Coastguard Worker {
796*6236dae4SAndroid Build Coastguard Worker struct rtsp_conn *rtspc = &(data->conn->proto.rtspc);
797*6236dae4SAndroid Build Coastguard Worker CURLcode result = CURLE_OK;
798*6236dae4SAndroid Build Coastguard Worker size_t consumed = 0;
799*6236dae4SAndroid Build Coastguard Worker
800*6236dae4SAndroid Build Coastguard Worker if(!data->req.header)
801*6236dae4SAndroid Build Coastguard Worker rtspc->in_header = FALSE;
802*6236dae4SAndroid Build Coastguard Worker if(!blen) {
803*6236dae4SAndroid Build Coastguard Worker goto out;
804*6236dae4SAndroid Build Coastguard Worker }
805*6236dae4SAndroid Build Coastguard Worker
806*6236dae4SAndroid Build Coastguard Worker DEBUGF(infof(data, "rtsp_rtp_write_resp(len=%zu, in_header=%d, eos=%d)",
807*6236dae4SAndroid Build Coastguard Worker blen, rtspc->in_header, is_eos));
808*6236dae4SAndroid Build Coastguard Worker
809*6236dae4SAndroid Build Coastguard Worker /* If header parsing is not ongoing, extract RTP messages */
810*6236dae4SAndroid Build Coastguard Worker if(!rtspc->in_header) {
811*6236dae4SAndroid Build Coastguard Worker result = rtsp_filter_rtp(data, buf, blen, &consumed);
812*6236dae4SAndroid Build Coastguard Worker if(result)
813*6236dae4SAndroid Build Coastguard Worker goto out;
814*6236dae4SAndroid Build Coastguard Worker buf += consumed;
815*6236dae4SAndroid Build Coastguard Worker blen -= consumed;
816*6236dae4SAndroid Build Coastguard Worker /* either we consumed all or are at the start of header parsing */
817*6236dae4SAndroid Build Coastguard Worker if(blen && !data->req.header)
818*6236dae4SAndroid Build Coastguard Worker DEBUGF(infof(data, "RTSP: %zu bytes, possibly excess in response body",
819*6236dae4SAndroid Build Coastguard Worker blen));
820*6236dae4SAndroid Build Coastguard Worker }
821*6236dae4SAndroid Build Coastguard Worker
822*6236dae4SAndroid Build Coastguard Worker /* we want to parse headers, do so */
823*6236dae4SAndroid Build Coastguard Worker if(data->req.header && blen) {
824*6236dae4SAndroid Build Coastguard Worker rtspc->in_header = TRUE;
825*6236dae4SAndroid Build Coastguard Worker result = Curl_http_write_resp_hds(data, buf, blen, &consumed);
826*6236dae4SAndroid Build Coastguard Worker if(result)
827*6236dae4SAndroid Build Coastguard Worker goto out;
828*6236dae4SAndroid Build Coastguard Worker
829*6236dae4SAndroid Build Coastguard Worker buf += consumed;
830*6236dae4SAndroid Build Coastguard Worker blen -= consumed;
831*6236dae4SAndroid Build Coastguard Worker
832*6236dae4SAndroid Build Coastguard Worker if(!data->req.header)
833*6236dae4SAndroid Build Coastguard Worker rtspc->in_header = FALSE;
834*6236dae4SAndroid Build Coastguard Worker
835*6236dae4SAndroid Build Coastguard Worker if(!rtspc->in_header) {
836*6236dae4SAndroid Build Coastguard Worker /* If header parsing is done, extract interleaved RTP messages */
837*6236dae4SAndroid Build Coastguard Worker if(data->req.size <= -1) {
838*6236dae4SAndroid Build Coastguard Worker /* Respect section 4.4 of rfc2326: If the Content-Length header is
839*6236dae4SAndroid Build Coastguard Worker absent, a length 0 must be assumed. */
840*6236dae4SAndroid Build Coastguard Worker data->req.size = 0;
841*6236dae4SAndroid Build Coastguard Worker data->req.download_done = TRUE;
842*6236dae4SAndroid Build Coastguard Worker }
843*6236dae4SAndroid Build Coastguard Worker result = rtsp_filter_rtp(data, buf, blen, &consumed);
844*6236dae4SAndroid Build Coastguard Worker if(result)
845*6236dae4SAndroid Build Coastguard Worker goto out;
846*6236dae4SAndroid Build Coastguard Worker blen -= consumed;
847*6236dae4SAndroid Build Coastguard Worker }
848*6236dae4SAndroid Build Coastguard Worker }
849*6236dae4SAndroid Build Coastguard Worker
850*6236dae4SAndroid Build Coastguard Worker if(rtspc->state != RTP_PARSE_SKIP)
851*6236dae4SAndroid Build Coastguard Worker data->req.done = FALSE;
852*6236dae4SAndroid Build Coastguard Worker /* we SHOULD have consumed all bytes, unless the response is borked.
853*6236dae4SAndroid Build Coastguard Worker * In which case we write out the left over bytes, letting the client
854*6236dae4SAndroid Build Coastguard Worker * writer deal with it (it will report EXCESS and fail the transfer). */
855*6236dae4SAndroid Build Coastguard Worker DEBUGF(infof(data, "rtsp_rtp_write_resp(len=%zu, in_header=%d, done=%d "
856*6236dae4SAndroid Build Coastguard Worker " rtspc->state=%d, req.size=%" FMT_OFF_T ")",
857*6236dae4SAndroid Build Coastguard Worker blen, rtspc->in_header, data->req.done, rtspc->state,
858*6236dae4SAndroid Build Coastguard Worker data->req.size));
859*6236dae4SAndroid Build Coastguard Worker if(!result && (is_eos || blen)) {
860*6236dae4SAndroid Build Coastguard Worker result = Curl_client_write(data, CLIENTWRITE_BODY|
861*6236dae4SAndroid Build Coastguard Worker (is_eos ? CLIENTWRITE_EOS : 0),
862*6236dae4SAndroid Build Coastguard Worker (char *)buf, blen);
863*6236dae4SAndroid Build Coastguard Worker }
864*6236dae4SAndroid Build Coastguard Worker
865*6236dae4SAndroid Build Coastguard Worker out:
866*6236dae4SAndroid Build Coastguard Worker if((data->set.rtspreq == RTSPREQ_RECEIVE) &&
867*6236dae4SAndroid Build Coastguard Worker (rtspc->state == RTP_PARSE_SKIP)) {
868*6236dae4SAndroid Build Coastguard Worker /* In special mode RECEIVE, we just process one chunk of network
869*6236dae4SAndroid Build Coastguard Worker * data, so we stop the transfer here, if we have no incomplete
870*6236dae4SAndroid Build Coastguard Worker * RTP message pending. */
871*6236dae4SAndroid Build Coastguard Worker data->req.download_done = TRUE;
872*6236dae4SAndroid Build Coastguard Worker }
873*6236dae4SAndroid Build Coastguard Worker return result;
874*6236dae4SAndroid Build Coastguard Worker }
875*6236dae4SAndroid Build Coastguard Worker
876*6236dae4SAndroid Build Coastguard Worker static
rtp_client_write(struct Curl_easy * data,const char * ptr,size_t len)877*6236dae4SAndroid Build Coastguard Worker CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr, size_t len)
878*6236dae4SAndroid Build Coastguard Worker {
879*6236dae4SAndroid Build Coastguard Worker size_t wrote;
880*6236dae4SAndroid Build Coastguard Worker curl_write_callback writeit;
881*6236dae4SAndroid Build Coastguard Worker void *user_ptr;
882*6236dae4SAndroid Build Coastguard Worker
883*6236dae4SAndroid Build Coastguard Worker if(len == 0) {
884*6236dae4SAndroid Build Coastguard Worker failf(data, "Cannot write a 0 size RTP packet.");
885*6236dae4SAndroid Build Coastguard Worker return CURLE_WRITE_ERROR;
886*6236dae4SAndroid Build Coastguard Worker }
887*6236dae4SAndroid Build Coastguard Worker
888*6236dae4SAndroid Build Coastguard Worker /* If the user has configured CURLOPT_INTERLEAVEFUNCTION then use that
889*6236dae4SAndroid Build Coastguard Worker function and any configured CURLOPT_INTERLEAVEDATA to write out the RTP
890*6236dae4SAndroid Build Coastguard Worker data. Otherwise, use the CURLOPT_WRITEFUNCTION with the CURLOPT_WRITEDATA
891*6236dae4SAndroid Build Coastguard Worker pointer to write out the RTP data. */
892*6236dae4SAndroid Build Coastguard Worker if(data->set.fwrite_rtp) {
893*6236dae4SAndroid Build Coastguard Worker writeit = data->set.fwrite_rtp;
894*6236dae4SAndroid Build Coastguard Worker user_ptr = data->set.rtp_out;
895*6236dae4SAndroid Build Coastguard Worker }
896*6236dae4SAndroid Build Coastguard Worker else {
897*6236dae4SAndroid Build Coastguard Worker writeit = data->set.fwrite_func;
898*6236dae4SAndroid Build Coastguard Worker user_ptr = data->set.out;
899*6236dae4SAndroid Build Coastguard Worker }
900*6236dae4SAndroid Build Coastguard Worker
901*6236dae4SAndroid Build Coastguard Worker Curl_set_in_callback(data, TRUE);
902*6236dae4SAndroid Build Coastguard Worker wrote = writeit((char *)ptr, 1, len, user_ptr);
903*6236dae4SAndroid Build Coastguard Worker Curl_set_in_callback(data, FALSE);
904*6236dae4SAndroid Build Coastguard Worker
905*6236dae4SAndroid Build Coastguard Worker if(CURL_WRITEFUNC_PAUSE == wrote) {
906*6236dae4SAndroid Build Coastguard Worker failf(data, "Cannot pause RTP");
907*6236dae4SAndroid Build Coastguard Worker return CURLE_WRITE_ERROR;
908*6236dae4SAndroid Build Coastguard Worker }
909*6236dae4SAndroid Build Coastguard Worker
910*6236dae4SAndroid Build Coastguard Worker if(wrote != len) {
911*6236dae4SAndroid Build Coastguard Worker failf(data, "Failed writing RTP data");
912*6236dae4SAndroid Build Coastguard Worker return CURLE_WRITE_ERROR;
913*6236dae4SAndroid Build Coastguard Worker }
914*6236dae4SAndroid Build Coastguard Worker
915*6236dae4SAndroid Build Coastguard Worker return CURLE_OK;
916*6236dae4SAndroid Build Coastguard Worker }
917*6236dae4SAndroid Build Coastguard Worker
Curl_rtsp_parseheader(struct Curl_easy * data,const char * header)918*6236dae4SAndroid Build Coastguard Worker CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, const char *header)
919*6236dae4SAndroid Build Coastguard Worker {
920*6236dae4SAndroid Build Coastguard Worker if(checkprefix("CSeq:", header)) {
921*6236dae4SAndroid Build Coastguard Worker long CSeq = 0;
922*6236dae4SAndroid Build Coastguard Worker char *endp;
923*6236dae4SAndroid Build Coastguard Worker const char *p = &header[5];
924*6236dae4SAndroid Build Coastguard Worker while(ISBLANK(*p))
925*6236dae4SAndroid Build Coastguard Worker p++;
926*6236dae4SAndroid Build Coastguard Worker CSeq = strtol(p, &endp, 10);
927*6236dae4SAndroid Build Coastguard Worker if(p != endp) {
928*6236dae4SAndroid Build Coastguard Worker struct RTSP *rtsp = data->req.p.rtsp;
929*6236dae4SAndroid Build Coastguard Worker rtsp->CSeq_recv = CSeq; /* mark the request */
930*6236dae4SAndroid Build Coastguard Worker data->state.rtsp_CSeq_recv = CSeq; /* update the handle */
931*6236dae4SAndroid Build Coastguard Worker }
932*6236dae4SAndroid Build Coastguard Worker else {
933*6236dae4SAndroid Build Coastguard Worker failf(data, "Unable to read the CSeq header: [%s]", header);
934*6236dae4SAndroid Build Coastguard Worker return CURLE_RTSP_CSEQ_ERROR;
935*6236dae4SAndroid Build Coastguard Worker }
936*6236dae4SAndroid Build Coastguard Worker }
937*6236dae4SAndroid Build Coastguard Worker else if(checkprefix("Session:", header)) {
938*6236dae4SAndroid Build Coastguard Worker const char *start, *end;
939*6236dae4SAndroid Build Coastguard Worker size_t idlen;
940*6236dae4SAndroid Build Coastguard Worker
941*6236dae4SAndroid Build Coastguard Worker /* Find the first non-space letter */
942*6236dae4SAndroid Build Coastguard Worker start = header + 8;
943*6236dae4SAndroid Build Coastguard Worker while(*start && ISBLANK(*start))
944*6236dae4SAndroid Build Coastguard Worker start++;
945*6236dae4SAndroid Build Coastguard Worker
946*6236dae4SAndroid Build Coastguard Worker if(!*start) {
947*6236dae4SAndroid Build Coastguard Worker failf(data, "Got a blank Session ID");
948*6236dae4SAndroid Build Coastguard Worker return CURLE_RTSP_SESSION_ERROR;
949*6236dae4SAndroid Build Coastguard Worker }
950*6236dae4SAndroid Build Coastguard Worker
951*6236dae4SAndroid Build Coastguard Worker /* Find the end of Session ID
952*6236dae4SAndroid Build Coastguard Worker *
953*6236dae4SAndroid Build Coastguard Worker * Allow any non whitespace content, up to the field separator or end of
954*6236dae4SAndroid Build Coastguard Worker * line. RFC 2326 is not 100% clear on the session ID and for example
955*6236dae4SAndroid Build Coastguard Worker * gstreamer does url-encoded session ID's not covered by the standard.
956*6236dae4SAndroid Build Coastguard Worker */
957*6236dae4SAndroid Build Coastguard Worker end = start;
958*6236dae4SAndroid Build Coastguard Worker while(*end && *end != ';' && !ISSPACE(*end))
959*6236dae4SAndroid Build Coastguard Worker end++;
960*6236dae4SAndroid Build Coastguard Worker idlen = end - start;
961*6236dae4SAndroid Build Coastguard Worker
962*6236dae4SAndroid Build Coastguard Worker if(data->set.str[STRING_RTSP_SESSION_ID]) {
963*6236dae4SAndroid Build Coastguard Worker
964*6236dae4SAndroid Build Coastguard Worker /* If the Session ID is set, then compare */
965*6236dae4SAndroid Build Coastguard Worker if(strlen(data->set.str[STRING_RTSP_SESSION_ID]) != idlen ||
966*6236dae4SAndroid Build Coastguard Worker strncmp(start, data->set.str[STRING_RTSP_SESSION_ID], idlen)) {
967*6236dae4SAndroid Build Coastguard Worker failf(data, "Got RTSP Session ID Line [%s], but wanted ID [%s]",
968*6236dae4SAndroid Build Coastguard Worker start, data->set.str[STRING_RTSP_SESSION_ID]);
969*6236dae4SAndroid Build Coastguard Worker return CURLE_RTSP_SESSION_ERROR;
970*6236dae4SAndroid Build Coastguard Worker }
971*6236dae4SAndroid Build Coastguard Worker }
972*6236dae4SAndroid Build Coastguard Worker else {
973*6236dae4SAndroid Build Coastguard Worker /* If the Session ID is not set, and we find it in a response, then set
974*6236dae4SAndroid Build Coastguard Worker * it.
975*6236dae4SAndroid Build Coastguard Worker */
976*6236dae4SAndroid Build Coastguard Worker
977*6236dae4SAndroid Build Coastguard Worker /* Copy the id substring into a new buffer */
978*6236dae4SAndroid Build Coastguard Worker data->set.str[STRING_RTSP_SESSION_ID] = Curl_memdup0(start, idlen);
979*6236dae4SAndroid Build Coastguard Worker if(!data->set.str[STRING_RTSP_SESSION_ID])
980*6236dae4SAndroid Build Coastguard Worker return CURLE_OUT_OF_MEMORY;
981*6236dae4SAndroid Build Coastguard Worker }
982*6236dae4SAndroid Build Coastguard Worker }
983*6236dae4SAndroid Build Coastguard Worker else if(checkprefix("Transport:", header)) {
984*6236dae4SAndroid Build Coastguard Worker CURLcode result;
985*6236dae4SAndroid Build Coastguard Worker result = rtsp_parse_transport(data, header + 10);
986*6236dae4SAndroid Build Coastguard Worker if(result)
987*6236dae4SAndroid Build Coastguard Worker return result;
988*6236dae4SAndroid Build Coastguard Worker }
989*6236dae4SAndroid Build Coastguard Worker return CURLE_OK;
990*6236dae4SAndroid Build Coastguard Worker }
991*6236dae4SAndroid Build Coastguard Worker
992*6236dae4SAndroid Build Coastguard Worker static
rtsp_parse_transport(struct Curl_easy * data,const char * transport)993*6236dae4SAndroid Build Coastguard Worker CURLcode rtsp_parse_transport(struct Curl_easy *data, const char *transport)
994*6236dae4SAndroid Build Coastguard Worker {
995*6236dae4SAndroid Build Coastguard Worker /* If we receive multiple Transport response-headers, the linterleaved
996*6236dae4SAndroid Build Coastguard Worker channels of each response header is recorded and used together for
997*6236dae4SAndroid Build Coastguard Worker subsequent data validity checks.*/
998*6236dae4SAndroid Build Coastguard Worker /* e.g.: ' RTP/AVP/TCP;unicast;interleaved=5-6' */
999*6236dae4SAndroid Build Coastguard Worker const char *start, *end;
1000*6236dae4SAndroid Build Coastguard Worker start = transport;
1001*6236dae4SAndroid Build Coastguard Worker while(start && *start) {
1002*6236dae4SAndroid Build Coastguard Worker while(*start && ISBLANK(*start) )
1003*6236dae4SAndroid Build Coastguard Worker start++;
1004*6236dae4SAndroid Build Coastguard Worker end = strchr(start, ';');
1005*6236dae4SAndroid Build Coastguard Worker if(checkprefix("interleaved=", start)) {
1006*6236dae4SAndroid Build Coastguard Worker long chan1, chan2, chan;
1007*6236dae4SAndroid Build Coastguard Worker char *endp;
1008*6236dae4SAndroid Build Coastguard Worker const char *p = start + 12;
1009*6236dae4SAndroid Build Coastguard Worker chan1 = strtol(p, &endp, 10);
1010*6236dae4SAndroid Build Coastguard Worker if(p != endp && chan1 >= 0 && chan1 <= 255) {
1011*6236dae4SAndroid Build Coastguard Worker unsigned char *rtp_channel_mask = data->state.rtp_channel_mask;
1012*6236dae4SAndroid Build Coastguard Worker chan2 = chan1;
1013*6236dae4SAndroid Build Coastguard Worker if(*endp == '-') {
1014*6236dae4SAndroid Build Coastguard Worker p = endp + 1;
1015*6236dae4SAndroid Build Coastguard Worker chan2 = strtol(p, &endp, 10);
1016*6236dae4SAndroid Build Coastguard Worker if(p == endp || chan2 < 0 || chan2 > 255) {
1017*6236dae4SAndroid Build Coastguard Worker infof(data, "Unable to read the interleaved parameter from "
1018*6236dae4SAndroid Build Coastguard Worker "Transport header: [%s]", transport);
1019*6236dae4SAndroid Build Coastguard Worker chan2 = chan1;
1020*6236dae4SAndroid Build Coastguard Worker }
1021*6236dae4SAndroid Build Coastguard Worker }
1022*6236dae4SAndroid Build Coastguard Worker for(chan = chan1; chan <= chan2; chan++) {
1023*6236dae4SAndroid Build Coastguard Worker long idx = chan / 8;
1024*6236dae4SAndroid Build Coastguard Worker long off = chan % 8;
1025*6236dae4SAndroid Build Coastguard Worker rtp_channel_mask[idx] |= (unsigned char)(1 << off);
1026*6236dae4SAndroid Build Coastguard Worker }
1027*6236dae4SAndroid Build Coastguard Worker }
1028*6236dae4SAndroid Build Coastguard Worker else {
1029*6236dae4SAndroid Build Coastguard Worker infof(data, "Unable to read the interleaved parameter from "
1030*6236dae4SAndroid Build Coastguard Worker "Transport header: [%s]", transport);
1031*6236dae4SAndroid Build Coastguard Worker }
1032*6236dae4SAndroid Build Coastguard Worker break;
1033*6236dae4SAndroid Build Coastguard Worker }
1034*6236dae4SAndroid Build Coastguard Worker /* skip to next parameter */
1035*6236dae4SAndroid Build Coastguard Worker start = (!end) ? end : (end + 1);
1036*6236dae4SAndroid Build Coastguard Worker }
1037*6236dae4SAndroid Build Coastguard Worker return CURLE_OK;
1038*6236dae4SAndroid Build Coastguard Worker }
1039*6236dae4SAndroid Build Coastguard Worker
1040*6236dae4SAndroid Build Coastguard Worker
1041*6236dae4SAndroid Build Coastguard Worker #endif /* CURL_DISABLE_RTSP or using Hyper */
1042