RTSP: improved RTP parser

- fix HTTP header parsing to report incomplete
  lines it buffers as consumed!
- re-implement the RTP parser for interleave RTP
  messages for robustness. It is now keeping its
  state at the connection
- RTSP protocol handler "readwrite" implementation
  now tracks if the response is before/in/after
  header parsing or "in" a bod by calling
  "Curl_http_readwrite_headers()" itself. This
  allows it to know when non-RTP bytes are "junk"
  or HEADER or BODY.
- tested with #12035 and various small receive
  sizes where current master fails

Closes #12052
This commit is contained in:
Stefan Eissing 2023-10-07 15:13:09 +02:00 committed by Daniel Stenberg
parent 117c9bd978
commit 7eb31c852d
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
4 changed files with 280 additions and 160 deletions

View file

@ -423,7 +423,6 @@ static CURLcode readwrite_data(struct Curl_easy *data,
{
CURLcode result = CURLE_OK;
ssize_t nread; /* number of bytes read */
size_t excess = 0; /* excess bytes read */
bool readmore = FALSE; /* used by RTP to signal for more data */
int maxloops = 100;
curl_off_t max_recv = data->set.max_recv_speed?
@ -439,6 +438,7 @@ static CURLcode readwrite_data(struct Curl_easy *data,
read or we get a CURLE_AGAIN */
do {
bool is_empty_data = FALSE;
size_t excess = 0; /* excess bytes read */
size_t buffersize = data->set.buffer_size;
size_t bytestoread = buffersize;
/* For HTTP/2 and HTTP/3, read data without caring about the content
@ -555,9 +555,11 @@ static CURLcode readwrite_data(struct Curl_easy *data,
is non-headers. */
if(!k->header && (nread > 0 || is_empty_data)) {
if(data->req.no_body) {
if(data->req.no_body && nread > 0) {
/* data arrives although we want none, bail out */
streamclose(conn, "ignoring body");
DEBUGF(infof(data, "did not want a BODY, but seeing %zd bytes",
nread));
*done = TRUE;
result = CURLE_WEIRD_SERVER_REPLY;
goto out;
@ -642,17 +644,6 @@ static CURLcode readwrite_data(struct Curl_easy *data,
(k->bytecount + nread >= k->maxdownload)) {
excess = (size_t)(k->bytecount + nread - k->maxdownload);
if(excess > 0 && !k->ignorebody) {
infof(data,
"Excess found in a read:"
" excess = %zu"
", size = %" CURL_FORMAT_CURL_OFF_T
", maxdownload = %" CURL_FORMAT_CURL_OFF_T
", bytecount = %" CURL_FORMAT_CURL_OFF_T,
excess, k->size, k->maxdownload, k->bytecount);
connclose(conn, "excess found in a read");
}
nread = (ssize_t) (k->maxdownload - k->bytecount);
if(nread < 0) /* this should be unusual */
nread = 0;
@ -718,24 +709,39 @@ static CURLcode readwrite_data(struct Curl_easy *data,
} /* if(!header and data to read) */
if(conn->handler->readwrite && excess) {
/* Parse the excess data */
k->str += nread;
if(excess > 0 && !k->ignorebody) {
if(conn->handler->readwrite) {
/* Give protocol handler a chance to do something with it */
k->str += nread;
if(&k->str[excess] > &buf[data->set.buffer_size]) {
/* the excess amount was too excessive(!), make sure
it doesn't read out of buffer */
excess = &buf[data->set.buffer_size] - k->str;
}
nread = (ssize_t)excess;
result = conn->handler->readwrite(data, conn, &nread, &readmore);
if(result)
goto out;
if(&k->str[excess] > &buf[data->set.buffer_size]) {
/* the excess amount was too excessive(!), make sure
it doesn't read out of buffer */
excess = &buf[data->set.buffer_size] - k->str;
if(readmore) {
DEBUGASSERT(nread == 0);
k->keepon |= KEEP_RECV; /* we're not done reading */
}
else if(nread == 0)
break;
/* protocol handler did not consume all excess data */
excess = nread;
}
if(excess) {
infof(data,
"Excess found in a read:"
" excess = %zu"
", size = %" CURL_FORMAT_CURL_OFF_T
", maxdownload = %" CURL_FORMAT_CURL_OFF_T
", bytecount = %" CURL_FORMAT_CURL_OFF_T,
excess, k->size, k->maxdownload, k->bytecount);
connclose(conn, "excess found in a read");
}
nread = (ssize_t)excess;
result = conn->handler->readwrite(data, conn, &nread, &readmore);
if(result)
goto out;
if(readmore)
k->keepon |= KEEP_RECV; /* we're not done reading */
break;
}
if(is_empty_data) {