diff options
Diffstat (limited to 'Utilities/cmcurl/lib/rtsp.c')
-rw-r--r-- | Utilities/cmcurl/lib/rtsp.c | 359 |
1 files changed, 230 insertions, 129 deletions
diff --git a/Utilities/cmcurl/lib/rtsp.c b/Utilities/cmcurl/lib/rtsp.c index ccd7264..e673bb8 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); @@ -59,14 +59,19 @@ static int rtsp_getsock_do(struct Curl_easy *data, /* * 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 + * @param data the transfer + * @param conn the connection + * @param buf data read from connection + * @param blen amount of data in buf + * @param consumed out, number of blen consumed + * @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, + const char *buf, + size_t blen, + size_t *pconsumed, bool *readmore); static CURLcode rtsp_setup_connection(struct Curl_easy *data, @@ -88,7 +93,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); @@ -585,153 +590,249 @@ 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; +static CURLcode rtsp_filter_rtp(struct Curl_easy *data, + struct connectdata *conn, + const char *buf, + size_t blen, + bool in_body, + size_t *pconsumed) +{ struct rtsp_conn *rtspc = &(conn->proto.rtspc); - unsigned char *rtp_channel_mask = data->state.rtp_channel_mask; + CURLcode result = CURLE_OK; - 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; + *pconsumed = 0; + while(blen) { + switch(rtspc->state) { - 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); - } - 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; + case RTP_PARSE_SKIP: { + DEBUGASSERT(Curl_dyn_len(&rtspc->buf) == 0); + if(in_body && buf[0] != '$') { + /* in BODY and no valid start, do not consume and return */ + goto out; + } + 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, consume without buffering */ + *pconsumed += 1; + ++buf; + --blen; + } + if(blen && buf[0] == '$') { + /* possible start of an RTP message, buffer */ + if(Curl_dyn_addn(&rtspc->buf, buf, 1)) { + result = CURLE_OUT_OF_MEMORY; + goto out; } - skip_size = 0; - rtspc->rtp_channel = rtp_channel; - - /* The length is two bytes */ - rtp_length = RTP_PKT_LENGTH(rtp); + *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; + if(in_body) { + /* we do not consume this byte, it is BODY data */ + DEBUGF(infof(data, "RTSP: invalid RTP channel %d in BODY, " + "treating as BODY data", 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 = Curl_client_write(data, CLIENTWRITE_BODY, + Curl_dyn_ptr(&rtspc->buf), 1); + Curl_dyn_free(&rtspc->buf); + if(result) + goto out; + } + else { + /* un-consume the '$' and leave */ + Curl_dyn_free(&rtspc->buf); + *pconsumed -= 1; + --buf; + ++blen; + 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 { + /* not BODY, forget the junk '$'. Do not consume this byte, + * it might be a start */ + infof(data, "RTSP: invalid RTP channel %d, skipping", idx); + 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: + return result; +} + +static CURLcode rtsp_rtp_readwrite(struct Curl_easy *data, + struct connectdata *conn, + const char *buf, + size_t blen, + size_t *pconsumed, + bool *readmore) +{ + struct rtsp_conn *rtspc = &(conn->proto.rtspc); + CURLcode result = CURLE_OK; + size_t consumed = 0; + bool in_body; - if(rtp_dataleft && rtp[0] == '$') { - DEBUGF(infof(data, "RTP Rewinding %zd %s", rtp_dataleft, - *readmore ? "(READMORE)" : "")); + if(!data->req.header) + rtspc->in_header = FALSE; + in_body = (data->req.headerline && !rtspc->in_header) && + (data->req.size >= 0) && + (data->req.bytecount < data->req.size); - /* 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); - } + *readmore = FALSE; + *pconsumed = 0; + if(!blen) { + goto out; + } - /* As far as the transfer is concerned, this data is consumed */ - *nread = 0; - return CURLE_OK; + /* If header parsing is not onging, extract RTP messages */ + if(!rtspc->in_header) { + result = rtsp_filter_rtp(data, conn, buf, blen, in_body, &consumed); + if(result) + goto out; + *pconsumed += consumed; + buf += consumed; + blen -= consumed; } - /* 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_readwrite_headers(data, conn, buf, blen, + &consumed); + if(result) + goto out; + + *pconsumed += consumed; + buf += consumed; + blen -= consumed; + + if(!data->req.header) + rtspc->in_header = FALSE; + + if(!rtspc->in_header) { + /* If header parsing is done and data left, extract RTP messages */ + in_body = (data->req.headerline && !rtspc->in_header) && + (data->req.size >= 0) && + (data->req.bytecount < data->req.size); + result = rtsp_filter_rtp(data, conn, buf, blen, in_body, &consumed); + if(result) + goto out; + *pconsumed += consumed; + } + } - /* If we get here, we have finished with the leftover/merge buffer */ - Curl_dyn_free(&rtspc->buf); + if(rtspc->state != RTP_PARSE_SKIP) + *readmore = TRUE; - return CURLE_OK; +out: + if(!*readmore && data->set.rtspreq == RTSPREQ_RECEIVE) { + /* 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.keepon &= ~KEEP_RECV; + } + 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 +857,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) { |