diff options
Diffstat (limited to 'lib/rtsp.c')
-rw-r--r-- | lib/rtsp.c | 181 |
1 files changed, 141 insertions, 40 deletions
diff --git a/lib/rtsp.c b/lib/rtsp.c index aef3560a9..c503cda42 100644 --- a/lib/rtsp.c +++ b/lib/rtsp.c @@ -45,8 +45,6 @@ #include "curl_memory.h" #include "memdebug.h" -#define RTP_PKT_CHANNEL(p) ((int)((unsigned char)((p)[1]))) - #define RTP_PKT_LENGTH(p) ((((int)((unsigned char)((p)[2]))) << 8) | \ ((int)((unsigned char)((p)[3])))) @@ -91,6 +89,8 @@ 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); +static +CURLcode rtsp_parse_transport(struct Curl_easy *data, char *transport); /* @@ -594,11 +594,14 @@ static CURLcode rtsp_rtp_readwrite(struct Curl_easy *data, 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 */ char *scratch; CURLcode result; + bool interleaved = false; + size_t skip_size = 0; if(rtspc->rtp_buf) { /* There was some leftover data the last time. Merge buffers */ @@ -621,52 +624,94 @@ static CURLcode rtsp_rtp_readwrite(struct Curl_easy *data, rtp_dataleft = *nread; } - while((rtp_dataleft > 0) && - (rtp[0] == '$')) { - if(rtp_dataleft > 4) { - int rtp_length; + 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; + } + if(skip_size > 0) { + DEBUGF(infof(data, "Skip the malformed interleaved data %lu " + "bytes", skip_size)); + } + skip_size = 0; + rtspc->rtp_channel = rtp_channel; - /* Parse the header */ - /* The channel identifier immediately follows and is 1 byte */ - rtspc->rtp_channel = RTP_PKT_CHANNEL(rtp); + /* The length is two bytes */ + rtp_length = RTP_PKT_LENGTH(rtp); - /* The length is two bytes */ - rtp_length = RTP_PKT_LENGTH(rtp); + if(rtp_dataleft < rtp_length + 4) { + /* Need more - incomplete payload */ + *readmore = TRUE; + break; + } + 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; + Curl_safefree(rtspc->rtp_buf); + rtspc->rtp_buf = NULL; + rtspc->rtp_bufsize = 0; + return result; + } - if(rtp_dataleft < rtp_length + 4) { - /* Need more - incomplete payload */ + /* Move forward in the buffer */ + rtp_dataleft -= rtp_length + 4; + rtp += rtp_length + 4; + + 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; + } + } + else { + /* Need more - incomplete header */ *readmore = TRUE; break; } - /* 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) { - failf(data, "Got an error writing an RTP packet"); - *readmore = FALSE; - Curl_safefree(rtspc->rtp_buf); - rtspc->rtp_buf = NULL; - rtspc->rtp_bufsize = 0; - return result; - } - - /* Move forward in the buffer */ - rtp_dataleft -= rtp_length + 4; - rtp += rtp_length + 4; - - 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; - } } else { - /* Need more - incomplete header */ - *readmore = TRUE; - break; + /* 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)); + } + skip_size = 0; + break; /* maybe is an RTSP message */ + } + /* 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'); } } @@ -822,7 +867,63 @@ CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, char *header) (data->set.str[STRING_RTSP_SESSION_ID])[idlen] = '\0'; } } + else if(checkprefix("Transport:", header)) { + CURLcode result; + result = rtsp_parse_transport(data, header + 10); + if(result) + return result; + } return CURLE_OK; } +static +CURLcode rtsp_parse_transport(struct Curl_easy *data, char *transport) +{ + /* If we receive multiple Transport response-headers, the linterleaved + channels of each response header is recorded and used together for + subsequent data validity checks.*/ + /* e.g.: ' RTP/AVP/TCP;unicast;interleaved=5-6' */ + char *start; + char *end; + start = transport; + while(start && *start) { + while(*start && ISBLANK(*start) ) + start++; + end = strchr(start, ';'); + if(checkprefix("interleaved=", start)) { + long chan1, chan2, chan; + char *endp; + char *p = start + 12; + chan1 = strtol(p, &endp, 10); + if(p != endp && chan1 >= 0 && chan1 <= 255) { + unsigned char *rtp_channel_mask = data->state.rtp_channel_mask; + chan2 = chan1; + if(*endp == '-') { + p = endp + 1; + chan2 = strtol(p, &endp, 10); + if(p == endp || chan2 < 0 || chan2 > 255) { + infof(data, "Unable to read the interleaved parameter from " + "Transport header: [%s]", transport); + chan2 = chan1; + } + } + for(chan = chan1; chan <= chan2; chan++) { + long idx = chan / 8; + long off = chan % 8; + rtp_channel_mask[idx] |= (unsigned char)(1 << off); + } + } + else { + infof(data, "Unable to read the interleaved parameter from " + "Transport header: [%s]", transport); + } + break; + } + /* skip to next parameter */ + start = (!end) ? end : (end + 1); + } + return CURLE_OK; +} + + #endif /* CURL_DISABLE_RTSP or using Hyper */ |