const HTTP2_FRAME_PRIORITY_LEN: usize = 5;
const HTTP2_FRAME_WINDOWUPDATE_LEN: usize = 4;
pub static mut HTTP2_MAX_TABLESIZE: u32 = 65536; // 0x10000
+// maximum size of reassembly for header + continuation
+static mut HTTP2_MAX_REASS: usize = 102400;
static mut HTTP2_MAX_STREAMS: usize = 4096; // 0x1000
#[repr(u8)]
TooManyStreams,
AuthorityHostMismatch,
UserinfoInUri,
+ ReassemblyLimitReached,
}
pub struct HTTP2DynTable {
}
}
+#[derive(Default)]
+struct HTTP2HeaderReassemblyBuffer {
+ data: Vec<u8>,
+ stream_id: u32,
+}
+
pub struct HTTP2State {
state_data: AppLayerStateData,
tx_id: u64,
dynamic_headers_tc: HTTP2DynTable,
transactions: VecDeque<HTTP2Transaction>,
progress: HTTP2ConnectionState,
+
+ c2s_buf: HTTP2HeaderReassemblyBuffer,
+ s2c_buf: HTTP2HeaderReassemblyBuffer,
}
impl State<HTTP2Transaction> for HTTP2State {
dynamic_headers_tc: HTTP2DynTable::new(),
transactions: VecDeque::new(),
progress: HTTP2ConnectionState::Http2StateInit,
+ c2s_buf: HTTP2HeaderReassemblyBuffer::default(),
+ s2c_buf: HTTP2HeaderReassemblyBuffer::default(),
}
}
}
fn parse_frame_data(
- &mut self, ftype: u8, input: &[u8], complete: bool, hflags: u8, dir: Direction,
+ &mut self, head: &parser::HTTP2FrameHeader, input: &[u8], complete: bool, dir: Direction,
+ reass_limit_reached: &mut bool,
) -> HTTP2FrameTypeData {
+ let ftype = head.ftype;
+ let hflags = head.flags;
match num::FromPrimitive::from_u8(ftype) {
Some(parser::HTTP2FrameType::GoAway) => {
if input.len() < HTTP2_FRAME_GOAWAY_LEN {
return HTTP2FrameTypeData::DATA;
}
Some(parser::HTTP2FrameType::Continuation) => {
+ let buf = if dir == Direction::ToClient {
+ &mut self.s2c_buf
+ } else {
+ &mut self.c2s_buf
+ };
+ if head.stream_id == buf.stream_id {
+ let max_reass = unsafe { HTTP2_MAX_REASS };
+ if buf.data.len() + input.len() < max_reass {
+ buf.data.extend(input);
+ } else if buf.data.len() < max_reass {
+ buf.data.extend(&input[..max_reass - buf.data.len()]);
+ *reass_limit_reached = true;
+ }
+ if head.flags & parser::HTTP2_FLAG_HEADER_END_HEADERS == 0 {
+ let hs = parser::HTTP2FrameContinuation {
+ blocks: Vec::new(),
+ };
+ return HTTP2FrameTypeData::CONTINUATION(hs);
+ }
+ } // else try to parse anyways
+ let input_reass = if head.stream_id == buf.stream_id { &buf.data } else { input };
+
let dyn_headers = if dir == Direction::ToClient {
&mut self.dynamic_headers_tc
} else {
&mut self.dynamic_headers_ts
};
- match parser::http2_parse_frame_continuation(input, dyn_headers) {
+ match parser::http2_parse_frame_continuation(input_reass, dyn_headers) {
Ok((_, hs)) => {
+ if head.stream_id == buf.stream_id {
+ buf.stream_id = 0;
+ buf.data.clear();
+ }
self.process_headers(&hs.blocks, dir);
return HTTP2FrameTypeData::CONTINUATION(hs);
}
Err(Err::Incomplete(_)) => {
+ if head.stream_id == buf.stream_id {
+ buf.stream_id = 0;
+ buf.data.clear();
+ }
if complete {
self.set_event(HTTP2Event::InvalidFrameData);
return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
}
}
Err(_) => {
+ if head.stream_id == buf.stream_id {
+ buf.stream_id = 0;
+ buf.data.clear();
+ }
self.set_event(HTTP2Event::InvalidFrameData);
return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
reason: HTTP2FrameUnhandledReason::ParsingError,
}
}
Some(parser::HTTP2FrameType::Headers) => {
+ if head.flags & parser::HTTP2_FLAG_HEADER_END_HEADERS == 0 {
+ let buf = if dir == Direction::ToClient {
+ &mut self.s2c_buf
+ } else {
+ &mut self.c2s_buf
+ };
+ buf.data.clear();
+ buf.data.extend(input);
+ buf.stream_id = head.stream_id;
+ let hs = parser::HTTP2FrameHeaders {
+ padlength: None,
+ priority: None,
+ blocks: Vec::new(),
+ };
+ return HTTP2FrameTypeData::HEADERS(hs);
+ }
let dyn_headers = if dir == Direction::ToClient {
&mut self.dynamic_headers_tc
} else {
input = &rem[hlsafe..];
continue;
}
+ let mut reass_limit_reached = false;
let txdata = self.parse_frame_data(
- head.ftype,
+ &head,
&rem[..hlsafe],
complete,
- head.flags,
dir,
+ &mut reass_limit_reached,
);
let tx = self.find_or_create_tx(&head, &txdata, dir);
+ if reass_limit_reached {
+ tx.tx_data.set_event(HTTP2Event::ReassemblyLimitReached as u8);
+ }
tx.handle_frame(&head, &txdata, dir);
let over = head.flags & parser::HTTP2_FLAG_HEADER_EOS != 0;
let ftype = head.ftype;
SCLogError!("Invalid value for http2.max-table-size");
}
}
+ if let Some(val) = conf_get("app-layer.protocols.http2.max-reassembly-size") {
+ if let Ok(v) = val.parse::<u32>() {
+ HTTP2_MAX_REASS = v as usize;
+ } else {
+ SCLogError!("Invalid value for http2.max-reassembly-size");
+ }
+ }
SCLogDebug!("Rust http2 parser registered.");
} else {
SCLogNotice!("Protocol detector and parser disabled for HTTP2.");