http2: add frames support

Ticket: 5743

Why ? To add detection capabilities
pull/11755/head
Philippe Antoine 2 months ago committed by Victor Julien
parent 65392c02f5
commit 0ebb84538e

@ -4,6 +4,14 @@ HTTP2 Keywords
HTTP2 frames are grouped into transactions based on the stream identifier it it is not 0.
For frames with stream identifier 0, whose effects are global for the connection, a transaction is created for each frame.
Frames
------
The HTTP2 parser supports the following frames (as defined by Suricata) which are created for each HTTP2 frame (as defined by the HTTP2 RFC) :
* http2.hdr
* http2.data
* http2.pdu
http2.frametype
---------------

@ -25,6 +25,7 @@ use crate::conf::conf_get;
use crate::core::*;
use crate::filecontainer::*;
use crate::filetracker::*;
use crate::frames::Frame;
use crate::dns::dns::{dns_parse_request, dns_parse_response, DNSTransaction};
@ -69,6 +70,13 @@ pub static mut HTTP2_MAX_TABLESIZE: u32 = 65536; // 0x10000
static mut HTTP2_MAX_REASS: usize = 102400;
static mut HTTP2_MAX_STREAMS: usize = 4096; // 0x1000
#[derive(AppLayerFrameType)]
pub enum Http2FrameType {
Hdr,
Data,
Pdu,
}
#[repr(u8)]
#[derive(Copy, Clone, PartialOrd, PartialEq, Eq, Debug)]
pub enum HTTP2FrameUnhandledReason {
@ -1094,6 +1102,7 @@ impl HTTP2State {
fn parse_frames(
&mut self, mut input: &[u8], il: usize, dir: Direction, flow: *const Flow,
stream_slice: &StreamSlice,
) -> AppLayerResult {
while !input.is_empty() {
match parser::http2_parse_frame_header(input) {
@ -1126,6 +1135,30 @@ impl HTTP2State {
(hl, true)
};
let frame_hdr = Frame::new(
flow,
stream_slice,
input,
HTTP2_FRAME_HEADER_LEN as i64,
Http2FrameType::Hdr as u8,
None,
);
let frame_data = Frame::new(
flow,
stream_slice,
&input[HTTP2_FRAME_HEADER_LEN..],
head.length as i64,
Http2FrameType::Data as u8,
None,
);
let frame_pdu = Frame::new(
flow,
stream_slice,
input,
HTTP2_FRAME_HEADER_LEN as i64 + head.length as i64,
Http2FrameType::Pdu as u8,
None,
);
if head.length == 0 && head.ftype == parser::HTTP2FrameType::Settings as u8 {
input = &rem[hlsafe..];
continue;
@ -1144,6 +1177,15 @@ impl HTTP2State {
return AppLayerResult::err();
}
let tx = tx.unwrap();
if let Some(frame) = frame_hdr {
frame.set_tx(flow, tx.tx_id);
}
if let Some(frame) = frame_data {
frame.set_tx(flow, tx.tx_id);
}
if let Some(frame) = frame_pdu {
frame.set_tx(flow, tx.tx_id);
}
if let Some(doh_req_buf) = tx.handle_frame(&head, &txdata, dir) {
if let Ok(mut dtx) = dns_parse_request(&doh_req_buf) {
dtx.id = 1;
@ -1153,7 +1195,10 @@ impl HTTP2State {
if let Some(doh) = &mut tx.doh {
doh.dns_request_tx = Some(dtx);
} else {
let doh = DohHttp2Tx { dns_request_tx: Some(dtx), ..Default::default() };
let doh = DohHttp2Tx {
dns_request_tx: Some(dtx),
..Default::default()
};
tx.doh = Some(doh);
}
}
@ -1236,8 +1281,9 @@ impl HTTP2State {
return AppLayerResult::ok();
}
fn parse_ts(&mut self, mut input: &[u8], flow: *const Flow) -> AppLayerResult {
fn parse_ts(&mut self, flow: *const Flow, stream_slice: StreamSlice) -> AppLayerResult {
//very first : skip magic
let mut input = stream_slice.as_slice();
let mut magic_consumed = 0;
if self.progress < HTTP2ConnectionState::Http2StateMagicDone {
//skip magic
@ -1276,7 +1322,7 @@ impl HTTP2State {
}
//then parse all we can
let r = self.parse_frames(input, il, Direction::ToServer, flow);
let r = self.parse_frames(input, il, Direction::ToServer, flow, &stream_slice);
if r.status == 1 {
//adds bytes consumed by banner to incomplete result
return AppLayerResult::incomplete(r.consumed + magic_consumed as u32, r.needed);
@ -1285,8 +1331,9 @@ impl HTTP2State {
}
}
fn parse_tc(&mut self, mut input: &[u8], flow: *const Flow) -> AppLayerResult {
fn parse_tc(&mut self, flow: *const Flow, stream_slice: StreamSlice) -> AppLayerResult {
//first consume frame bytes
let mut input = stream_slice.as_slice();
let il = input.len();
if self.response_frame_size > 0 {
let ilen = input.len() as u32;
@ -1300,7 +1347,7 @@ impl HTTP2State {
}
}
//then parse all we can
return self.parse_frames(input, il, Direction::ToClient, flow);
return self.parse_frames(input, il, Direction::ToClient, flow, &stream_slice);
}
}
@ -1400,8 +1447,7 @@ pub unsafe extern "C" fn rs_http2_parse_ts(
stream_slice: StreamSlice, _data: *const std::os::raw::c_void,
) -> AppLayerResult {
let state = cast_pointer!(state, HTTP2State);
let buf = stream_slice.as_slice();
return state.parse_ts(buf, flow);
return state.parse_ts(flow, stream_slice);
}
#[no_mangle]
@ -1410,8 +1456,7 @@ pub unsafe extern "C" fn rs_http2_parse_tc(
stream_slice: StreamSlice, _data: *const std::os::raw::c_void,
) -> AppLayerResult {
let state = cast_pointer!(state, HTTP2State);
let buf = stream_slice.as_slice();
return state.parse_tc(buf, flow);
return state.parse_tc(flow, stream_slice);
}
#[no_mangle]
@ -1505,8 +1550,8 @@ pub unsafe extern "C" fn rs_http2_register_parser() {
get_state_data: rs_http2_get_state_data,
apply_tx_config: None,
flags: 0,
get_frame_id_by_name: None,
get_frame_name_by_id: None,
get_frame_id_by_name: Some(Http2FrameType::ffi_id_from_name),
get_frame_name_by_id: Some(Http2FrameType::ffi_name_from_id),
};
let ip_proto_str = CString::new("tcp").unwrap();

Loading…
Cancel
Save