check for overly large header field in send_headers
This commit is contained in:
@@ -35,6 +35,9 @@ pub enum UserError {
|
||||
/// The payload size is too big
|
||||
PayloadTooBig,
|
||||
|
||||
/// A header size is too big
|
||||
HeaderTooBig,
|
||||
|
||||
/// The application attempted to initiate too many streams to remote.
|
||||
Rejected,
|
||||
|
||||
@@ -131,6 +134,7 @@ impl error::Error for UserError {
|
||||
InactiveStreamId => "inactive stream",
|
||||
UnexpectedFrameType => "unexpected frame type",
|
||||
PayloadTooBig => "payload too big",
|
||||
HeaderTooBig => "header too big",
|
||||
Rejected => "rejected",
|
||||
ReleaseCapacityTooBig => "release capacity too big",
|
||||
OverflowedStreamId => "stream ID overflowed",
|
||||
|
||||
@@ -12,6 +12,10 @@ use string::String;
|
||||
use std::fmt;
|
||||
use std::io::Cursor;
|
||||
|
||||
// Minimum MAX_FRAME_SIZE is 16kb, so save some arbitrary space for frame
|
||||
// head and other header bits.
|
||||
const MAX_HEADER_LENGTH: usize = 1024 * 16 - 100;
|
||||
|
||||
/// Header frame
|
||||
///
|
||||
/// This could be either a request or a response.
|
||||
@@ -232,6 +236,10 @@ impl Headers {
|
||||
self.header_block.is_over_size
|
||||
}
|
||||
|
||||
pub(crate) fn has_too_big_field(&self) -> bool {
|
||||
self.header_block.has_too_big_field()
|
||||
}
|
||||
|
||||
pub fn into_parts(self) -> (Pseudo, HeaderMap) {
|
||||
(self.header_block.pseudo, self.header_block.fields)
|
||||
}
|
||||
@@ -844,6 +852,46 @@ impl HeaderBlock {
|
||||
.map(|(name, value)| decoded_header_size(name.as_str().len(), value.len()))
|
||||
.sum::<usize>()
|
||||
}
|
||||
|
||||
/// Iterate over all pseudos and headers to see if any individual pair
|
||||
/// would be too large to encode.
|
||||
pub(crate) fn has_too_big_field(&self) -> bool {
|
||||
macro_rules! pseudo_size {
|
||||
($name:ident) => ({
|
||||
self.pseudo
|
||||
.$name
|
||||
.as_ref()
|
||||
.map(|m| decoded_header_size(stringify!($name).len() + 1, m.as_str().len()))
|
||||
.unwrap_or(0)
|
||||
});
|
||||
}
|
||||
|
||||
if pseudo_size!(method) > MAX_HEADER_LENGTH {
|
||||
return true;
|
||||
}
|
||||
|
||||
if pseudo_size!(scheme) > MAX_HEADER_LENGTH {
|
||||
return true;
|
||||
}
|
||||
|
||||
if pseudo_size!(authority) > MAX_HEADER_LENGTH {
|
||||
return true;
|
||||
}
|
||||
|
||||
if pseudo_size!(path) > MAX_HEADER_LENGTH {
|
||||
return true;
|
||||
}
|
||||
|
||||
// skip :status, its never going to be too big
|
||||
|
||||
for (name, value) in &self.fields {
|
||||
if decoded_header_size(name.as_str().len(), value.len()) > MAX_HEADER_LENGTH {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn decoded_header_size(name: usize, value: usize) -> usize {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
mod encoder;
|
||||
mod decoder;
|
||||
mod header;
|
||||
pub(crate) mod header;
|
||||
mod huffman;
|
||||
mod table;
|
||||
|
||||
|
||||
@@ -85,6 +85,10 @@ impl Send {
|
||||
}
|
||||
}
|
||||
|
||||
if frame.has_too_big_field() {
|
||||
return Err(UserError::HeaderTooBig);
|
||||
}
|
||||
|
||||
let end_stream = frame.is_end_stream();
|
||||
|
||||
// Update the state
|
||||
@@ -216,6 +220,10 @@ impl Send {
|
||||
return Err(UserError::UnexpectedFrameType);
|
||||
}
|
||||
|
||||
if frame.has_too_big_field() {
|
||||
return Err(UserError::HeaderTooBig);
|
||||
}
|
||||
|
||||
stream.state.send_close();
|
||||
|
||||
trace!("send_trailers -- queuing; frame={:?}", frame);
|
||||
|
||||
@@ -180,8 +180,9 @@ where
|
||||
Ok(()) => Ok(()),
|
||||
Err(RecvHeaderBlockError::Oversize(resp)) => {
|
||||
if let Some(resp) = resp {
|
||||
let _ = actions.send.send_headers(
|
||||
let sent = actions.send.send_headers(
|
||||
resp, send_buffer, stream, counts, &mut actions.task);
|
||||
debug_assert!(sent.is_ok(), "oversize response should not fail");
|
||||
|
||||
actions.send.schedule_implicit_reset(
|
||||
stream,
|
||||
|
||||
Reference in New Issue
Block a user