rtsp.c
Go to the documentation of this file.
00001 /***************************************************************************
00002  *                                  _   _ ____  _
00003  *  Project                     ___| | | |  _ \| |
00004  *                             / __| | | | |_) | |
00005  *                            | (__| |_| |  _ <| |___
00006  *                             \___|\___/|_| \_\_____|
00007  *
00008  * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
00009  *
00010  * This software is licensed as described in the file COPYING, which
00011  * you should have received as part of this distribution. The terms
00012  * are also available at https://curl.haxx.se/docs/copyright.html.
00013  *
00014  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
00015  * copies of the Software, and permit persons to whom the Software is
00016  * furnished to do so, under the terms of the COPYING file.
00017  *
00018  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
00019  * KIND, either express or implied.
00020  *
00021  ***************************************************************************/
00022 
00023 #include "curl_setup.h"
00024 
00025 #ifndef CURL_DISABLE_RTSP
00026 
00027 #include "urldata.h"
00028 #include <curl/curl.h>
00029 #include "transfer.h"
00030 #include "sendf.h"
00031 #include "multiif.h"
00032 #include "http.h"
00033 #include "url.h"
00034 #include "progress.h"
00035 #include "rtsp.h"
00036 #include "strcase.h"
00037 #include "select.h"
00038 #include "connect.h"
00039 #include "strdup.h"
00040 /* The last 3 #include files should be in this order */
00041 #include "curl_printf.h"
00042 #include "curl_memory.h"
00043 #include "memdebug.h"
00044 
00045 /*
00046  * TODO (general)
00047  *  -incoming server requests
00048  *      -server CSeq counter
00049  *  -digest authentication
00050  *  -connect thru proxy
00051  *  -pipelining?
00052  */
00053 
00054 
00055 #define RTP_PKT_CHANNEL(p)   ((int)((unsigned char)((p)[1])))
00056 
00057 #define RTP_PKT_LENGTH(p)  ((((int)((unsigned char)((p)[2]))) << 8) | \
00058                              ((int)((unsigned char)((p)[3]))))
00059 
00060 /* protocol-specific functions set up to be called by the main engine */
00061 static CURLcode rtsp_do(struct connectdata *conn, bool *done);
00062 static CURLcode rtsp_done(struct connectdata *conn, CURLcode, bool premature);
00063 static CURLcode rtsp_connect(struct connectdata *conn, bool *done);
00064 static CURLcode rtsp_disconnect(struct connectdata *conn, bool dead);
00065 
00066 static int rtsp_getsock_do(struct connectdata *conn,
00067                            curl_socket_t *socks,
00068                            int numsocks);
00069 
00070 /*
00071  * Parse and write out any available RTP data.
00072  *
00073  * nread: amount of data left after k->str. will be modified if RTP
00074  *        data is parsed and k->str is moved up
00075  * readmore: whether or not the RTP parser needs more data right away
00076  */
00077 static CURLcode rtsp_rtp_readwrite(struct Curl_easy *data,
00078                                    struct connectdata *conn,
00079                                    ssize_t *nread,
00080                                    bool *readmore);
00081 
00082 static CURLcode rtsp_setup_connection(struct connectdata *conn);
00083 
00084 
00085 /* this returns the socket to wait for in the DO and DOING state for the multi
00086    interface and then we're always _sending_ a request and thus we wait for
00087    the single socket to become writable only */
00088 static int rtsp_getsock_do(struct connectdata *conn,
00089                            curl_socket_t *socks,
00090                            int numsocks)
00091 {
00092   /* write mode */
00093   (void)numsocks; /* unused, we trust it to be at least 1 */
00094   socks[0] = conn->sock[FIRSTSOCKET];
00095   return GETSOCK_WRITESOCK(0);
00096 }
00097 
00098 static
00099 CURLcode rtp_client_write(struct connectdata *conn, char *ptr, size_t len);
00100 
00101 
00102 /*
00103  * RTSP handler interface.
00104  */
00105 const struct Curl_handler Curl_handler_rtsp = {
00106   "RTSP",                               /* scheme */
00107   rtsp_setup_connection,                /* setup_connection */
00108   rtsp_do,                              /* do_it */
00109   rtsp_done,                            /* done */
00110   ZERO_NULL,                            /* do_more */
00111   rtsp_connect,                         /* connect_it */
00112   ZERO_NULL,                            /* connecting */
00113   ZERO_NULL,                            /* doing */
00114   ZERO_NULL,                            /* proto_getsock */
00115   rtsp_getsock_do,                      /* doing_getsock */
00116   ZERO_NULL,                            /* domore_getsock */
00117   ZERO_NULL,                            /* perform_getsock */
00118   rtsp_disconnect,                      /* disconnect */
00119   rtsp_rtp_readwrite,                   /* readwrite */
00120   PORT_RTSP,                            /* defport */
00121   CURLPROTO_RTSP,                       /* protocol */
00122   PROTOPT_NONE                          /* flags */
00123 };
00124 
00125 
00126 static CURLcode rtsp_setup_connection(struct connectdata *conn)
00127 {
00128   struct RTSP *rtsp;
00129 
00130   conn->data->req.protop = rtsp = calloc(1, sizeof(struct RTSP));
00131   if(!rtsp)
00132     return CURLE_OUT_OF_MEMORY;
00133 
00134   return CURLE_OK;
00135 }
00136 
00137 
00138 /*
00139  * The server may send us RTP data at any point, and RTSPREQ_RECEIVE does not
00140  * want to block the application forever while receiving a stream. Therefore,
00141  * we cannot assume that an RTSP socket is dead just because it is readable.
00142  *
00143  * Instead, if it is readable, run Curl_connalive() to peek at the socket
00144  * and distinguish between closed and data.
00145  */
00146 bool Curl_rtsp_connisdead(struct connectdata *check)
00147 {
00148   int sval;
00149   bool ret_val = TRUE;
00150 
00151   sval = SOCKET_READABLE(check->sock[FIRSTSOCKET], 0);
00152   if(sval == 0) {
00153     /* timeout */
00154     ret_val = FALSE;
00155   }
00156   else if(sval & CURL_CSELECT_ERR) {
00157     /* socket is in an error state */
00158     ret_val = TRUE;
00159   }
00160   else if(sval & CURL_CSELECT_IN) {
00161     /* readable with no error. could still be closed */
00162     ret_val = !Curl_connalive(check);
00163   }
00164 
00165   return ret_val;
00166 }
00167 
00168 static CURLcode rtsp_connect(struct connectdata *conn, bool *done)
00169 {
00170   CURLcode httpStatus;
00171   struct Curl_easy *data = conn->data;
00172 
00173   httpStatus = Curl_http_connect(conn, done);
00174 
00175   /* Initialize the CSeq if not already done */
00176   if(data->state.rtsp_next_client_CSeq == 0)
00177     data->state.rtsp_next_client_CSeq = 1;
00178   if(data->state.rtsp_next_server_CSeq == 0)
00179     data->state.rtsp_next_server_CSeq = 1;
00180 
00181   conn->proto.rtspc.rtp_channel = -1;
00182 
00183   return httpStatus;
00184 }
00185 
00186 static CURLcode rtsp_disconnect(struct connectdata *conn, bool dead)
00187 {
00188   (void) dead;
00189   Curl_safefree(conn->proto.rtspc.rtp_buf);
00190   return CURLE_OK;
00191 }
00192 
00193 
00194 static CURLcode rtsp_done(struct connectdata *conn,
00195                           CURLcode status, bool premature)
00196 {
00197   struct Curl_easy *data = conn->data;
00198   struct RTSP *rtsp = data->req.protop;
00199   CURLcode httpStatus;
00200   long CSeq_sent;
00201   long CSeq_recv;
00202 
00203   /* Bypass HTTP empty-reply checks on receive */
00204   if(data->set.rtspreq == RTSPREQ_RECEIVE)
00205     premature = TRUE;
00206 
00207   httpStatus = Curl_http_done(conn, status, premature);
00208 
00209   if(rtsp) {
00210     /* Check the sequence numbers */
00211     CSeq_sent = rtsp->CSeq_sent;
00212     CSeq_recv = rtsp->CSeq_recv;
00213     if((data->set.rtspreq != RTSPREQ_RECEIVE) && (CSeq_sent != CSeq_recv)) {
00214       failf(data,
00215             "The CSeq of this request %ld did not match the response %ld",
00216             CSeq_sent, CSeq_recv);
00217       return CURLE_RTSP_CSEQ_ERROR;
00218     }
00219     else if(data->set.rtspreq == RTSPREQ_RECEIVE &&
00220             (conn->proto.rtspc.rtp_channel == -1)) {
00221       infof(data, "Got an RTP Receive with a CSeq of %ld\n", CSeq_recv);
00222       /* TODO CPC: Server -> Client logic here */
00223     }
00224   }
00225 
00226   return httpStatus;
00227 }
00228 
00229 static CURLcode rtsp_do(struct connectdata *conn, bool *done)
00230 {
00231   struct Curl_easy *data = conn->data;
00232   CURLcode result=CURLE_OK;
00233   Curl_RtspReq rtspreq = data->set.rtspreq;
00234   struct RTSP *rtsp = data->req.protop;
00235   struct HTTP *http;
00236   Curl_send_buffer *req_buffer;
00237   curl_off_t postsize = 0; /* for ANNOUNCE and SET_PARAMETER */
00238   curl_off_t putsize = 0; /* for ANNOUNCE and SET_PARAMETER */
00239 
00240   const char *p_request = NULL;
00241   const char *p_session_id = NULL;
00242   const char *p_accept = NULL;
00243   const char *p_accept_encoding = NULL;
00244   const char *p_range = NULL;
00245   const char *p_referrer = NULL;
00246   const char *p_stream_uri = NULL;
00247   const char *p_transport = NULL;
00248   const char *p_uagent = NULL;
00249   const char *p_proxyuserpwd = NULL;
00250   const char *p_userpwd = NULL;
00251 
00252   *done = TRUE;
00253 
00254   http = &(rtsp->http_wrapper);
00255   /* Assert that no one has changed the RTSP struct in an evil way */
00256   DEBUGASSERT((void *)http == (void *)rtsp);
00257 
00258   rtsp->CSeq_sent = data->state.rtsp_next_client_CSeq;
00259   rtsp->CSeq_recv = 0;
00260 
00261   /* Setup the 'p_request' pointer to the proper p_request string
00262    * Since all RTSP requests are included here, there is no need to
00263    * support custom requests like HTTP.
00264    **/
00265   data->set.opt_no_body = TRUE; /* most requests don't contain a body */
00266   switch(rtspreq) {
00267   default:
00268     failf(data, "Got invalid RTSP request");
00269     return CURLE_BAD_FUNCTION_ARGUMENT;
00270   case RTSPREQ_OPTIONS:
00271     p_request = "OPTIONS";
00272     break;
00273   case RTSPREQ_DESCRIBE:
00274     p_request = "DESCRIBE";
00275     data->set.opt_no_body = FALSE;
00276     break;
00277   case RTSPREQ_ANNOUNCE:
00278     p_request = "ANNOUNCE";
00279     break;
00280   case RTSPREQ_SETUP:
00281     p_request = "SETUP";
00282     break;
00283   case RTSPREQ_PLAY:
00284     p_request = "PLAY";
00285     break;
00286   case RTSPREQ_PAUSE:
00287     p_request = "PAUSE";
00288     break;
00289   case RTSPREQ_TEARDOWN:
00290     p_request = "TEARDOWN";
00291     break;
00292   case RTSPREQ_GET_PARAMETER:
00293     /* GET_PARAMETER's no_body status is determined later */
00294     p_request = "GET_PARAMETER";
00295     data->set.opt_no_body = FALSE;
00296     break;
00297   case RTSPREQ_SET_PARAMETER:
00298     p_request = "SET_PARAMETER";
00299     break;
00300   case RTSPREQ_RECORD:
00301     p_request = "RECORD";
00302     break;
00303   case RTSPREQ_RECEIVE:
00304     p_request = "";
00305     /* Treat interleaved RTP as body*/
00306     data->set.opt_no_body = FALSE;
00307     break;
00308   case RTSPREQ_LAST:
00309     failf(data, "Got invalid RTSP request: RTSPREQ_LAST");
00310     return CURLE_BAD_FUNCTION_ARGUMENT;
00311   }
00312 
00313   if(rtspreq == RTSPREQ_RECEIVE) {
00314     Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
00315                         &http->readbytecount, -1, NULL);
00316 
00317     return result;
00318   }
00319 
00320   p_session_id = data->set.str[STRING_RTSP_SESSION_ID];
00321   if(!p_session_id &&
00322      (rtspreq & ~(RTSPREQ_OPTIONS | RTSPREQ_DESCRIBE | RTSPREQ_SETUP))) {
00323     failf(data, "Refusing to issue an RTSP request [%s] without a session ID.",
00324           p_request);
00325     return CURLE_BAD_FUNCTION_ARGUMENT;
00326   }
00327 
00328   /* TODO: proxy? */
00329 
00330   /* Stream URI. Default to server '*' if not specified */
00331   if(data->set.str[STRING_RTSP_STREAM_URI]) {
00332     p_stream_uri = data->set.str[STRING_RTSP_STREAM_URI];
00333   }
00334   else {
00335     p_stream_uri = "*";
00336   }
00337 
00338   /* Transport Header for SETUP requests */
00339   p_transport = Curl_checkheaders(conn, "Transport:");
00340   if(rtspreq == RTSPREQ_SETUP && !p_transport) {
00341     /* New Transport: setting? */
00342     if(data->set.str[STRING_RTSP_TRANSPORT]) {
00343       Curl_safefree(conn->allocptr.rtsp_transport);
00344 
00345       conn->allocptr.rtsp_transport =
00346         aprintf("Transport: %s\r\n",
00347                 data->set.str[STRING_RTSP_TRANSPORT]);
00348       if(!conn->allocptr.rtsp_transport)
00349         return CURLE_OUT_OF_MEMORY;
00350     }
00351     else {
00352       failf(data,
00353             "Refusing to issue an RTSP SETUP without a Transport: header.");
00354       return CURLE_BAD_FUNCTION_ARGUMENT;
00355     }
00356 
00357     p_transport = conn->allocptr.rtsp_transport;
00358   }
00359 
00360   /* Accept Headers for DESCRIBE requests */
00361   if(rtspreq == RTSPREQ_DESCRIBE) {
00362     /* Accept Header */
00363     p_accept = Curl_checkheaders(conn, "Accept:")?
00364       NULL:"Accept: application/sdp\r\n";
00365 
00366     /* Accept-Encoding header */
00367     if(!Curl_checkheaders(conn, "Accept-Encoding:") &&
00368        data->set.str[STRING_ENCODING]) {
00369       Curl_safefree(conn->allocptr.accept_encoding);
00370       conn->allocptr.accept_encoding =
00371         aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]);
00372 
00373       if(!conn->allocptr.accept_encoding)
00374         return CURLE_OUT_OF_MEMORY;
00375 
00376       p_accept_encoding = conn->allocptr.accept_encoding;
00377     }
00378   }
00379 
00380   /* The User-Agent string might have been allocated in url.c already, because
00381      it might have been used in the proxy connect, but if we have got a header
00382      with the user-agent string specified, we erase the previously made string
00383      here. */
00384   if(Curl_checkheaders(conn, "User-Agent:") && conn->allocptr.uagent) {
00385     Curl_safefree(conn->allocptr.uagent);
00386     conn->allocptr.uagent = NULL;
00387   }
00388   else if(!Curl_checkheaders(conn, "User-Agent:") &&
00389           data->set.str[STRING_USERAGENT]) {
00390     p_uagent = conn->allocptr.uagent;
00391   }
00392 
00393   /* setup the authentication headers */
00394   result = Curl_http_output_auth(conn, p_request, p_stream_uri, FALSE);
00395   if(result)
00396     return result;
00397 
00398   p_proxyuserpwd = conn->allocptr.proxyuserpwd;
00399   p_userpwd = conn->allocptr.userpwd;
00400 
00401   /* Referrer */
00402   Curl_safefree(conn->allocptr.ref);
00403   if(data->change.referer && !Curl_checkheaders(conn, "Referer:"))
00404     conn->allocptr.ref = aprintf("Referer: %s\r\n", data->change.referer);
00405   else
00406     conn->allocptr.ref = NULL;
00407 
00408   p_referrer = conn->allocptr.ref;
00409 
00410   /*
00411    * Range Header
00412    * Only applies to PLAY, PAUSE, RECORD
00413    *
00414    * Go ahead and use the Range stuff supplied for HTTP
00415    */
00416   if(data->state.use_range &&
00417      (rtspreq  & (RTSPREQ_PLAY | RTSPREQ_PAUSE | RTSPREQ_RECORD))) {
00418 
00419     /* Check to see if there is a range set in the custom headers */
00420     if(!Curl_checkheaders(conn, "Range:") && data->state.range) {
00421       Curl_safefree(conn->allocptr.rangeline);
00422       conn->allocptr.rangeline = aprintf("Range: %s\r\n", data->state.range);
00423       p_range = conn->allocptr.rangeline;
00424     }
00425   }
00426 
00427   /*
00428    * Sanity check the custom headers
00429    */
00430   if(Curl_checkheaders(conn, "CSeq:")) {
00431     failf(data, "CSeq cannot be set as a custom header.");
00432     return CURLE_RTSP_CSEQ_ERROR;
00433   }
00434   if(Curl_checkheaders(conn, "Session:")) {
00435     failf(data, "Session ID cannot be set as a custom header.");
00436     return CURLE_BAD_FUNCTION_ARGUMENT;
00437   }
00438 
00439   /* Initialize a dynamic send buffer */
00440   req_buffer = Curl_add_buffer_init();
00441 
00442   if(!req_buffer)
00443     return CURLE_OUT_OF_MEMORY;
00444 
00445   result =
00446     Curl_add_bufferf(req_buffer,
00447                      "%s %s RTSP/1.0\r\n" /* Request Stream-URI RTSP/1.0 */
00448                      "CSeq: %ld\r\n", /* CSeq */
00449                      p_request, p_stream_uri, rtsp->CSeq_sent);
00450   if(result)
00451     return result;
00452 
00453   /*
00454    * Rather than do a normal alloc line, keep the session_id unformatted
00455    * to make comparison easier
00456    */
00457   if(p_session_id) {
00458     result = Curl_add_bufferf(req_buffer, "Session: %s\r\n", p_session_id);
00459     if(result)
00460       return result;
00461   }
00462 
00463   /*
00464    * Shared HTTP-like options
00465    */
00466   result = Curl_add_bufferf(req_buffer,
00467                             "%s" /* transport */
00468                             "%s" /* accept */
00469                             "%s" /* accept-encoding */
00470                             "%s" /* range */
00471                             "%s" /* referrer */
00472                             "%s" /* user-agent */
00473                             "%s" /* proxyuserpwd */
00474                             "%s" /* userpwd */
00475                             ,
00476                             p_transport ? p_transport : "",
00477                             p_accept ? p_accept : "",
00478                             p_accept_encoding ? p_accept_encoding : "",
00479                             p_range ? p_range : "",
00480                             p_referrer ? p_referrer : "",
00481                             p_uagent ? p_uagent : "",
00482                             p_proxyuserpwd ? p_proxyuserpwd : "",
00483                             p_userpwd ? p_userpwd : "");
00484 
00485   /*
00486    * Free userpwd now --- cannot reuse this for Negotiate and possibly NTLM
00487    * with basic and digest, it will be freed anyway by the next request
00488    */
00489   Curl_safefree(conn->allocptr.userpwd);
00490   conn->allocptr.userpwd = NULL;
00491 
00492   if(result)
00493     return result;
00494 
00495   if((rtspreq == RTSPREQ_SETUP) || (rtspreq == RTSPREQ_DESCRIBE)) {
00496     result = Curl_add_timecondition(data, req_buffer);
00497     if(result)
00498       return result;
00499   }
00500 
00501   result = Curl_add_custom_headers(conn, FALSE, req_buffer);
00502   if(result)
00503     return result;
00504 
00505   if(rtspreq == RTSPREQ_ANNOUNCE ||
00506      rtspreq == RTSPREQ_SET_PARAMETER ||
00507      rtspreq == RTSPREQ_GET_PARAMETER) {
00508 
00509     if(data->set.upload) {
00510       putsize = data->state.infilesize;
00511       data->set.httpreq = HTTPREQ_PUT;
00512 
00513     }
00514     else {
00515       postsize = (data->state.infilesize != -1)?
00516         data->state.infilesize:
00517         (data->set.postfields? (curl_off_t)strlen(data->set.postfields):0);
00518       data->set.httpreq = HTTPREQ_POST;
00519     }
00520 
00521     if(putsize > 0 || postsize > 0) {
00522       /* As stated in the http comments, it is probably not wise to
00523        * actually set a custom Content-Length in the headers */
00524       if(!Curl_checkheaders(conn, "Content-Length:")) {
00525         result = Curl_add_bufferf(req_buffer,
00526             "Content-Length: %" CURL_FORMAT_CURL_OFF_T"\r\n",
00527             (data->set.upload ? putsize : postsize));
00528         if(result)
00529           return result;
00530       }
00531 
00532       if(rtspreq == RTSPREQ_SET_PARAMETER ||
00533          rtspreq == RTSPREQ_GET_PARAMETER) {
00534         if(!Curl_checkheaders(conn, "Content-Type:")) {
00535           result = Curl_add_bufferf(req_buffer,
00536               "Content-Type: text/parameters\r\n");
00537           if(result)
00538             return result;
00539         }
00540       }
00541 
00542       if(rtspreq == RTSPREQ_ANNOUNCE) {
00543         if(!Curl_checkheaders(conn, "Content-Type:")) {
00544           result = Curl_add_bufferf(req_buffer,
00545               "Content-Type: application/sdp\r\n");
00546           if(result)
00547             return result;
00548         }
00549       }
00550 
00551       data->state.expect100header = FALSE; /* RTSP posts are simple/small */
00552     }
00553     else if(rtspreq == RTSPREQ_GET_PARAMETER) {
00554       /* Check for an empty GET_PARAMETER (heartbeat) request */
00555       data->set.httpreq = HTTPREQ_HEAD;
00556       data->set.opt_no_body = TRUE;
00557     }
00558   }
00559 
00560   /* RTSP never allows chunked transfer */
00561   data->req.forbidchunk = TRUE;
00562   /* Finish the request buffer */
00563   result = Curl_add_buffer(req_buffer, "\r\n", 2);
00564   if(result)
00565     return result;
00566 
00567   if(postsize > 0) {
00568     result = Curl_add_buffer(req_buffer, data->set.postfields,
00569                              (size_t)postsize);
00570     if(result)
00571       return result;
00572   }
00573 
00574   /* issue the request */
00575   result = Curl_add_buffer_send(req_buffer, conn,
00576                                 &data->info.request_size, 0, FIRSTSOCKET);
00577   if(result) {
00578     failf(data, "Failed sending RTSP request");
00579     return result;
00580   }
00581 
00582   Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE, &http->readbytecount,
00583                       putsize?FIRSTSOCKET:-1,
00584                       putsize?&http->writebytecount:NULL);
00585 
00586   /* Increment the CSeq on success */
00587   data->state.rtsp_next_client_CSeq++;
00588 
00589   if(http->writebytecount) {
00590     /* if a request-body has been sent off, we make sure this progress is
00591        noted properly */
00592     Curl_pgrsSetUploadCounter(data, http->writebytecount);
00593     if(Curl_pgrsUpdate(conn))
00594       result = CURLE_ABORTED_BY_CALLBACK;
00595   }
00596 
00597   return result;
00598 }
00599 
00600 
00601 static CURLcode rtsp_rtp_readwrite(struct Curl_easy *data,
00602                                    struct connectdata *conn,
00603                                    ssize_t *nread,
00604                                    bool *readmore) {
00605   struct SingleRequest *k = &data->req;
00606   struct rtsp_conn *rtspc = &(conn->proto.rtspc);
00607 
00608   char *rtp; /* moving pointer to rtp data */
00609   ssize_t rtp_dataleft; /* how much data left to parse in this round */
00610   char *scratch;
00611   CURLcode result;
00612 
00613   if(rtspc->rtp_buf) {
00614     /* There was some leftover data the last time. Merge buffers */
00615     char *newptr = Curl_saferealloc(rtspc->rtp_buf,
00616                                     rtspc->rtp_bufsize + *nread);
00617     if(!newptr) {
00618       rtspc->rtp_buf = NULL;
00619       rtspc->rtp_bufsize = 0;
00620       return CURLE_OUT_OF_MEMORY;
00621     }
00622     rtspc->rtp_buf = newptr;
00623     memcpy(rtspc->rtp_buf + rtspc->rtp_bufsize, k->str, *nread);
00624     rtspc->rtp_bufsize += *nread;
00625     rtp = rtspc->rtp_buf;
00626     rtp_dataleft = rtspc->rtp_bufsize;
00627   }
00628   else {
00629     /* Just parse the request buffer directly */
00630     rtp = k->str;
00631     rtp_dataleft = *nread;
00632   }
00633 
00634   while((rtp_dataleft > 0) &&
00635         (rtp[0] == '$')) {
00636     if(rtp_dataleft > 4) {
00637       int rtp_length;
00638 
00639       /* Parse the header */
00640       /* The channel identifier immediately follows and is 1 byte */
00641       rtspc->rtp_channel = RTP_PKT_CHANNEL(rtp);
00642 
00643       /* The length is two bytes */
00644       rtp_length = RTP_PKT_LENGTH(rtp);
00645 
00646       if(rtp_dataleft < rtp_length + 4) {
00647         /* Need more - incomplete payload*/
00648         *readmore = TRUE;
00649         break;
00650       }
00651       else {
00652         /* We have the full RTP interleaved packet
00653          * Write out the header including the leading '$' */
00654         DEBUGF(infof(data, "RTP write channel %d rtp_length %d\n",
00655               rtspc->rtp_channel, rtp_length));
00656         result = rtp_client_write(conn, &rtp[0], rtp_length + 4);
00657         if(result) {
00658           failf(data, "Got an error writing an RTP packet");
00659           *readmore = FALSE;
00660           Curl_safefree(rtspc->rtp_buf);
00661           rtspc->rtp_buf = NULL;
00662           rtspc->rtp_bufsize = 0;
00663           return result;
00664         }
00665 
00666         /* Move forward in the buffer */
00667         rtp_dataleft -= rtp_length + 4;
00668         rtp += rtp_length + 4;
00669 
00670         if(data->set.rtspreq == RTSPREQ_RECEIVE) {
00671           /* If we are in a passive receive, give control back
00672            * to the app as often as we can.
00673            */
00674           k->keepon &= ~KEEP_RECV;
00675         }
00676       }
00677     }
00678     else {
00679       /* Need more - incomplete header */
00680       *readmore = TRUE;
00681       break;
00682     }
00683   }
00684 
00685   if(rtp_dataleft != 0 && rtp[0] == '$') {
00686     DEBUGF(infof(data, "RTP Rewinding %zd %s\n", rtp_dataleft,
00687           *readmore ? "(READMORE)" : ""));
00688 
00689     /* Store the incomplete RTP packet for a "rewind" */
00690     scratch = malloc(rtp_dataleft);
00691     if(!scratch) {
00692       Curl_safefree(rtspc->rtp_buf);
00693       rtspc->rtp_buf = NULL;
00694       rtspc->rtp_bufsize = 0;
00695       return CURLE_OUT_OF_MEMORY;
00696     }
00697     memcpy(scratch, rtp, rtp_dataleft);
00698     Curl_safefree(rtspc->rtp_buf);
00699     rtspc->rtp_buf = scratch;
00700     rtspc->rtp_bufsize = rtp_dataleft;
00701 
00702     /* As far as the transfer is concerned, this data is consumed */
00703     *nread = 0;
00704     return CURLE_OK;
00705   }
00706   else {
00707     /* Fix up k->str to point just after the last RTP packet */
00708     k->str += *nread - rtp_dataleft;
00709 
00710     /* either all of the data has been read or...
00711      * rtp now points at the next byte to parse
00712      */
00713     if(rtp_dataleft > 0)
00714       DEBUGASSERT(k->str[0] == rtp[0]);
00715 
00716     DEBUGASSERT(rtp_dataleft <= *nread); /* sanity check */
00717 
00718     *nread = rtp_dataleft;
00719   }
00720 
00721   /* If we get here, we have finished with the leftover/merge buffer */
00722   Curl_safefree(rtspc->rtp_buf);
00723   rtspc->rtp_buf = NULL;
00724   rtspc->rtp_bufsize = 0;
00725 
00726   return CURLE_OK;
00727 }
00728 
00729 static
00730 CURLcode rtp_client_write(struct connectdata *conn, char *ptr, size_t len)
00731 {
00732   struct Curl_easy *data = conn->data;
00733   size_t wrote;
00734   curl_write_callback writeit;
00735 
00736   if(len == 0) {
00737     failf(data, "Cannot write a 0 size RTP packet.");
00738     return CURLE_WRITE_ERROR;
00739   }
00740 
00741   writeit = data->set.fwrite_rtp?data->set.fwrite_rtp:data->set.fwrite_func;
00742   wrote = writeit(ptr, 1, len, data->set.rtp_out);
00743 
00744   if(CURL_WRITEFUNC_PAUSE == wrote) {
00745     failf(data, "Cannot pause RTP");
00746     return CURLE_WRITE_ERROR;
00747   }
00748 
00749   if(wrote != len) {
00750     failf(data, "Failed writing RTP data");
00751     return CURLE_WRITE_ERROR;
00752   }
00753 
00754   return CURLE_OK;
00755 }
00756 
00757 CURLcode Curl_rtsp_parseheader(struct connectdata *conn,
00758                                char *header)
00759 {
00760   struct Curl_easy *data = conn->data;
00761   long CSeq = 0;
00762 
00763   if(checkprefix("CSeq:", header)) {
00764     /* Store the received CSeq. Match is verified in rtsp_done */
00765     int nc = sscanf(&header[4], ": %ld", &CSeq);
00766     if(nc == 1) {
00767       struct RTSP *rtsp = data->req.protop;
00768       rtsp->CSeq_recv = CSeq; /* mark the request */
00769       data->state.rtsp_CSeq_recv = CSeq; /* update the handle */
00770     }
00771     else {
00772       failf(data, "Unable to read the CSeq header: [%s]", header);
00773       return CURLE_RTSP_CSEQ_ERROR;
00774     }
00775   }
00776   else if(checkprefix("Session:", header)) {
00777     char *start;
00778 
00779     /* Find the first non-space letter */
00780     start = header + 8;
00781     while(*start && ISSPACE(*start))
00782       start++;
00783 
00784     if(!*start) {
00785       failf(data, "Got a blank Session ID");
00786     }
00787     else if(data->set.str[STRING_RTSP_SESSION_ID]) {
00788       /* If the Session ID is set, then compare */
00789       if(strncmp(start, data->set.str[STRING_RTSP_SESSION_ID],
00790                  strlen(data->set.str[STRING_RTSP_SESSION_ID]))  != 0) {
00791         failf(data, "Got RTSP Session ID Line [%s], but wanted ID [%s]",
00792               start, data->set.str[STRING_RTSP_SESSION_ID]);
00793         return CURLE_RTSP_SESSION_ERROR;
00794       }
00795     }
00796     else {
00797       /* If the Session ID is not set, and we find it in a response, then set
00798        * it.
00799        *
00800        * Allow any non whitespace content, up to the field seperator or end of
00801        * line. RFC 2326 isn't 100% clear on the session ID and for example
00802        * gstreamer does url-encoded session ID's not covered by the standard.
00803        */
00804       char *end = start;
00805       while(*end && *end != ';' && !ISSPACE(*end))
00806         end++;
00807 
00808       /* Copy the id substring into a new buffer */
00809       data->set.str[STRING_RTSP_SESSION_ID] = malloc(end - start + 1);
00810       if(data->set.str[STRING_RTSP_SESSION_ID] == NULL)
00811         return CURLE_OUT_OF_MEMORY;
00812       memcpy(data->set.str[STRING_RTSP_SESSION_ID], start, end - start);
00813       (data->set.str[STRING_RTSP_SESSION_ID])[end - start] = '\0';
00814     }
00815   }
00816   return CURLE_OK;
00817 }
00818 
00819 #endif /* CURL_DISABLE_RTSP */


rc_visard_driver
Author(s): Heiko Hirschmueller , Christian Emmerich , Felix Ruess
autogenerated on Thu Jun 6 2019 20:43:06