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