diff options
Diffstat (limited to 'lib/rtsp.c')
-rw-r--r-- | lib/rtsp.c | 177 |
1 files changed, 103 insertions, 74 deletions
@@ -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)) { |