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