VirtualBox

source: vbox/trunk/src/libs/curl-8.7.1/lib/rtsp.c@ 106165

最後變更 在這個檔案從106165是 104083,由 vboxsync 提交於 8 月 前

curl-8.7.1: Applied and adjusted our curl changes to 8.4.0. bugref:10639

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette