summaryrefslogtreecommitdiffstats
path: root/Utilities/cmcurl/lib/rtsp.c
diff options
context:
space:
mode:
Diffstat (limited to 'Utilities/cmcurl/lib/rtsp.c')
-rw-r--r--Utilities/cmcurl/lib/rtsp.c410
1 files changed, 270 insertions, 140 deletions
diff --git a/Utilities/cmcurl/lib/rtsp.c b/Utilities/cmcurl/lib/rtsp.c
index ccd7264..26f4735 100644
--- a/Utilities/cmcurl/lib/rtsp.c
+++ b/Utilities/cmcurl/lib/rtsp.c
@@ -45,8 +45,8 @@
#include "curl_memory.h"
#include "memdebug.h"
-#define RTP_PKT_LENGTH(p) ((((int)((unsigned char)((p)[2]))) << 8) | \
- ((int)((unsigned char)((p)[3]))))
+#define RTP_PKT_LENGTH(p) ((((unsigned int)((unsigned char)((p)[2]))) << 8) | \
+ ((unsigned int)((unsigned char)((p)[3]))))
/* protocol-specific functions set up to be called by the main engine */
static CURLcode rtsp_do(struct Curl_easy *data, bool *done);
@@ -58,16 +58,20 @@ static int rtsp_getsock_do(struct Curl_easy *data,
struct connectdata *conn, curl_socket_t *socks);
/*
- * Parse and write out any available RTP data.
- *
- * nread: amount of data left after k->str. will be modified if RTP
- * data is parsed and k->str is moved up
- * readmore: whether or not the RTP parser needs more data right away
+ * Parse and write out an RTSP response.
+ * @param data the transfer
+ * @param conn the connection
+ * @param buf data read from connection
+ * @param blen amount of data in buf
+ * @param is_eos TRUE iff this is the last write
+ * @param readmore out, TRUE iff complete buf was consumed and more data
+ * is needed
*/
-static CURLcode rtsp_rtp_readwrite(struct Curl_easy *data,
- struct connectdata *conn,
- ssize_t *nread,
- bool *readmore);
+static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data,
+ const char *buf,
+ size_t blen,
+ bool is_eos,
+ bool *done);
static CURLcode rtsp_setup_connection(struct Curl_easy *data,
struct connectdata *conn);
@@ -88,7 +92,7 @@ static int rtsp_getsock_do(struct Curl_easy *data, struct connectdata *conn,
}
static
-CURLcode rtp_client_write(struct Curl_easy *data, char *ptr, size_t len);
+CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr, size_t len);
static
CURLcode rtsp_parse_transport(struct Curl_easy *data, char *transport);
@@ -110,7 +114,7 @@ const struct Curl_handler Curl_handler_rtsp = {
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
rtsp_disconnect, /* disconnect */
- rtsp_rtp_readwrite, /* readwrite */
+ rtsp_rtp_write_resp, /* write_resp */
rtsp_conncheck, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_RTSP, /* defport */
@@ -585,153 +589,281 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
return result;
}
-
-static CURLcode rtsp_rtp_readwrite(struct Curl_easy *data,
- struct connectdata *conn,
- ssize_t *nread,
- bool *readmore) {
- struct SingleRequest *k = &data->req;
- struct rtsp_conn *rtspc = &(conn->proto.rtspc);
- unsigned char *rtp_channel_mask = data->state.rtp_channel_mask;
-
- char *rtp; /* moving pointer to rtp data */
- ssize_t rtp_dataleft; /* how much data left to parse in this round */
- CURLcode result;
- bool interleaved = false;
- size_t skip_size = 0;
-
- if(Curl_dyn_len(&rtspc->buf)) {
- /* There was some leftover data the last time. Append new buffers */
- if(Curl_dyn_addn(&rtspc->buf, k->str, *nread))
- return CURLE_OUT_OF_MEMORY;
- rtp = Curl_dyn_ptr(&rtspc->buf);
- rtp_dataleft = Curl_dyn_len(&rtspc->buf);
+/**
+ * write any BODY bytes missing to the client, ignore the rest.
+ */
+static CURLcode rtp_write_body_junk(struct Curl_easy *data,
+ const char *buf,
+ size_t blen)
+{
+ struct rtsp_conn *rtspc = &(data->conn->proto.rtspc);
+ curl_off_t body_remain;
+ bool in_body;
+
+ in_body = (data->req.headerline && !rtspc->in_header) &&
+ (data->req.size >= 0) &&
+ (data->req.bytecount < data->req.size);
+ body_remain = in_body? (data->req.size - data->req.bytecount) : 0;
+ DEBUGASSERT(body_remain >= 0);
+ if(body_remain) {
+ if((curl_off_t)blen > body_remain)
+ blen = (size_t)body_remain;
+ return Curl_client_write(data, CLIENTWRITE_BODY, (char *)buf, blen);
}
- else {
- /* Just parse the request buffer directly */
- rtp = k->str;
- rtp_dataleft = *nread;
- }
-
- while(rtp_dataleft > 0) {
- if(rtp[0] == '$') {
- if(rtp_dataleft > 4) {
- unsigned char rtp_channel;
- int rtp_length;
- int idx;
- int off;
-
- /* Parse the header */
- /* The channel identifier immediately follows and is 1 byte */
- rtp_channel = (unsigned char)rtp[1];
- idx = rtp_channel / 8;
- off = rtp_channel % 8;
- if(!(rtp_channel_mask[idx] & (1 << off))) {
- /* invalid channel number, maybe not an RTP packet */
- rtp++;
- rtp_dataleft--;
- skip_size++;
- continue;
+ return CURLE_OK;
+}
+
+static CURLcode rtsp_filter_rtp(struct Curl_easy *data,
+ const char *buf,
+ size_t blen,
+ size_t *pconsumed)
+{
+ struct rtsp_conn *rtspc = &(data->conn->proto.rtspc);
+ CURLcode result = CURLE_OK;
+ size_t skip_len = 0;
+
+ *pconsumed = 0;
+ while(blen) {
+ bool in_body = (data->req.headerline && !rtspc->in_header) &&
+ (data->req.size >= 0) &&
+ (data->req.bytecount < data->req.size);
+ switch(rtspc->state) {
+
+ case RTP_PARSE_SKIP: {
+ DEBUGASSERT(Curl_dyn_len(&rtspc->buf) == 0);
+ while(blen && buf[0] != '$') {
+ if(!in_body && buf[0] == 'R' &&
+ data->set.rtspreq != RTSPREQ_RECEIVE) {
+ if(strncmp(buf, "RTSP/", (blen < 5) ? blen : 5) == 0) {
+ /* This could be the next response, no consume and return */
+ if(*pconsumed) {
+ DEBUGF(infof(data, "RTP rtsp_filter_rtp[SKIP] RTSP/ prefix, "
+ "skipping %zd bytes of junk", *pconsumed));
+ }
+ rtspc->state = RTP_PARSE_SKIP;
+ rtspc->in_header = TRUE;
+ goto out;
+ }
}
- if(skip_size > 0) {
- DEBUGF(infof(data, "Skip the malformed interleaved data %lu "
- "bytes", skip_size));
+ /* junk/BODY, consume without buffering */
+ *pconsumed += 1;
+ ++buf;
+ --blen;
+ ++skip_len;
+ }
+ if(blen && buf[0] == '$') {
+ /* possible start of an RTP message, buffer */
+ if(skip_len) {
+ /* end of junk/BODY bytes, flush */
+ result = rtp_write_body_junk(data,
+ (char *)(buf - skip_len), skip_len);
+ skip_len = 0;
+ if(result)
+ goto out;
}
- skip_size = 0;
- rtspc->rtp_channel = rtp_channel;
-
- /* The length is two bytes */
- rtp_length = RTP_PKT_LENGTH(rtp);
+ if(Curl_dyn_addn(&rtspc->buf, buf, 1)) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ *pconsumed += 1;
+ ++buf;
+ --blen;
+ rtspc->state = RTP_PARSE_CHANNEL;
+ }
+ break;
+ }
- if(rtp_dataleft < rtp_length + 4) {
- /* Need more - incomplete payload */
- *readmore = TRUE;
- break;
+ case RTP_PARSE_CHANNEL: {
+ int idx = ((unsigned char)buf[0]) / 8;
+ int off = ((unsigned char)buf[0]) % 8;
+ DEBUGASSERT(Curl_dyn_len(&rtspc->buf) == 1);
+ if(!(data->state.rtp_channel_mask[idx] & (1 << off))) {
+ /* invalid channel number, junk or BODY data */
+ rtspc->state = RTP_PARSE_SKIP;
+ DEBUGASSERT(skip_len == 0);
+ /* we do not consume this byte, it is BODY data */
+ DEBUGF(infof(data, "RTSP: invalid RTP channel %d, skipping", idx));
+ if(*pconsumed == 0) {
+ /* We did not consume the initial '$' in our buffer, but had
+ * it from an earlier call. We cannot un-consume it and have
+ * to write it directly as BODY data */
+ result = rtp_write_body_junk(data, Curl_dyn_ptr(&rtspc->buf), 1);
+ if(result)
+ goto out;
}
- interleaved = true;
- /* We have the full RTP interleaved packet
- * Write out the header including the leading '$' */
- DEBUGF(infof(data, "RTP write channel %d rtp_length %d",
- rtspc->rtp_channel, rtp_length));
- result = rtp_client_write(data, &rtp[0], rtp_length + 4);
- if(result) {
- *readmore = FALSE;
- return result;
+ else {
+ /* count the '$' as skip and continue */
+ skip_len = 1;
}
+ Curl_dyn_free(&rtspc->buf);
+ break;
+ }
+ /* a valid channel, so we expect this to be a real RTP message */
+ rtspc->rtp_channel = (unsigned char)buf[0];
+ if(Curl_dyn_addn(&rtspc->buf, buf, 1)) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ *pconsumed += 1;
+ ++buf;
+ --blen;
+ rtspc->state = RTP_PARSE_LEN;
+ break;
+ }
- /* Move forward in the buffer */
- rtp_dataleft -= rtp_length + 4;
- rtp += rtp_length + 4;
+ case RTP_PARSE_LEN: {
+ size_t rtp_len = Curl_dyn_len(&rtspc->buf);
+ const char *rtp_buf;
+ DEBUGASSERT(rtp_len >= 2 && rtp_len < 4);
+ if(Curl_dyn_addn(&rtspc->buf, buf, 1)) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ *pconsumed += 1;
+ ++buf;
+ --blen;
+ if(rtp_len == 2)
+ break;
+ rtp_buf = Curl_dyn_ptr(&rtspc->buf);
+ rtspc->rtp_len = RTP_PKT_LENGTH(rtp_buf) + 4;
+ rtspc->state = RTP_PARSE_DATA;
+ break;
+ }
- if(data->set.rtspreq == RTSPREQ_RECEIVE) {
- /* If we are in a passive receive, give control back
- * to the app as often as we can.
- */
- k->keepon &= ~KEEP_RECV;
+ case RTP_PARSE_DATA: {
+ size_t rtp_len = Curl_dyn_len(&rtspc->buf);
+ size_t needed;
+ DEBUGASSERT(rtp_len < rtspc->rtp_len);
+ needed = rtspc->rtp_len - rtp_len;
+ if(needed <= blen) {
+ if(Curl_dyn_addn(&rtspc->buf, buf, needed)) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
}
+ *pconsumed += needed;
+ buf += needed;
+ blen -= needed;
+ /* complete RTP message in buffer */
+ DEBUGF(infof(data, "RTP write channel %d rtp_len %zu",
+ rtspc->rtp_channel, rtspc->rtp_len));
+ result = rtp_client_write(data, Curl_dyn_ptr(&rtspc->buf),
+ rtspc->rtp_len);
+ Curl_dyn_free(&rtspc->buf);
+ rtspc->state = RTP_PARSE_SKIP;
+ if(result)
+ goto out;
}
else {
- /* Need more - incomplete header */
- *readmore = TRUE;
- break;
- }
- }
- else {
- /* If the following data begins with 'RTSP/', which might be an RTSP
- message, we should stop skipping the data. */
- /* If `k-> headerline> 0 && !interleaved` is true, we are maybe in the
- middle of an RTSP message. It is difficult to determine this, so we
- stop skipping. */
- size_t prefix_len = (rtp_dataleft < 5) ? rtp_dataleft : 5;
- if((k->headerline > 0 && !interleaved) ||
- strncmp(rtp, "RTSP/", prefix_len) == 0) {
- if(skip_size > 0) {
- DEBUGF(infof(data, "Skip the malformed interleaved data %lu "
- "bytes", skip_size));
+ if(Curl_dyn_addn(&rtspc->buf, buf, blen)) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
}
- break; /* maybe is an RTSP message */
+ *pconsumed += blen;
+ buf += blen;
+ blen = 0;
}
- /* Skip incorrect data util the next RTP packet or RTSP message */
- do {
- rtp++;
- rtp_dataleft--;
- skip_size++;
- } while(rtp_dataleft > 0 && rtp[0] != '$' && rtp[0] != 'R');
+ break;
+ }
+
+ default:
+ DEBUGASSERT(0);
+ return CURLE_RECV_ERROR;
}
}
+out:
+ if(!result && skip_len)
+ result = rtp_write_body_junk(data, (char *)(buf - skip_len), skip_len);
+ return result;
+}
- if(rtp_dataleft && rtp[0] == '$') {
- DEBUGF(infof(data, "RTP Rewinding %zd %s", rtp_dataleft,
- *readmore ? "(READMORE)" : ""));
+static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data,
+ const char *buf,
+ size_t blen,
+ bool is_eos,
+ bool *done)
+{
+ struct rtsp_conn *rtspc = &(data->conn->proto.rtspc);
+ CURLcode result = CURLE_OK;
+ size_t consumed = 0;
- /* Store the incomplete RTP packet for a "rewind" */
- if(!Curl_dyn_len(&rtspc->buf)) {
- /* nothing was stored, add this data */
- if(Curl_dyn_addn(&rtspc->buf, rtp, rtp_dataleft))
- return CURLE_OUT_OF_MEMORY;
- }
- else {
- /* keep the remainder */
- Curl_dyn_tail(&rtspc->buf, rtp_dataleft);
- }
+ if(!data->req.header)
+ rtspc->in_header = FALSE;
+ *done = FALSE;
+ if(!blen) {
+ goto out;
+ }
- /* As far as the transfer is concerned, this data is consumed */
- *nread = 0;
- return CURLE_OK;
+ DEBUGF(infof(data, "rtsp_rtp_write_resp(len=%zu, in_header=%d, eos=%d)",
+ blen, rtspc->in_header, is_eos));
+
+ /* If header parsing is not onging, extract RTP messages */
+ if(!rtspc->in_header) {
+ result = rtsp_filter_rtp(data, buf, blen, &consumed);
+ if(result)
+ goto out;
+ buf += consumed;
+ blen -= consumed;
+ /* either we consumed all or are at the start of header parsing */
+ if(blen && !data->req.header)
+ DEBUGF(infof(data, "RTSP: %zu bytes, possibly excess in response body",
+ blen));
}
- /* Fix up k->str to point just after the last RTP packet */
- k->str += *nread - rtp_dataleft;
- *nread = rtp_dataleft;
+ /* we want to parse headers, do so */
+ if(data->req.header && blen) {
+ rtspc->in_header = TRUE;
+ result = Curl_http_write_resp_hds(data, buf, blen, &consumed, done);
+ if(result)
+ goto out;
- /* If we get here, we have finished with the leftover/merge buffer */
- Curl_dyn_free(&rtspc->buf);
+ buf += consumed;
+ blen -= consumed;
- return CURLE_OK;
+ if(!data->req.header)
+ rtspc->in_header = FALSE;
+
+ if(!rtspc->in_header) {
+ /* If header parsing is done, extract interleaved RTP messages */
+ if(data->req.size <= -1) {
+ /* Respect section 4.4 of rfc2326: If the Content-Length header is
+ absent, a length 0 must be assumed. */
+ data->req.size = 0;
+ data->req.download_done = TRUE;
+ }
+ result = rtsp_filter_rtp(data, buf, blen, &consumed);
+ if(result)
+ goto out;
+ blen -= consumed;
+ }
+ }
+
+ if(rtspc->state != RTP_PARSE_SKIP)
+ *done = FALSE;
+ /* we SHOULD have consumed all bytes, unless the response is borked.
+ * In which case we write out the left over bytes, letting the client
+ * writer deal with it (it will report EXCESS and fail the transfer). */
+ DEBUGF(infof(data, "rtsp_rtp_write_resp(len=%zu, in_header=%d, done=%d "
+ " rtspc->state=%d, req.size=%" CURL_FORMAT_CURL_OFF_T ")",
+ blen, rtspc->in_header, *done, rtspc->state, data->req.size));
+ if(!result && (is_eos || blen)) {
+ result = Curl_client_write(data, CLIENTWRITE_BODY|
+ (is_eos? CLIENTWRITE_EOS:0),
+ (char *)buf, blen);
+ }
+
+out:
+ if((data->set.rtspreq == RTSPREQ_RECEIVE) &&
+ (rtspc->state == RTP_PARSE_SKIP)) {
+ /* In special mode RECEIVE, we just process one chunk of network
+ * data, so we stop the transfer here, if we have no incomplete
+ * RTP message pending. */
+ data->req.download_done = TRUE;
+ }
+ return result;
}
static
-CURLcode rtp_client_write(struct Curl_easy *data, char *ptr, size_t len)
+CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr, size_t len)
{
size_t wrote;
curl_write_callback writeit;
@@ -756,7 +888,7 @@ CURLcode rtp_client_write(struct Curl_easy *data, char *ptr, size_t len)
}
Curl_set_in_callback(data, true);
- wrote = writeit(ptr, 1, len, user_ptr);
+ wrote = writeit((char *)ptr, 1, len, user_ptr);
Curl_set_in_callback(data, false);
if(CURL_WRITEFUNC_PAUSE == wrote) {
@@ -821,7 +953,7 @@ CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, char *header)
/* If the Session ID is set, then compare */
if(strlen(data->set.str[STRING_RTSP_SESSION_ID]) != idlen ||
- strncmp(start, data->set.str[STRING_RTSP_SESSION_ID], idlen) != 0) {
+ strncmp(start, data->set.str[STRING_RTSP_SESSION_ID], idlen)) {
failf(data, "Got RTSP Session ID Line [%s], but wanted ID [%s]",
start, data->set.str[STRING_RTSP_SESSION_ID]);
return CURLE_RTSP_SESSION_ERROR;
@@ -833,11 +965,9 @@ CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, char *header)
*/
/* Copy the id substring into a new buffer */
- data->set.str[STRING_RTSP_SESSION_ID] = malloc(idlen + 1);
+ data->set.str[STRING_RTSP_SESSION_ID] = Curl_memdup0(start, idlen);
if(!data->set.str[STRING_RTSP_SESSION_ID])
return CURLE_OUT_OF_MEMORY;
- memcpy(data->set.str[STRING_RTSP_SESSION_ID], start, idlen);
- (data->set.str[STRING_RTSP_SESSION_ID])[idlen] = '\0';
}
}
else if(checkprefix("Transport:", header)) {