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


rc_tagdetect_client
Author(s): Monika Florek-Jasinska , Raphael Schaller
autogenerated on Sat Feb 13 2021 03:42:16