http2: split transaction state machines

Split into 2 sub-states:
- stream, which has the "HTTP" requests and responses, including DOH2
- global, which has the settings and other global or control handling

Introduce a simpler progress tracking for the global sub state:
- HTTP2ProgGlobalStart and HTTP2ProgGlobalComplete.

The stream sub state uses the same state machine as before.

Ticket: #8386.
pull/15846/head
Victor Julien 1 month ago
parent 2f41e0ef2d
commit 7ebc699df0

@ -79,6 +79,8 @@ include = [
"FtpStateValues",
"FtpDataStateValues",
"HTTP2TxProgress",
"HTTP2TxGlobalProgress",
"HTTP2TxType",
"DataRepType",
]

@ -16,7 +16,8 @@
*/
use super::http2::{
HTTP2Event, HTTP2Frame, HTTP2FrameTypeData, HTTP2State, HTTP2Transaction, HTTP2TxProgress,
HTTP2Event, HTTP2Frame, HTTP2FrameTypeData, HTTP2Progress, HTTP2State, HTTP2Transaction,
HTTP2TxProgress,
};
use super::parser;
use crate::detect::uint::{
@ -42,11 +43,7 @@ pub unsafe extern "C" fn SCHttp2TxHasFrametype(
} else {
&tx.frames_tc
};
let eof = if direction & Direction::ToServer as u8 != 0 {
tx.progress_ts >= HTTP2TxProgress::HTTP2ProgComplete
} else {
tx.progress_tc >= HTTP2TxProgress::HTTP2ProgComplete
};
let eof = tx.progress.is_complete_for_direction(direction);
return detect_uint_match_at_index::<HTTP2Frame, u8>(
frames,
ctx,
@ -89,11 +86,7 @@ pub unsafe extern "C" fn SCHttp2TxHasErrorCode(
} else {
&tx.frames_tc
};
let eof = if direction & Direction::ToServer as u8 != 0 {
tx.progress_ts >= HTTP2TxProgress::HTTP2ProgComplete
} else {
tx.progress_tc >= HTTP2TxProgress::HTTP2ProgComplete
};
let eof = tx.progress.is_complete_for_direction(direction);
return detect_uint_match_at_index::<HTTP2Frame, u32>(frames, ctx, http2_tx_get_errorcode, eof);
}
@ -147,11 +140,7 @@ fn http2_match_priority(
} else {
&tx.frames_tc
};
let eof = if direction == Direction::ToServer {
tx.progress_ts >= HTTP2TxProgress::HTTP2ProgComplete
} else {
tx.progress_tc >= HTTP2TxProgress::HTTP2ProgComplete
};
let eof = tx.progress.is_complete_for_direction(direction.into());
return detect_uint_match_at_index::<HTTP2Frame, u8>(frames, ctx, get_http2_priority, eof);
}
@ -180,11 +169,7 @@ fn http2_match_window(
&tx.frames_tc
};
// WINDOW_UPDATE frames may be sent in half-closed state
let eof = if direction == Direction::ToServer {
tx.progress_ts >= HTTP2TxProgress::HTTP2ProgComplete
} else {
tx.progress_tc >= HTTP2TxProgress::HTTP2ProgComplete
};
let eof = tx.progress.is_complete_for_direction(direction.into());
return detect_uint_match_at_index::<HTTP2Frame, u32>(frames, ctx, get_http2_window, eof);
}
@ -997,7 +982,9 @@ fn http2_tx_set_header(state: &mut HTTP2State, name: &[u8], input: &[u8]) {
data: txdata,
});
//we do not expect more data from client
tx.progress_ts = HTTP2TxProgress::HTTP2ProgClosed;
if let HTTP2Progress::STREAM(ref mut stream_tx) = tx.progress {
stream_tx.progress_ts = HTTP2TxProgress::HTTP2ProgClosed;
}
}
#[no_mangle]

@ -83,6 +83,13 @@ static mut HTTP2_MAX_STREAMS: usize = 4096; // 0x1000
static mut HTTP2_MAX_FRAMES: usize = 65536;
pub(super) static mut HTTP2_COMPRESSION_BOMB_LIMIT: u64 = 1_048_576;
#[repr(u8)]
#[derive(Copy, Clone, PartialOrd, PartialEq, Eq, Debug)]
pub enum HTTP2TxType {
HTTP2TxTypeStream = 1,
HTTP2TxTypeGlobal = 2,
}
#[derive(AppLayerFrameType)]
pub enum Http2FrameType {
Hdr,
@ -133,9 +140,14 @@ pub enum HTTP2TxProgress {
HTTP2ProgHeaders = 1,
HTTP2ProgData = 2,
HTTP2ProgClosed = 3,
HTTP2ProgComplete = 4,
//not a RFC-defined state, used for stream 0 frames applying to the global connection
HTTP2ProgGlobal = 5,
HTTP2ProgComplete = 4, // complete is a pseudo state set only when both sides are closed
}
#[repr(u8)]
#[derive(Copy, Clone, PartialOrd, PartialEq, Eq, Debug)]
pub enum HTTP2TxGlobalProgress {
HTTP2ProgGlobalStart = 0,
HTTP2ProgGlobalComplete = 1,
}
#[derive(Debug)]
@ -157,12 +169,122 @@ pub struct DohHttp2Tx {
pub dns_response_tx: Option<DNSTransaction>,
}
#[derive(Debug)]
pub struct HTTP2StreamProgress {
pub progress_ts: HTTP2TxProgress,
pub progress_tc: HTTP2TxProgress,
}
impl HTTP2StreamProgress {
fn init() -> Self {
Self {
progress_ts: HTTP2TxProgress::HTTP2ProgStart,
progress_tc: HTTP2TxProgress::HTTP2ProgStart,
}
}
fn complete() -> Self {
Self {
progress_ts: HTTP2TxProgress::HTTP2ProgComplete,
progress_tc: HTTP2TxProgress::HTTP2ProgComplete,
}
}
fn is_complete(&self) -> bool {
self.progress_ts >= HTTP2TxProgress::HTTP2ProgComplete
&& self.progress_tc >= HTTP2TxProgress::HTTP2ProgComplete
}
fn is_complete_for_direction(&self, direction: u8) -> bool {
if direction & Direction::ToServer as u8 != 0 {
self.progress_ts >= HTTP2TxProgress::HTTP2ProgComplete
} else {
self.progress_tc >= HTTP2TxProgress::HTTP2ProgComplete
}
}
}
#[derive(Debug)]
pub struct HTTP2GlobalProgress {
pub progress_ts: HTTP2TxGlobalProgress,
pub progress_tc: HTTP2TxGlobalProgress,
}
impl HTTP2GlobalProgress {
fn _init() -> Self {
Self {
progress_ts: HTTP2TxGlobalProgress::HTTP2ProgGlobalStart,
progress_tc: HTTP2TxGlobalProgress::HTTP2ProgGlobalStart,
}
}
fn complete() -> Self {
Self {
progress_ts: HTTP2TxGlobalProgress::HTTP2ProgGlobalComplete,
progress_tc: HTTP2TxGlobalProgress::HTTP2ProgGlobalComplete,
}
}
fn is_complete(&self) -> bool {
self.progress_ts >= HTTP2TxGlobalProgress::HTTP2ProgGlobalComplete
&& self.progress_tc >= HTTP2TxGlobalProgress::HTTP2ProgGlobalComplete
}
fn is_complete_for_direction(&self, direction: u8) -> bool {
if direction & Direction::ToServer as u8 != 0 {
self.progress_ts >= HTTP2TxGlobalProgress::HTTP2ProgGlobalComplete
} else {
self.progress_tc >= HTTP2TxGlobalProgress::HTTP2ProgGlobalComplete
}
}
}
#[derive(Debug)]
pub enum HTTP2Progress {
STREAM(HTTP2StreamProgress),
GLOBAL(HTTP2GlobalProgress),
}
impl HTTP2Progress {
fn get(&self, direction: u8) -> i32 {
if let HTTP2Progress::STREAM(ref s) = self {
if direction == STREAM_TOSERVER {
return s.progress_ts as i32;
} else {
return s.progress_tc as i32;
}
} else if let HTTP2Progress::GLOBAL(ref g) = self {
if direction == STREAM_TOSERVER {
return g.progress_ts as i32;
} else {
return g.progress_tc as i32;
}
}
0
}
fn is_complete(&self) -> bool {
let complete = if let HTTP2Progress::STREAM(ref s) = self {
s.is_complete()
} else if let HTTP2Progress::GLOBAL(ref g) = self {
g.is_complete()
} else {
false
};
complete
}
pub fn is_complete_for_direction(&self, direction: u8) -> bool {
let complete = if let HTTP2Progress::STREAM(ref s) = self {
s.is_complete_for_direction(direction)
} else if let HTTP2Progress::GLOBAL(ref g) = self {
g.is_complete_for_direction(direction)
} else {
false
};
complete
}
}
#[derive(Debug)]
pub struct HTTP2Transaction {
tx_id: u64,
pub stream_id: u32,
pub progress_tc: HTTP2TxProgress,
pub progress_ts: HTTP2TxProgress,
pub progress: HTTP2Progress,
to_drop: bool,
child_stream_id: u32,
@ -200,8 +322,7 @@ impl HTTP2Transaction {
tx_id: 0,
stream_id: 0,
child_stream_id: 0,
progress_tc: HTTP2TxProgress::HTTP2ProgStart,
progress_ts: HTTP2TxProgress::HTTP2ProgStart,
progress: HTTP2Progress::STREAM(HTTP2StreamProgress::init()),
to_drop: false,
frames_tc: Vec::new(),
frames_ts: Vec::new(),
@ -217,6 +338,7 @@ impl HTTP2Transaction {
}
pub fn free(&mut self) {
SCLogDebug!("free: tx_id {} stream_id {}", self.tx_id, self.stream_id);
if !self.file_range.is_null() {
if let Some(sfcm) = unsafe { SURICATA_HTTP2_FILE_CONFIG } {
//TODO get a file container instead of NULL
@ -407,8 +529,12 @@ impl HTTP2Transaction {
if header.flags & parser::HTTP2_FLAG_HEADER_END_HEADERS == 0 {
self.child_stream_id = hs.stream_id;
}
if self.progress_tc < HTTP2TxProgress::HTTP2ProgHeaders {
self.progress_tc = HTTP2TxProgress::HTTP2ProgHeaders;
if let HTTP2Progress::STREAM(ref mut stream_tx) = self.progress {
if stream_tx.progress_tc < HTTP2TxProgress::HTTP2ProgHeaders {
stream_tx.progress_tc = HTTP2TxProgress::HTTP2ProgHeaders;
}
} else {
panic!("global");
}
}
r = self.handle_headers(&hs.blocks, dir);
@ -432,34 +558,36 @@ impl HTTP2Transaction {
}
_ => {}
}
//handle closing state changes
let state = if dir == Direction::ToServer {
&mut self.progress_ts
} else {
&mut self.progress_tc
};
match data {
HTTP2FrameTypeData::HEADERS(_) | HTTP2FrameTypeData::DATA => {
if header.flags & parser::HTTP2_FLAG_HEADER_EOS != 0 {
if *state < HTTP2TxProgress::HTTP2ProgClosed {
*state = HTTP2TxProgress::HTTP2ProgClosed;
if self.progress_ts == HTTP2TxProgress::HTTP2ProgClosed
&& self.progress_tc == HTTP2TxProgress::HTTP2ProgClosed
{
self.progress_ts = HTTP2TxProgress::HTTP2ProgComplete;
self.progress_tc = HTTP2TxProgress::HTTP2ProgComplete;
if let HTTP2Progress::STREAM(ref mut stream_tx) = self.progress {
//handle closing state changes
let state = if dir == Direction::ToServer {
&mut stream_tx.progress_ts
} else {
&mut stream_tx.progress_tc
};
match data {
HTTP2FrameTypeData::HEADERS(_) | HTTP2FrameTypeData::DATA => {
if header.flags & parser::HTTP2_FLAG_HEADER_EOS != 0 {
if *state < HTTP2TxProgress::HTTP2ProgClosed {
*state = HTTP2TxProgress::HTTP2ProgClosed;
if stream_tx.progress_ts == HTTP2TxProgress::HTTP2ProgClosed
&& stream_tx.progress_tc == HTTP2TxProgress::HTTP2ProgClosed
{
stream_tx.progress_ts = HTTP2TxProgress::HTTP2ProgComplete;
stream_tx.progress_tc = HTTP2TxProgress::HTTP2ProgComplete;
}
}
} else if header.ftype == parser::HTTP2FrameType::Data as u8 {
//not end of stream
if *state < HTTP2TxProgress::HTTP2ProgData {
*state = HTTP2TxProgress::HTTP2ProgData;
}
} else if *state < HTTP2TxProgress::HTTP2ProgHeaders {
*state = HTTP2TxProgress::HTTP2ProgHeaders;
}
} else if header.ftype == parser::HTTP2FrameType::Data as u8 {
//not end of stream
if *state < HTTP2TxProgress::HTTP2ProgData {
*state = HTTP2TxProgress::HTTP2ProgData;
}
} else if *state < HTTP2TxProgress::HTTP2ProgHeaders {
*state = HTTP2TxProgress::HTTP2ProgHeaders;
}
_ => {}
}
_ => {}
}
return r;
}
@ -753,10 +881,13 @@ impl HTTP2State {
//as it affects the global connection, there is no end to it
let mut tx = HTTP2Transaction::new();
tx.tx_data = AppLayerTxData::for_direction(dir);
tx.tx_data.0.tx_type = HTTP2TxType::HTTP2TxTypeGlobal as u8;
tx.tx_data.0.tx_type_eop_ts = HTTP2TxGlobalProgress::HTTP2ProgGlobalComplete as u8;
tx.tx_data.0.tx_type_eop_tc = HTTP2TxGlobalProgress::HTTP2ProgGlobalComplete as u8;
self.tx_id += 1;
tx.tx_id = self.tx_id;
tx.progress_tc = HTTP2TxProgress::HTTP2ProgGlobal;
tx.progress_ts = HTTP2TxProgress::HTTP2ProgGlobal;
tx.progress = HTTP2Progress::GLOBAL(HTTP2GlobalProgress::complete());
SCLogDebug!("global tx created {:?}", tx);
// a global tx (stream id 0) does not hold files cf RFC 9113 section 5.1.1
self.transactions.push_back(tx);
return self.transactions.back_mut().unwrap();
@ -774,8 +905,7 @@ impl HTTP2State {
}
tx_old.set_event(HTTP2Event::TooManyStreams);
// use a distinct state, even if we do not log it
tx_old.progress_ts = HTTP2TxProgress::HTTP2ProgComplete;
tx_old.progress_tc = HTTP2TxProgress::HTTP2ProgComplete;
tx_old.progress = HTTP2Progress::STREAM(HTTP2StreamProgress::complete());
tx_old.to_drop = true;
tx_old.tx_data.0.updated_tc = true;
tx_old.tx_data.0.updated_ts = true;
@ -799,9 +929,7 @@ impl HTTP2State {
};
let index = self.find_tx_index(sid);
if index > 0 {
if self.transactions[index - 1].progress_tc >= HTTP2TxProgress::HTTP2ProgClosed
&& self.transactions[index - 1].progress_ts >= HTTP2TxProgress::HTTP2ProgClosed
{
if self.transactions[index - 1].progress.is_complete() {
//these frames can be received in this state for a short period
if header.ftype != parser::HTTP2FrameType::RstStream as u8
&& header.ftype != parser::HTTP2FrameType::WindowUpdate as u8
@ -827,8 +955,7 @@ impl HTTP2State {
}
tx_old.set_event(HTTP2Event::TooManyStreams);
// use a distinct state, even if we do not log it
tx_old.progress_ts = HTTP2TxProgress::HTTP2ProgComplete;
tx_old.progress_tc = HTTP2TxProgress::HTTP2ProgComplete;
tx_old.progress = HTTP2Progress::STREAM(HTTP2StreamProgress::complete());
tx_old.to_drop = true;
tx_old.tx_data.0.updated_tc = true;
tx_old.tx_data.0.updated_ts = true;
@ -842,6 +969,9 @@ impl HTTP2State {
tx.tx_data.update_file_flags(self.state_data.file_flags);
tx.update_file_flags(tx.tx_data.0.file_flags);
tx.tx_data.0.file_tx = STREAM_TOSERVER | STREAM_TOCLIENT; // might hold files in both directions
tx.tx_data.0.tx_type = HTTP2TxType::HTTP2TxTypeStream as u8;
tx.tx_data.0.tx_type_eop_ts = HTTP2TxProgress::HTTP2ProgComplete as u8;
tx.tx_data.0.tx_type_eop_tc = HTTP2TxProgress::HTTP2ProgComplete as u8;
self.transactions.push_back(tx);
return Some(self.transactions.back_mut().unwrap());
}
@ -1249,6 +1379,12 @@ impl HTTP2State {
return AppLayerResult::err();
}
let tx = tx.unwrap();
SCLogDebug!(
"tx stream_id {} tx id {} progress {:?}",
tx.stream_id,
tx.tx_id,
tx.progress
);
if let Some(frame) = frame_hdr {
frame.set_tx(flow, tx.tx_id);
}
@ -1529,11 +1665,7 @@ unsafe extern "C" fn http2_tx_get_alstate_progress(
tx: *mut std::os::raw::c_void, direction: u8,
) -> std::os::raw::c_int {
let tx = cast_pointer!(tx, HTTP2Transaction);
if direction == STREAM_TOSERVER {
return tx.progress_ts as i32;
} else {
return tx.progress_tc as i32;
}
return tx.progress.get(direction);
}
unsafe extern "C" fn http2_getfiles(

@ -398,15 +398,12 @@ void DetectHttpHeaderRegister(void)
PrefilterMpmHttpHeaderResponseRegister, NULL, ALPROTO_HTTP1,
0); /* not used, registered twice: HEADERS/TRAILER */
DetectAppLayerInspectEngineRegister("http_header", ALPROTO_HTTP2, SIG_FLAG_TOSERVER,
HTTP2ProgHeaders, DetectEngineInspectBufferGeneric, GetBuffer2ForTX);
DetectAppLayerMpmRegister("http_header", SIG_FLAG_TOSERVER, 2, PrefilterGenericMpmRegister,
GetBuffer2ForTX, ALPROTO_HTTP2, HTTP2ProgHeaders);
DetectAppLayerInspectEngineRegister("http_header", ALPROTO_HTTP2, SIG_FLAG_TOCLIENT,
HTTP2ProgHeaders, DetectEngineInspectBufferGeneric, GetBuffer2ForTX);
DetectAppLayerMpmRegister("http_header", SIG_FLAG_TOCLIENT, 2, PrefilterGenericMpmRegister,
GetBuffer2ForTX, ALPROTO_HTTP2, HTTP2ProgHeaders);
/* header is for stream TX type */
DetectAppLayerInspectEngineRegisterSubState("http_header", ALPROTO_HTTP2, SIG_FLAG_TOCLIENT,
HTTP2TxTypeStream, HTTP2ProgHeaders, DetectEngineInspectBufferGeneric, GetBuffer2ForTX);
DetectAppLayerMpmRegisterSubState("http_header", SIG_FLAG_TOCLIENT, 2,
PrefilterGenericMpmRegister, GetBuffer2ForTX, ALPROTO_HTTP2, HTTP2TxTypeStream,
HTTP2ProgHeaders);
DetectBufferTypeSetDescriptionByName("http_header",
"http headers");
@ -574,8 +571,9 @@ void DetectHttpRequestHeaderRegister(void)
sigmatch_table[DETECT_HTTP_REQUEST_HEADER].flags |=
SIGMATCH_NOOPT | SIGMATCH_INFO_STICKY_BUFFER | SIGMATCH_INFO_MULTI_BUFFER;
DetectAppLayerMultiRegister("http_request_header", ALPROTO_HTTP2, SIG_FLAG_TOSERVER,
HTTP2ProgHeaders, GetHttp2HeaderData, 2);
DetectAppLayerMultiRegisterSubState("http_request_header", ALPROTO_HTTP2, SIG_FLAG_TOSERVER,
HTTP2TxTypeStream, HTTP2ProgHeaders, GetHttp2HeaderData, 2);
DetectAppLayerMultiRegister("http_request_header", ALPROTO_HTTP1, SIG_FLAG_TOSERVER,
HTP_REQUEST_PROGRESS_HEADERS, GetHttp1HeaderData, 2);

@ -183,29 +183,40 @@ void DetectHttp2Register(void)
sigmatch_table[DETECT_HTTP2_HEADERNAME].flags |=
SIGMATCH_NOOPT | SIGMATCH_INFO_STICKY_BUFFER | SIGMATCH_INFO_MULTI_BUFFER;
DetectAppLayerMultiRegister("http2_header_name", ALPROTO_HTTP2, SIG_FLAG_TOCLIENT,
HTTP2ProgHeaders, SCHttp2TxGetHeaderName, 2);
DetectAppLayerMultiRegister("http2_header_name", ALPROTO_HTTP2, SIG_FLAG_TOSERVER,
HTTP2ProgHeaders, SCHttp2TxGetHeaderName, 2);
DetectBufferTypeSupportsMultiInstance("http2_header_name");
DetectBufferTypeSetDescriptionByName("http2_header_name",
"HTTP2 header name");
g_http2_header_buffer_id = DetectBufferTypeGetByName("http2_header_name");
DetectAppLayerInspectEngineRegister(
"http2", ALPROTO_HTTP2, SIG_FLAG_TOSERVER, 0, DetectEngineInspectGenericList, NULL);
DetectAppLayerInspectEngineRegister(
"http2", ALPROTO_HTTP2, SIG_FLAG_TOCLIENT, 0, DetectEngineInspectGenericList, NULL);
g_http2_match_buffer_id = DetectBufferTypeRegister("http2");
DetectAppLayerInspectEngineRegister("http2_complete", ALPROTO_HTTP2, SIG_FLAG_TOSERVER,
HTTP2ProgComplete, DetectEngineInspectGenericList, NULL);
DetectAppLayerInspectEngineRegister("http2_complete", ALPROTO_HTTP2, SIG_FLAG_TOCLIENT,
HTTP2ProgComplete, DetectEngineInspectGenericList, NULL);
g_http2_complete_buffer_id = DetectBufferTypeRegister("http2_complete");
/* registration for for Stream Tx Sub State */
DetectAppLayerMultiRegisterSubState("http2:header_name", ALPROTO_HTTP2, SIG_FLAG_TOCLIENT,
HTTP2TxTypeStream, HTTP2ProgHeaders, SCHttp2TxGetHeaderName, 2);
DetectAppLayerMultiRegisterSubState("http2:header_name", ALPROTO_HTTP2, SIG_FLAG_TOSERVER,
HTTP2TxTypeStream, HTTP2ProgHeaders, SCHttp2TxGetHeaderName, 2);
DetectBufferTypeSupportsMultiInstance("http2:header_name");
DetectBufferTypeSetDescriptionByName("http2:header_name", "HTTP2 header name");
g_http2_header_buffer_id = DetectBufferTypeGetByName("http2:header_name");
g_http2_match_buffer_id = DetectBufferTypeRegister("http2:start");
/* registration for for Stream Tx Sub State */
DetectAppLayerInspectEngineRegisterSubState("http2:start", ALPROTO_HTTP2, SIG_FLAG_TOSERVER,
HTTP2TxTypeStream, 0, DetectEngineInspectGenericList, NULL);
DetectAppLayerInspectEngineRegisterSubState("http2:start", ALPROTO_HTTP2, SIG_FLAG_TOCLIENT,
HTTP2TxTypeStream, 0, DetectEngineInspectGenericList, NULL);
/* registration for for Global Tx Sub State */
DetectAppLayerInspectEngineRegisterSubState("http2:start", ALPROTO_HTTP2, SIG_FLAG_TOSERVER,
HTTP2TxTypeGlobal, 0, DetectEngineInspectGenericList, NULL);
DetectAppLayerInspectEngineRegisterSubState("http2:start", ALPROTO_HTTP2, SIG_FLAG_TOCLIENT,
HTTP2TxTypeGlobal, 0, DetectEngineInspectGenericList, NULL);
g_http2_complete_buffer_id = DetectBufferTypeRegister("http2:complete");
/* registration for for Stream Tx Sub State */
DetectAppLayerInspectEngineRegisterSubState("http2:complete", ALPROTO_HTTP2, SIG_FLAG_TOSERVER,
HTTP2TxTypeStream, HTTP2ProgComplete, DetectEngineInspectGenericList, NULL);
DetectAppLayerInspectEngineRegisterSubState("http2:complete", ALPROTO_HTTP2, SIG_FLAG_TOCLIENT,
HTTP2TxTypeStream, HTTP2ProgComplete, DetectEngineInspectGenericList, NULL);
/* registration for for Global Tx Sub State */
DetectAppLayerInspectEngineRegisterSubState("http2:complete", ALPROTO_HTTP2, SIG_FLAG_TOSERVER,
HTTP2TxTypeGlobal, HTTP2ProgGlobalComplete, DetectEngineInspectGenericList, NULL);
DetectAppLayerInspectEngineRegisterSubState("http2:complete", ALPROTO_HTTP2, SIG_FLAG_TOCLIENT,
HTTP2TxTypeGlobal, HTTP2ProgGlobalComplete, DetectEngineInspectGenericList, NULL);
}
/**

Loading…
Cancel
Save