"Fossies" - the Fresh Open Source Software Archive

Member "curl-7.66.0/lib/rtsp.c" (9 Sep 2019, 25485 Bytes) of package /linux/www/curl-7.66.0.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "rtsp.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 7.65.3_vs_7.66.0.

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