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
|
/// The payload size is too big
|
||||||
PayloadTooBig,
|
PayloadTooBig,
|
||||||
|
|
||||||
|
/// A header size is too big
|
||||||
|
HeaderTooBig,
|
||||||
|
|
||||||
/// The application attempted to initiate too many streams to remote.
|
/// The application attempted to initiate too many streams to remote.
|
||||||
Rejected,
|
Rejected,
|
||||||
|
|
||||||
@@ -131,6 +134,7 @@ impl error::Error for UserError {
|
|||||||
InactiveStreamId => "inactive stream",
|
InactiveStreamId => "inactive stream",
|
||||||
UnexpectedFrameType => "unexpected frame type",
|
UnexpectedFrameType => "unexpected frame type",
|
||||||
PayloadTooBig => "payload too big",
|
PayloadTooBig => "payload too big",
|
||||||
|
HeaderTooBig => "header too big",
|
||||||
Rejected => "rejected",
|
Rejected => "rejected",
|
||||||
ReleaseCapacityTooBig => "release capacity too big",
|
ReleaseCapacityTooBig => "release capacity too big",
|
||||||
OverflowedStreamId => "stream ID overflowed",
|
OverflowedStreamId => "stream ID overflowed",
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ use string::String;
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io::Cursor;
|
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
|
/// Header frame
|
||||||
///
|
///
|
||||||
/// This could be either a request or a response.
|
/// This could be either a request or a response.
|
||||||
@@ -232,6 +236,10 @@ impl Headers {
|
|||||||
self.header_block.is_over_size
|
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) {
|
pub fn into_parts(self) -> (Pseudo, HeaderMap) {
|
||||||
(self.header_block.pseudo, self.header_block.fields)
|
(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()))
|
.map(|(name, value)| decoded_header_size(name.as_str().len(), value.len()))
|
||||||
.sum::<usize>()
|
.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 {
|
fn decoded_header_size(name: usize, value: usize) -> usize {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
mod encoder;
|
mod encoder;
|
||||||
mod decoder;
|
mod decoder;
|
||||||
mod header;
|
pub(crate) mod header;
|
||||||
mod huffman;
|
mod huffman;
|
||||||
mod table;
|
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();
|
let end_stream = frame.is_end_stream();
|
||||||
|
|
||||||
// Update the state
|
// Update the state
|
||||||
@@ -216,6 +220,10 @@ impl Send {
|
|||||||
return Err(UserError::UnexpectedFrameType);
|
return Err(UserError::UnexpectedFrameType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if frame.has_too_big_field() {
|
||||||
|
return Err(UserError::HeaderTooBig);
|
||||||
|
}
|
||||||
|
|
||||||
stream.state.send_close();
|
stream.state.send_close();
|
||||||
|
|
||||||
trace!("send_trailers -- queuing; frame={:?}", frame);
|
trace!("send_trailers -- queuing; frame={:?}", frame);
|
||||||
|
|||||||
@@ -180,8 +180,9 @@ where
|
|||||||
Ok(()) => Ok(()),
|
Ok(()) => Ok(()),
|
||||||
Err(RecvHeaderBlockError::Oversize(resp)) => {
|
Err(RecvHeaderBlockError::Oversize(resp)) => {
|
||||||
if let Some(resp) = 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);
|
resp, send_buffer, stream, counts, &mut actions.task);
|
||||||
|
debug_assert!(sent.is_ok(), "oversize response should not fail");
|
||||||
|
|
||||||
actions.send.schedule_implicit_reset(
|
actions.send.schedule_implicit_reset(
|
||||||
stream,
|
stream,
|
||||||
|
|||||||
Reference in New Issue
Block a user