summaryrefslogtreecommitdiffstats
path: root/lib/rtsp.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rtsp.c')
-rw-r--r--lib/rtsp.c177
1 files changed, 103 insertions, 74 deletions
diff --git a/lib/rtsp.c b/lib/rtsp.c
index e673bb8..26f4735 100644
--- a/lib/rtsp.c
+++ b/lib/rtsp.c
@@ -58,21 +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.
+ * 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 consumed out, number of blen consumed
+ * @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,
- const char *buf,
- size_t blen,
- size_t *pconsumed,
- 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);
@@ -115,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 */
@@ -590,26 +589,48 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
return result;
}
+/**
+ * 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);
+ }
+ return CURLE_OK;
+}
+
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);
+ 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);
- 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) {
@@ -624,13 +645,22 @@ static CURLcode rtsp_filter_rtp(struct Curl_easy *data,
goto out;
}
}
- /* junk, consume without buffering */
+ /* 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;
+ }
if(Curl_dyn_addn(&rtspc->buf, buf, 1)) {
result = CURLE_OUT_OF_MEMORY;
goto out;
@@ -650,35 +680,22 @@ static CURLcode rtsp_filter_rtp(struct Curl_easy *data,
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;
+ 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;
- }
}
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);
+ /* 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 */
@@ -754,52 +771,51 @@ static CURLcode rtsp_filter_rtp(struct Curl_easy *data,
}
}
out:
+ if(!result && skip_len)
+ result = rtp_write_body_junk(data, (char *)(buf - skip_len), skip_len);
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)
+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 = &(conn->proto.rtspc);
+ struct rtsp_conn *rtspc = &(data->conn->proto.rtspc);
CURLcode result = CURLE_OK;
size_t consumed = 0;
- bool in_body;
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);
-
- *readmore = FALSE;
- *pconsumed = 0;
+ *done = FALSE;
if(!blen) {
goto out;
}
+ 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, conn, buf, blen, in_body, &consumed);
+ result = rtsp_filter_rtp(data, buf, blen, &consumed);
if(result)
goto out;
- *pconsumed += consumed;
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));
}
/* 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);
+ result = Curl_http_write_resp_hds(data, buf, blen, &consumed, done);
if(result)
goto out;
- *pconsumed += consumed;
buf += consumed;
blen -= consumed;
@@ -807,26 +823,41 @@ static CURLcode rtsp_rtp_readwrite(struct Curl_easy *data,
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 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;
- *pconsumed += consumed;
+ blen -= consumed;
}
}
if(rtspc->state != RTP_PARSE_SKIP)
- *readmore = TRUE;
+ *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(!*readmore && data->set.rtspreq == RTSPREQ_RECEIVE) {
+ 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.keepon &= ~KEEP_RECV;
+ data->req.download_done = TRUE;
}
return result;
}
@@ -922,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;
@@ -934,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)) {