use super::decompression;
use super::detect;
use super::parser;
use super::range;
use crate::applayer::{self, *};
use crate::conf::{conf_get, get_memval};
use crate::core::*;
use crate::direction::Direction;
use crate::dns::dns::DnsVariant;
use crate::filecontainer::*;
use crate::filetracker::*;
use crate::flow::Flow;
use crate::frames::Frame;
use crate::dns::dns::{dns_parse_request, dns_parse_response, DNSTransaction};
use nom7::Err;
use std;
use std::collections::VecDeque;
use std::ffi::CString;
use std::fmt;
use std::io;
use suricata_sys::sys::{
AppLayerParserState, AppProto, SCAppLayerForceProtocolChange,
SCAppLayerParserConfParserEnabled, SCAppLayerParserRegisterLogger,
SCAppLayerProtoDetectConfProtoDetectionEnabled,
};
use suricata_sys::sys::AppProtoEnum::ALPROTO_HTTP1;
static mut ALPROTO_HTTP2: AppProto = ALPROTO_UNKNOWN;
static mut ALPROTO_DOH2: AppProto = ALPROTO_UNKNOWN;
const HTTP2_DEFAULT_MAX_FRAME_SIZE: u32 = 16384;
const HTTP2_MAX_HANDLED_FRAME_SIZE: usize = 65536;
const HTTP2_MIN_HANDLED_FRAME_SIZE: usize = 256;
pub static mut SURICATA_HTTP2_FILE_CONFIG: Option<&'static SuricataFileContext> = None;
#[no_mangle]
pub extern "C" fn SCHttp2Init(context: &'static mut SuricataFileContext) {
unsafe {
SURICATA_HTTP2_FILE_CONFIG = Some(context);
}
}
#[repr(u8)]
#[derive(Copy, Clone, PartialOrd, PartialEq, Eq)]
pub enum HTTP2ConnectionState {
Http2StateInit = 0,
Http2StateMagicDone = 1,
}
const HTTP2_FRAME_HEADER_LEN: usize = 9;
const HTTP2_MAGIC_LEN: usize = 24;
const HTTP2_FRAME_GOAWAY_LEN: usize = 8;
const HTTP2_FRAME_RSTSTREAM_LEN: usize = 4;
const HTTP2_FRAME_PRIORITY_LEN: usize = 5;
const HTTP2_FRAME_WINDOWUPDATE_LEN: usize = 4;
pub static mut HTTP2_MAX_TABLESIZE: u32 = 65536; static mut HTTP2_MAX_REASS: usize = 102400;
static mut HTTP2_MAX_STREAMS: usize = 4096; static mut HTTP2_MAX_FRAMES: usize = 65536;
pub(super) static mut HTTP2_COMPRESSION_BOMB_LIMIT: u64 = 1_048_576;
#[derive(AppLayerFrameType)]
pub enum Http2FrameType {
Hdr,
Data,
Pdu,
}
#[repr(u8)]
#[derive(Copy, Clone, PartialOrd, PartialEq, Eq, Debug)]
pub enum HTTP2FrameUnhandledReason {
UnknownType = 0,
TooLong = 1,
ParsingError = 2,
Incomplete = 3,
}
impl fmt::Display for HTTP2FrameUnhandledReason {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}
#[derive(Debug)]
pub struct HTTP2FrameUnhandled {
pub reason: HTTP2FrameUnhandledReason,
}
#[derive(Debug)]
pub enum HTTP2FrameTypeData {
PRIORITY(parser::HTTP2FramePriority),
GOAWAY(parser::HTTP2FrameGoAway),
RSTSTREAM(parser::HTTP2FrameRstStream),
SETTINGS(Vec<parser::HTTP2FrameSettings>),
WINDOWUPDATE(parser::HTTP2FrameWindowUpdate),
HEADERS(parser::HTTP2FrameHeaders),
PUSHPROMISE(parser::HTTP2FramePushPromise),
CONTINUATION(parser::HTTP2FrameContinuation),
PING,
DATA,
UNHANDLED(HTTP2FrameUnhandled),
}
#[repr(u8)]
#[derive(Copy, Clone, PartialOrd, PartialEq, Eq, Debug)]
pub enum HTTP2TransactionState {
HTTP2StateIdle = 0,
HTTP2StateOpen = 1,
HTTP2StateReserved = 2,
HTTP2StateDataClient = 3,
HTTP2StateHalfClosedClient = 4,
HTTP2StateDataServer = 5,
HTTP2StateHalfClosedServer = 6,
HTTP2StateClosed = 7,
HTTP2StateGlobal = 8,
HTTP2StateTodrop = 9,
}
#[derive(Debug)]
pub struct HTTP2Frame {
pub header: parser::HTTP2FrameHeader,
pub data: HTTP2FrameTypeData,
}
#[derive(Debug, Default)]
pub struct DohHttp2Tx {
is_doh_data: [bool; 2],
pub data_buf: [Vec<u8>; 2],
pub dns_request_tx: Option<DNSTransaction>,
pub dns_response_tx: Option<DNSTransaction>,
}
#[derive(Debug)]
pub struct HTTP2Transaction {
tx_id: u64,
pub stream_id: u32,
pub state: HTTP2TransactionState,
child_stream_id: u32,
pub frames_tc: Vec<HTTP2Frame>,
pub frames_ts: Vec<HTTP2Frame>,
decoder: decompression::HTTP2Decoder,
pub file_range: *mut HttpRangeContainerBlock,
pub tx_data: AppLayerTxData,
pub ft_tc: FileTransferTracker,
pub ft_ts: FileTransferTracker,
pub req_line: Vec<u8>,
pub resp_line: Vec<u8>,
pub doh: Option<DohHttp2Tx>,
}
impl Transaction for HTTP2Transaction {
fn id(&self) -> u64 {
self.tx_id
}
}
impl Default for HTTP2Transaction {
fn default() -> Self {
Self::new()
}
}
impl HTTP2Transaction {
pub fn new() -> Self {
Self {
tx_id: 0,
stream_id: 0,
child_stream_id: 0,
state: HTTP2TransactionState::HTTP2StateIdle,
frames_tc: Vec::new(),
frames_ts: Vec::new(),
decoder: decompression::HTTP2Decoder::new(),
file_range: std::ptr::null_mut(),
tx_data: AppLayerTxData::new(),
ft_tc: FileTransferTracker::new(),
ft_ts: FileTransferTracker::new(),
req_line: Vec::new(),
resp_line: Vec::new(),
doh: None,
}
}
pub fn free(&mut self) {
if !self.file_range.is_null() {
if let Some(c) = unsafe { SC } {
if let Some(sfcm) = unsafe { SURICATA_HTTP2_FILE_CONFIG } {
(c.HTPFileCloseHandleRange)(
sfcm.files_sbcfg,
std::ptr::null_mut(),
0,
self.file_range,
std::ptr::null_mut(),
0,
);
(c.HttpRangeFreeBlock)(self.file_range);
self.file_range = std::ptr::null_mut();
}
}
}
}
pub fn set_event(&mut self, event: HTTP2Event) {
self.tx_data.set_event(event as u8);
}
fn handle_headers(
&mut self, blocks: &[parser::HTTP2FrameHeaderBlock], dir: Direction,
) -> Option<Vec<u8>> {
let mut authority = None;
let mut path = None;
let mut doh = false;
let mut host = None;
for block in blocks {
if block.name.as_ref() == b"content-encoding" {
self.decoder.http2_encoding_fromvec(&block.value, dir);
} else if block.name.as_ref() == b"accept" {
if block.value.as_ref() == b"application/dns-message" {
doh = true;
}
} else if block.name.as_ref() == b"content-type" {
if block.value.as_ref() == b"application/dns-message" {
if let Some(doh) = &mut self.doh {
doh.is_doh_data[dir.index()] = true;
} else {
let mut doh = DohHttp2Tx::default();
doh.is_doh_data[dir.index()] = true;
self.doh = Some(doh);
}
}
} else if block.name.as_ref() == b":path" {
path = Some(&block.value);
} else if block.name.eq_ignore_ascii_case(b":authority") {
authority = Some(&block.value);
if block.value.contains(&b'@') {
self.set_event(HTTP2Event::UserinfoInUri);
}
} else if block.name.eq_ignore_ascii_case(b"host") {
host = Some(&block.value);
}
}
if let Some(a) = authority {
if let Some(h) = host {
if !a.eq_ignore_ascii_case(h) {
self.set_event(HTTP2Event::AuthorityHostMismatch);
}
}
}
if doh && unsafe { ALPROTO_DOH2 } != ALPROTO_UNKNOWN {
if let Some(p) = path {
if let Ok((_, dns_req)) = parser::doh_extract_request(p) {
return Some(dns_req);
}
}
}
return None;
}
pub fn update_file_flags(&mut self, flow_file_flags: u16) {
self.ft_ts.file_flags = unsafe { FileFlowFlagsToFlags(flow_file_flags, STREAM_TOSERVER) };
self.ft_tc.file_flags = unsafe { FileFlowFlagsToFlags(flow_file_flags, STREAM_TOCLIENT) };
}
fn decompress<'a>(
&'a mut self, input: &'a [u8], output: &'a mut Vec<u8>, dir: Direction,
sfcm: &'static SuricataFileContext, over: bool, flow: *const Flow,
) -> io::Result<()> {
let decompressed = self.decoder.decompress(input, output, dir)?;
let xid: u32 = self.tx_id as u32;
if dir == Direction::ToClient {
self.ft_tc.tx_id = self.tx_id - 1;
if !self.ft_tc.is_initialized() {
self.tx_data.incr_files_opened();
if let Ok(value) = detect::http2_frames_get_header_value_vec(
self,
Direction::ToClient,
"content-range",
) {
match range::http2_parse_check_content_range(&value) {
Ok((_, v)) => {
range::http2_range_open(
self,
&v,
flow,
sfcm,
Direction::ToClient,
decompressed,
);
if over && !self.file_range.is_null() {
range::http2_range_close(self, Direction::ToClient, &[])
}
}
_ => {
self.set_event(HTTP2Event::InvalidRange);
}
}
}
} else if !self.file_range.is_null() {
if over {
range::http2_range_close(self, Direction::ToClient, decompressed)
} else {
range::http2_range_append(sfcm, self.file_range, decompressed)
}
}
self.ft_tc.new_chunk(
sfcm,
b"",
decompressed,
self.ft_tc.tracked, decompressed.len() as u32,
0,
over,
&xid,
);
} else {
self.ft_ts.tx_id = self.tx_id - 1;
if !self.ft_ts.file_open {
self.tx_data.incr_files_opened();
}
self.ft_ts.new_chunk(
sfcm,
b"",
decompressed,
self.ft_ts.tracked, decompressed.len() as u32,
0,
over,
&xid,
);
};
if unsafe { ALPROTO_DOH2 } != ALPROTO_UNKNOWN {
if let Some(doh) = &mut self.doh {
if doh.is_doh_data[dir.index()] {
if doh.data_buf[dir.index()].len() + decompressed.len() <= 0xFFFF {
doh.data_buf[dir.index()].extend_from_slice(decompressed);
} else {
doh.is_doh_data[dir.index()] = false;
if dir == Direction::ToClient {
self.set_event(HTTP2Event::DnsResponseTooLong);
} else {
self.set_event(HTTP2Event::DnsRequestTooLong);
}
}
}
}
}
return Ok(());
}
fn handle_frame(
&mut self, header: &parser::HTTP2FrameHeader, data: &HTTP2FrameTypeData, dir: Direction,
) -> Option<Vec<u8>> {
let mut r = None;
match data {
HTTP2FrameTypeData::PUSHPROMISE(hs) => {
if dir == Direction::ToClient {
if header.flags & parser::HTTP2_FLAG_HEADER_END_HEADERS == 0 {
self.child_stream_id = hs.stream_id;
}
self.state = HTTP2TransactionState::HTTP2StateReserved;
}
r = self.handle_headers(&hs.blocks, dir);
}
HTTP2FrameTypeData::CONTINUATION(hs) => {
if dir == Direction::ToClient
&& header.flags & parser::HTTP2_FLAG_HEADER_END_HEADERS != 0
{
self.child_stream_id = 0;
}
r = self.handle_headers(&hs.blocks, dir);
}
HTTP2FrameTypeData::HEADERS(hs) => {
if dir == Direction::ToClient {
self.child_stream_id = 0;
}
r = self.handle_headers(&hs.blocks, dir);
}
HTTP2FrameTypeData::RSTSTREAM(_) => {
self.child_stream_id = 0;
}
_ => {}
}
match data {
HTTP2FrameTypeData::HEADERS(_) | HTTP2FrameTypeData::DATA => {
if header.flags & parser::HTTP2_FLAG_HEADER_EOS != 0 {
match self.state {
HTTP2TransactionState::HTTP2StateHalfClosedClient
| HTTP2TransactionState::HTTP2StateDataServer => {
if dir == Direction::ToClient {
self.state = HTTP2TransactionState::HTTP2StateClosed;
}
}
HTTP2TransactionState::HTTP2StateHalfClosedServer => {
if dir == Direction::ToServer {
self.state = HTTP2TransactionState::HTTP2StateClosed;
}
}
HTTP2TransactionState::HTTP2StateClosed => {}
HTTP2TransactionState::HTTP2StateGlobal => {}
_ => {
if dir == Direction::ToClient {
self.state = HTTP2TransactionState::HTTP2StateHalfClosedServer;
} else {
self.state = HTTP2TransactionState::HTTP2StateHalfClosedClient;
}
}
}
} else if header.ftype == parser::HTTP2FrameType::Data as u8 {
if dir == Direction::ToServer {
if self.state < HTTP2TransactionState::HTTP2StateDataClient {
self.state = HTTP2TransactionState::HTTP2StateDataClient;
}
} else if self.state < HTTP2TransactionState::HTTP2StateDataServer {
self.state = HTTP2TransactionState::HTTP2StateDataServer;
}
}
}
_ => {}
}
return r;
}
fn handle_dns_data(&mut self, dir: Direction, flow: *mut Flow) {
if let Some(doh) = &mut self.doh {
if !doh.data_buf[dir.index()].is_empty() {
if dir.is_to_client() {
if let Ok(mut dtx) = dns_parse_response(&doh.data_buf[dir.index()]) {
dtx.id = 1;
doh.dns_response_tx = Some(dtx);
unsafe {
SCAppLayerForceProtocolChange(flow, ALPROTO_DOH2);
}
}
} else if let Ok(mut dtx) =
dns_parse_request(&doh.data_buf[dir.index()], &DnsVariant::Dns)
{
dtx.id = 1;
doh.dns_request_tx = Some(dtx);
unsafe {
SCAppLayerForceProtocolChange(flow, ALPROTO_DOH2);
}
}
}
}
}
fn handle_data_frame(
&mut self, rem: &[u8], hlsafe: usize, dir: Direction, flow: *mut Flow, padded: bool,
over: bool,
) {
match unsafe { SURICATA_HTTP2_FILE_CONFIG } {
Some(sfcm) => {
if dir == Direction::ToServer {
self.ft_tc.tx_id = self.tx_id - 1;
} else {
self.ft_ts.tx_id = self.tx_id - 1;
};
let mut dinput = &rem[..hlsafe];
if padded && !rem.is_empty() && usize::from(rem[0]) < hlsafe {
dinput = &rem[1..hlsafe - usize::from(rem[0])];
}
let mut output = Vec::with_capacity(decompression::HTTP2_DECOMPRESSION_CHUNK_SIZE);
match self.decompress(dinput, &mut output, dir, sfcm, over, flow) {
Ok(_) => {
if over {
self.handle_dns_data(dir, flow);
}
}
Err(e) => {
if e.kind() == io::ErrorKind::OutOfMemory {
self.set_event(HTTP2Event::CompressionBomb);
} else {
self.set_event(HTTP2Event::FailedDecompression);
}
}
}
}
None => panic!("no SURICATA_HTTP2_FILE_CONFIG"),
}
}
}
impl Drop for HTTP2Transaction {
fn drop(&mut self) {
if let Some(sfcm) = unsafe { SURICATA_HTTP2_FILE_CONFIG } {
self.ft_ts.file.free(sfcm);
self.ft_tc.file.free(sfcm);
}
self.free();
}
}
#[derive(AppLayerEvent)]
pub enum HTTP2Event {
InvalidFrameHeader,
InvalidClientMagic,
InvalidFrameData,
InvalidHeader,
InvalidFrameLength,
ExtraHeaderData,
LongFrameData,
StreamIdReuse,
InvalidHttp1Settings,
FailedDecompression,
InvalidRange,
HeaderIntegerOverflow,
TooManyStreams,
AuthorityHostMismatch,
UserinfoInUri,
ReassemblyLimitReached,
DnsRequestTooLong,
DnsResponseTooLong,
DataStreamZero,
TooManyFrames,
CompressionBomb,
}
pub struct HTTP2DynTable {
pub table: Vec<parser::HTTP2FrameHeaderBlock>,
pub current_size: usize,
pub max_size: usize,
pub overflow: u8,
}
impl Default for HTTP2DynTable {
fn default() -> Self {
Self::new()
}
}
impl HTTP2DynTable {
pub fn new() -> Self {
Self {
table: Vec::with_capacity(64),
current_size: 0,
max_size: 4096, overflow: 0,
}
}
}
#[derive(Default)]
struct HTTP2HeaderReassemblyBuffer {
data: Vec<u8>,
stream_id: u32,
}
pub struct HTTP2State {
state_data: AppLayerStateData,
tx_id: u64,
request_frame_size: u32,
response_frame_size: u32,
dynamic_headers_ts: HTTP2DynTable,
dynamic_headers_tc: HTTP2DynTable,
transactions: VecDeque<HTTP2Transaction>,
progress: HTTP2ConnectionState,
comp_len: u64,
decomp_len: u64,
c2s_buf: HTTP2HeaderReassemblyBuffer,
s2c_buf: HTTP2HeaderReassemblyBuffer,
}
impl State<HTTP2Transaction> for HTTP2State {
fn get_transaction_count(&self) -> usize {
self.transactions.len()
}
fn get_transaction_by_index(&self, index: usize) -> Option<&HTTP2Transaction> {
self.transactions.get(index)
}
}
impl Default for HTTP2State {
fn default() -> Self {
Self::new()
}
}
impl HTTP2State {
pub fn new() -> Self {
Self {
state_data: AppLayerStateData::new(),
tx_id: 0,
request_frame_size: 0,
response_frame_size: 0,
dynamic_headers_ts: HTTP2DynTable::new(),
dynamic_headers_tc: HTTP2DynTable::new(),
transactions: VecDeque::new(),
progress: HTTP2ConnectionState::Http2StateInit,
comp_len: 0,
decomp_len: 0,
c2s_buf: HTTP2HeaderReassemblyBuffer::default(),
s2c_buf: HTTP2HeaderReassemblyBuffer::default(),
}
}
pub fn free(&mut self) {
for tx in &mut self.transactions {
if !tx.file_range.is_null() {
if let Some(c) = unsafe { SC } {
if let Some(sfcm) = unsafe { SURICATA_HTTP2_FILE_CONFIG } {
(c.HTPFileCloseHandleRange)(
sfcm.files_sbcfg,
&mut tx.ft_tc.file,
0,
tx.file_range,
std::ptr::null_mut(),
0,
);
(c.HttpRangeFreeBlock)(tx.file_range);
tx.file_range = std::ptr::null_mut();
}
}
}
}
self.transactions.clear();
}
pub fn set_event(&mut self, event: HTTP2Event) {
let len = self.transactions.len();
if len == 0 {
return;
}
let tx = &mut self.transactions[len - 1];
tx.tx_data.set_event(event as u8);
}
fn free_tx(&mut self, tx_id: u64) {
let len = self.transactions.len();
let mut found = false;
let mut index = 0;
for i in 0..len {
let tx = &mut self.transactions[i];
if tx.tx_id == tx_id + 1 {
found = true;
index = i;
if !tx.file_range.is_null() {
if let Some(c) = unsafe { SC } {
if let Some(sfcm) = unsafe { SURICATA_HTTP2_FILE_CONFIG } {
(c.HTPFileCloseHandleRange)(
sfcm.files_sbcfg,
&mut tx.ft_tc.file,
0,
tx.file_range,
std::ptr::null_mut(),
0,
);
(c.HttpRangeFreeBlock)(tx.file_range);
tx.file_range = std::ptr::null_mut();
}
}
}
break;
}
}
if found {
self.transactions.remove(index);
}
}
pub fn get_tx(&mut self, tx_id: u64) -> Option<&HTTP2Transaction> {
for tx in &mut self.transactions {
if tx.tx_id == tx_id + 1 {
tx.tx_data.update_file_flags(self.state_data.file_flags);
tx.update_file_flags(tx.tx_data.file_flags);
return Some(tx);
}
}
return None;
}
fn find_tx_index(&mut self, sid: u32) -> usize {
for i in 0..self.transactions.len() {
let idx = self.transactions.len() - 1 - i;
if sid == self.transactions[idx].stream_id {
return idx + 1;
}
}
return 0;
}
fn find_child_stream_id(&mut self, sid: u32) -> u32 {
for i in 0..self.transactions.len() {
if sid == self.transactions[self.transactions.len() - 1 - i].stream_id {
if self.transactions[self.transactions.len() - 1 - i].child_stream_id > 0 {
return self.transactions[self.transactions.len() - 1 - i].child_stream_id;
}
return sid;
}
}
return sid;
}
fn create_global_tx(&mut self) -> &mut HTTP2Transaction {
let mut tx = HTTP2Transaction::new();
self.tx_id += 1;
tx.tx_id = self.tx_id;
tx.state = HTTP2TransactionState::HTTP2StateGlobal;
self.transactions.push_back(tx);
return self.transactions.back_mut().unwrap();
}
pub fn find_or_create_tx(
&mut self, header: &parser::HTTP2FrameHeader, data: &HTTP2FrameTypeData, dir: Direction,
) -> Option<&mut HTTP2Transaction> {
if header.stream_id == 0 {
if self.transactions.len() >= unsafe { HTTP2_MAX_STREAMS } {
for tx_old in &mut self.transactions {
if tx_old.state == HTTP2TransactionState::HTTP2StateTodrop {
break;
}
tx_old.set_event(HTTP2Event::TooManyStreams);
tx_old.state = HTTP2TransactionState::HTTP2StateTodrop;
tx_old.tx_data.updated_tc = true;
tx_old.tx_data.updated_ts = true;
}
return None;
}
return Some(self.create_global_tx());
}
let sid = match data {
HTTP2FrameTypeData::PUSHPROMISE(hs) => hs.stream_id,
HTTP2FrameTypeData::CONTINUATION(_) => {
if dir == Direction::ToClient {
self.find_child_stream_id(header.stream_id)
} else {
header.stream_id
}
}
_ => header.stream_id,
};
let index = self.find_tx_index(sid);
if index > 0 {
if self.transactions[index - 1].state == HTTP2TransactionState::HTTP2StateClosed {
if header.ftype != parser::HTTP2FrameType::RstStream as u8
&& header.ftype != parser::HTTP2FrameType::WindowUpdate as u8
&& header.ftype != parser::HTTP2FrameType::Priority as u8
{
self.set_event(HTTP2Event::StreamIdReuse);
}
}
let tx = &mut self.transactions[index - 1];
tx.tx_data.update_file_flags(self.state_data.file_flags);
tx.update_file_flags(tx.tx_data.file_flags);
tx.tx_data.updated_tc = true;
tx.tx_data.updated_ts = true;
return Some(tx);
} else {
if self.transactions.len() >= unsafe { HTTP2_MAX_STREAMS } {
for tx_old in &mut self.transactions {
if tx_old.state == HTTP2TransactionState::HTTP2StateTodrop {
break;
}
tx_old.set_event(HTTP2Event::TooManyStreams);
tx_old.state = HTTP2TransactionState::HTTP2StateTodrop;
tx_old.tx_data.updated_tc = true;
tx_old.tx_data.updated_ts = true;
}
return None;
}
let mut tx = HTTP2Transaction::new();
self.tx_id += 1;
tx.tx_id = self.tx_id;
tx.stream_id = sid;
tx.state = HTTP2TransactionState::HTTP2StateOpen;
tx.tx_data.update_file_flags(self.state_data.file_flags);
tx.update_file_flags(tx.tx_data.file_flags);
tx.tx_data.file_tx = STREAM_TOSERVER | STREAM_TOCLIENT; self.transactions.push_back(tx);
return Some(self.transactions.back_mut().unwrap());
}
}
fn process_headers(&mut self, blocks: &Vec<parser::HTTP2FrameHeaderBlock>, dir: Direction) {
let (mut update, mut sizeup) = (false, 0);
for block in blocks {
if block.error >= parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeError {
self.set_event(HTTP2Event::InvalidHeader);
} else if block.error == parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate {
update = true;
if block.sizeupdate > sizeup {
sizeup = block.sizeupdate;
}
} else if block.error
== parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeIntegerOverflow
{
self.set_event(HTTP2Event::HeaderIntegerOverflow);
}
}
if update {
let dyn_headers = if dir == Direction::ToClient {
&mut self.dynamic_headers_tc
} else {
&mut self.dynamic_headers_ts
};
dyn_headers.max_size = sizeup as usize;
}
}
fn parse_frame_data(
&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 {
self.set_event(HTTP2Event::InvalidFrameLength);
return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
reason: HTTP2FrameUnhandledReason::Incomplete,
});
}
match parser::http2_parse_frame_goaway(input) {
Ok((_, goaway)) => {
return HTTP2FrameTypeData::GOAWAY(goaway);
}
Err(_) => {
self.set_event(HTTP2Event::InvalidFrameData);
return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
reason: HTTP2FrameUnhandledReason::ParsingError,
});
}
}
}
Some(parser::HTTP2FrameType::Settings) => {
match parser::http2_parse_frame_settings(input) {
Ok((_, set)) => {
for e in &set {
if e.id == parser::HTTP2SettingsId::HeaderTableSize {
let dyn_headers = if dir == Direction::ToClient {
&mut self.dynamic_headers_ts
} else {
&mut self.dynamic_headers_tc
};
dyn_headers.max_size = e.value as usize;
if e.value > unsafe { HTTP2_MAX_TABLESIZE } {
dyn_headers.overflow = 1;
} else {
dyn_headers.overflow = 0;
}
}
}
return HTTP2FrameTypeData::SETTINGS(set);
}
Err(Err::Incomplete(_)) => {
if complete {
self.set_event(HTTP2Event::InvalidFrameData);
return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
reason: HTTP2FrameUnhandledReason::ParsingError,
});
} else {
return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
reason: HTTP2FrameUnhandledReason::TooLong,
});
}
}
Err(_) => {
self.set_event(HTTP2Event::InvalidFrameData);
return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
reason: HTTP2FrameUnhandledReason::ParsingError,
});
}
}
}
Some(parser::HTTP2FrameType::RstStream) => {
if input.len() != HTTP2_FRAME_RSTSTREAM_LEN {
self.set_event(HTTP2Event::InvalidFrameLength);
return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
reason: HTTP2FrameUnhandledReason::Incomplete,
});
} else {
match parser::http2_parse_frame_rststream(input) {
Ok((_, rst)) => {
return HTTP2FrameTypeData::RSTSTREAM(rst);
}
Err(_) => {
self.set_event(HTTP2Event::InvalidFrameData);
return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
reason: HTTP2FrameUnhandledReason::ParsingError,
});
}
}
}
}
Some(parser::HTTP2FrameType::Priority) => {
if input.len() != HTTP2_FRAME_PRIORITY_LEN {
self.set_event(HTTP2Event::InvalidFrameLength);
return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
reason: HTTP2FrameUnhandledReason::Incomplete,
});
} else {
match parser::http2_parse_frame_priority(input) {
Ok((_, priority)) => {
return HTTP2FrameTypeData::PRIORITY(priority);
}
Err(_) => {
self.set_event(HTTP2Event::InvalidFrameData);
return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
reason: HTTP2FrameUnhandledReason::ParsingError,
});
}
}
}
}
Some(parser::HTTP2FrameType::WindowUpdate) => {
if input.len() != HTTP2_FRAME_WINDOWUPDATE_LEN {
self.set_event(HTTP2Event::InvalidFrameLength);
return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
reason: HTTP2FrameUnhandledReason::Incomplete,
});
} else {
match parser::http2_parse_frame_windowupdate(input) {
Ok((_, wu)) => {
return HTTP2FrameTypeData::WINDOWUPDATE(wu);
}
Err(_) => {
self.set_event(HTTP2Event::InvalidFrameData);
return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
reason: HTTP2FrameUnhandledReason::ParsingError,
});
}
}
}
}
Some(parser::HTTP2FrameType::PushPromise) => {
let dyn_headers = if dir == Direction::ToClient {
&mut self.dynamic_headers_tc
} else {
&mut self.dynamic_headers_ts
};
match parser::http2_parse_frame_push_promise(input, hflags, dyn_headers) {
Ok((_, hs)) => {
self.process_headers(&hs.blocks, dir);
return HTTP2FrameTypeData::PUSHPROMISE(hs);
}
Err(Err::Incomplete(_)) => {
if complete {
self.set_event(HTTP2Event::InvalidFrameData);
return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
reason: HTTP2FrameUnhandledReason::ParsingError,
});
} else {
return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
reason: HTTP2FrameUnhandledReason::TooLong,
});
}
}
Err(_) => {
self.set_event(HTTP2Event::InvalidFrameData);
return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
reason: HTTP2FrameUnhandledReason::ParsingError,
});
}
}
}
Some(parser::HTTP2FrameType::Data) => {
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);
}
} 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_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 {
reason: HTTP2FrameUnhandledReason::ParsingError,
});
} else {
return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
reason: HTTP2FrameUnhandledReason::TooLong,
});
}
}
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 {
&mut self.dynamic_headers_ts
};
match parser::http2_parse_frame_headers(input, hflags, dyn_headers) {
Ok((hrem, hs)) => {
self.process_headers(&hs.blocks, dir);
if !hrem.is_empty() {
SCLogDebug!("Remaining data for HTTP2 headers");
self.set_event(HTTP2Event::ExtraHeaderData);
}
return HTTP2FrameTypeData::HEADERS(hs);
}
Err(Err::Incomplete(_)) => {
if complete {
self.set_event(HTTP2Event::InvalidFrameData);
return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
reason: HTTP2FrameUnhandledReason::ParsingError,
});
} else {
return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
reason: HTTP2FrameUnhandledReason::TooLong,
});
}
}
Err(_) => {
self.set_event(HTTP2Event::InvalidFrameData);
return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
reason: HTTP2FrameUnhandledReason::ParsingError,
});
}
}
}
Some(parser::HTTP2FrameType::Ping) => {
return HTTP2FrameTypeData::PING;
}
_ => {
return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
reason: HTTP2FrameUnhandledReason::UnknownType,
});
}
}
}
fn parse_frames(
&mut self, mut input: &[u8], il: usize, dir: Direction, flow: *mut Flow,
stream_slice: &StreamSlice,
) -> AppLayerResult {
while !input.is_empty() {
match parser::http2_parse_frame_header(input) {
Ok((rem, head)) => {
let hl = head.length as usize;
if rem.len() < hl {
if hl < HTTP2_MAX_HANDLED_FRAME_SIZE {
return AppLayerResult::incomplete(
(il - input.len()) as u32,
(HTTP2_FRAME_HEADER_LEN + hl) as u32,
);
} else if rem.len() < HTTP2_MIN_HANDLED_FRAME_SIZE {
return AppLayerResult::incomplete(
(il - input.len()) as u32,
(HTTP2_FRAME_HEADER_LEN + HTTP2_MIN_HANDLED_FRAME_SIZE) as u32,
);
} else {
self.set_event(HTTP2Event::LongFrameData);
if dir == Direction::ToServer {
self.request_frame_size = head.length - (rem.len() as u32);
} else {
self.response_frame_size = head.length - (rem.len() as u32);
}
}
}
let (hlsafe, complete) = if rem.len() < hl {
(rem.len(), false)
} else {
(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;
}
let mut reass_limit_reached = false;
let txdata = self.parse_frame_data(
&head,
&rem[..hlsafe],
complete,
dir,
&mut reass_limit_reached,
);
let (comp_len, decomp_len) = (self.comp_len, self.decomp_len);
let tx = self.find_or_create_tx(&head, &txdata, dir);
if tx.is_none() {
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, &DnsVariant::Dns) {
dtx.id = 1;
unsafe {
SCAppLayerForceProtocolChange(flow, ALPROTO_DOH2);
}
if let Some(doh) = &mut tx.doh {
doh.dns_request_tx = Some(dtx);
} else {
let doh = DohHttp2Tx {
dns_request_tx: Some(dtx),
..Default::default()
};
tx.doh = Some(doh);
}
}
}
if reass_limit_reached {
tx.tx_data
.set_event(HTTP2Event::ReassemblyLimitReached as u8);
}
let over = head.flags & parser::HTTP2_FLAG_HEADER_EOS != 0;
let ftype = head.ftype;
let sid = head.stream_id;
let padded = head.flags & parser::HTTP2_FLAG_HEADER_PADDED != 0;
let h2frames = if dir == Direction::ToServer {
&mut tx.frames_ts
} else {
&mut tx.frames_tc
};
if h2frames.len() < unsafe { HTTP2_MAX_FRAMES } {
h2frames.push(HTTP2Frame {
header: head,
data: txdata,
});
} else {
tx.tx_data.set_event(HTTP2Event::TooManyFrames as u8);
}
if ftype == parser::HTTP2FrameType::Data as u8 && sid == 0 {
tx.tx_data.set_event(HTTP2Event::DataStreamZero as u8);
} else if ftype == parser::HTTP2FrameType::Data as u8 && sid > 0 {
tx.handle_data_frame(rem, hlsafe, dir, flow, padded, over);
let (il, ol) = if dir == Direction::ToClient {
(
tx.decoder.decoder_tc.input_len,
tx.decoder.decoder_tc.output_len,
)
} else {
(
tx.decoder.decoder_ts.input_len,
tx.decoder.decoder_ts.output_len,
)
};
let (il, ol) = (il + comp_len, ol + decomp_len);
if ol > decompression::DEFAULT_BOMB_RATIO * il {
if ol > unsafe { HTTP2_COMPRESSION_BOMB_LIMIT } {
tx.set_event(HTTP2Event::CompressionBomb);
return AppLayerResult::err();
}
if over {
self.comp_len += il;
self.decomp_len += ol;
}
}
}
sc_app_layer_parser_trigger_raw_stream_inspection(flow, dir as i32);
input = &rem[hlsafe..];
}
Err(Err::Incomplete(_)) => {
return AppLayerResult::incomplete(
(il - input.len()) as u32,
HTTP2_FRAME_HEADER_LEN as u32,
);
}
Err(_) => {
self.set_event(HTTP2Event::InvalidFrameHeader);
return AppLayerResult::err();
}
}
}
return AppLayerResult::ok();
}
fn parse_ts(&mut self, flow: *mut Flow, stream_slice: StreamSlice) -> AppLayerResult {
let mut input = stream_slice.as_slice();
let mut magic_consumed = 0;
if self.progress < HTTP2ConnectionState::Http2StateMagicDone {
if input.len() >= HTTP2_MAGIC_LEN {
match std::str::from_utf8(&input[..HTTP2_MAGIC_LEN]) {
Ok("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n") => {
input = &input[HTTP2_MAGIC_LEN..];
magic_consumed = HTTP2_MAGIC_LEN;
}
Ok(&_) => {
self.set_event(HTTP2Event::InvalidClientMagic);
}
Err(_) => {
return AppLayerResult::err();
}
}
self.progress = HTTP2ConnectionState::Http2StateMagicDone;
} else {
return AppLayerResult::incomplete(0_u32, HTTP2_MAGIC_LEN as u32);
}
}
let il = input.len();
if self.request_frame_size > 0 {
let ilen = input.len() as u32;
if self.request_frame_size >= ilen {
self.request_frame_size -= ilen;
return AppLayerResult::ok();
} else {
let start = self.request_frame_size as usize;
input = &input[start..];
self.request_frame_size = 0;
}
}
let r = self.parse_frames(input, il, Direction::ToServer, flow, &stream_slice);
if r.status == 1 {
return AppLayerResult::incomplete(r.consumed + magic_consumed as u32, r.needed);
} else {
return r;
}
}
fn parse_tc(&mut self, flow: *mut Flow, stream_slice: StreamSlice) -> AppLayerResult {
let mut input = stream_slice.as_slice();
let il = input.len();
if self.response_frame_size > 0 {
let ilen = input.len() as u32;
if self.response_frame_size >= ilen {
self.response_frame_size -= ilen;
return AppLayerResult::ok();
} else {
let start = self.response_frame_size as usize;
input = &input[start..];
self.response_frame_size = 0;
}
}
return self.parse_frames(input, il, Direction::ToClient, flow, &stream_slice);
}
}
#[no_mangle]
pub unsafe extern "C" fn SCDoH2GetDnsTx(
tx: &HTTP2Transaction, flags: u8,
) -> *mut std::os::raw::c_void {
if let Some(doh) = &tx.doh {
if flags & Direction::ToServer as u8 != 0 {
if let Some(ref dtx) = &doh.dns_request_tx {
return dtx as *const _ as *mut _;
}
} else if flags & Direction::ToClient as u8 != 0 {
if let Some(ref dtx) = &doh.dns_response_tx {
return dtx as *const _ as *mut _;
}
}
}
std::ptr::null_mut()
}
export_tx_data_get!(http2_get_tx_data, HTTP2Transaction);
export_state_data_get!(http2_get_state_data, HTTP2State);
unsafe extern "C" fn http2_probing_parser_tc(
_flow: *const Flow, _direction: u8, input: *const u8, input_len: u32, _rdir: *mut u8,
) -> AppProto {
if !input.is_null() {
let slice = build_slice!(input, input_len as usize);
match parser::http2_parse_frame_header(slice) {
Ok((_, header)) => {
if header.reserved != 0
|| header.length > HTTP2_DEFAULT_MAX_FRAME_SIZE
|| header.flags & 0xFE != 0
|| header.ftype != parser::HTTP2FrameType::Settings as u8
{
return ALPROTO_FAILED;
}
return ALPROTO_HTTP2;
}
Err(Err::Incomplete(_)) => {
return ALPROTO_UNKNOWN;
}
Err(_) => {
return ALPROTO_FAILED;
}
}
}
return ALPROTO_UNKNOWN;
}
extern "C" {
pub fn HTTP2MimicHttp1Request(
orig_state: *mut std::os::raw::c_void, new_state: *mut std::os::raw::c_void,
);
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
extern "C" fn http2_state_new(
orig_state: *mut std::os::raw::c_void, orig_proto: AppProto,
) -> *mut std::os::raw::c_void {
let state = HTTP2State::new();
let boxed = Box::new(state);
let r = Box::into_raw(boxed) as *mut _;
if !orig_state.is_null() && orig_proto == ALPROTO_HTTP1 as u16 {
unsafe {
HTTP2MimicHttp1Request(orig_state, r);
}
}
return r;
}
unsafe extern "C" fn http2_state_free(state: *mut std::os::raw::c_void) {
let mut state: Box<HTTP2State> = Box::from_raw(state as _);
state.free();
}
unsafe extern "C" fn http2_state_tx_free(state: *mut std::os::raw::c_void, tx_id: u64) {
let state = cast_pointer!(state, HTTP2State);
state.free_tx(tx_id);
}
unsafe extern "C" fn http2_parse_ts(
flow: *mut Flow, state: *mut std::os::raw::c_void, _pstate: *mut AppLayerParserState,
stream_slice: StreamSlice, _data: *const std::os::raw::c_void,
) -> AppLayerResult {
let state = cast_pointer!(state, HTTP2State);
return state.parse_ts(flow, stream_slice);
}
unsafe extern "C" fn http2_parse_tc(
flow: *mut Flow, state: *mut std::os::raw::c_void, _pstate: *mut AppLayerParserState,
stream_slice: StreamSlice, _data: *const std::os::raw::c_void,
) -> AppLayerResult {
let state = cast_pointer!(state, HTTP2State);
return state.parse_tc(flow, stream_slice);
}
unsafe extern "C" fn http2_state_get_tx(
state: *mut std::os::raw::c_void, tx_id: u64,
) -> *mut std::os::raw::c_void {
let state = cast_pointer!(state, HTTP2State);
match state.get_tx(tx_id) {
Some(tx) => {
return tx as *const _ as *mut _;
}
None => {
return std::ptr::null_mut();
}
}
}
unsafe extern "C" fn http2_state_get_tx_count(state: *mut std::os::raw::c_void) -> u64 {
let state = cast_pointer!(state, HTTP2State);
return state.tx_id;
}
unsafe extern "C" fn http2_tx_get_state(tx: *mut std::os::raw::c_void) -> HTTP2TransactionState {
let tx = cast_pointer!(tx, HTTP2Transaction);
return tx.state;
}
unsafe extern "C" fn http2_tx_get_alstate_progress(
tx: *mut std::os::raw::c_void, _direction: u8,
) -> std::os::raw::c_int {
return http2_tx_get_state(tx) as i32;
}
unsafe extern "C" fn http2_getfiles(
tx: *mut std::os::raw::c_void, direction: u8,
) -> AppLayerGetFileState {
let tx = cast_pointer!(tx, HTTP2Transaction);
if let Some(sfcm) = { SURICATA_HTTP2_FILE_CONFIG } {
if direction & STREAM_TOSERVER != 0 {
return AppLayerGetFileState {
fc: &mut tx.ft_ts.file,
cfg: sfcm.files_sbcfg,
};
} else {
return AppLayerGetFileState {
fc: &mut tx.ft_tc.file,
cfg: sfcm.files_sbcfg,
};
}
}
AppLayerGetFileState::err()
}
const PARSER_NAME: &[u8] = b"http2\0";
#[no_mangle]
pub unsafe extern "C" fn SCRegisterHttp2Parser() {
let default_port = CString::new("[80]").unwrap();
let mut parser = RustParser {
name: PARSER_NAME.as_ptr() as *const std::os::raw::c_char,
default_port: default_port.as_ptr(),
ipproto: IPPROTO_TCP,
probe_ts: None, probe_tc: Some(http2_probing_parser_tc),
min_depth: HTTP2_FRAME_HEADER_LEN as u16,
max_depth: HTTP2_MAGIC_LEN as u16,
state_new: http2_state_new,
state_free: http2_state_free,
tx_free: http2_state_tx_free,
parse_ts: http2_parse_ts,
parse_tc: http2_parse_tc,
get_tx_count: http2_state_get_tx_count,
get_tx: http2_state_get_tx,
tx_comp_st_ts: HTTP2TransactionState::HTTP2StateClosed as i32,
tx_comp_st_tc: HTTP2TransactionState::HTTP2StateClosed as i32,
tx_get_progress: http2_tx_get_alstate_progress,
get_eventinfo: Some(HTTP2Event::get_event_info),
get_eventinfo_byid: Some(HTTP2Event::get_event_info_by_id),
localstorage_new: None,
localstorage_free: None,
get_tx_files: Some(http2_getfiles),
get_tx_iterator: Some(applayer::state_get_tx_iterator::<HTTP2State, HTTP2Transaction>),
get_tx_data: http2_get_tx_data,
get_state_data: http2_get_state_data,
apply_tx_config: None,
flags: 0,
get_frame_id_by_name: Some(Http2FrameType::ffi_id_from_name),
get_frame_name_by_id: Some(Http2FrameType::ffi_name_from_id),
get_state_id_by_name: None,
get_state_name_by_id: None,
};
let ip_proto_str = CString::new("tcp").unwrap();
if SCAppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
let alproto = AppLayerRegisterProtocolDetection(&parser, 1);
ALPROTO_HTTP2 = alproto;
if SCAppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
let _ = AppLayerRegisterParser(&parser, alproto);
}
if let Some(val) = conf_get("app-layer.protocols.http2.max-streams") {
if let Ok(v) = val.parse::<usize>() {
HTTP2_MAX_STREAMS = v;
} else {
SCLogError!("Invalid value for http2.max-streams");
}
}
if let Some(val) = conf_get("app-layer.protocols.http2.max-frames") {
if let Ok(v) = val.parse::<usize>() {
HTTP2_MAX_FRAMES = v;
} else {
SCLogError!("Invalid value for http2.max-frames");
}
}
if let Some(val) = conf_get("app-layer.protocols.http2.max-table-size") {
if let Ok(v) = val.parse::<u32>() {
HTTP2_MAX_TABLESIZE = v;
} else {
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");
}
}
if let Some(val) = conf_get("app-layer.protocols.http2.compression-bomb-limit") {
if let Ok(v) = get_memval(val) {
HTTP2_COMPRESSION_BOMB_LIMIT = v;
} else {
SCLogWarning!("Invalid value for http2.compression-bomb-limit");
}
}
SCAppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_HTTP2);
SCLogDebug!("Rust http2 parser registered.");
} else {
SCLogNotice!("Protocol detector and parser disabled for HTTP2.");
}
parser.name = b"doh2\0".as_ptr() as *const std::os::raw::c_char;
parser.probe_tc = None;
parser.default_port = std::ptr::null();
if SCAppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
let alproto = AppLayerRegisterProtocolDetection(&parser, 1);
ALPROTO_DOH2 = alproto;
if SCAppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
let _ = AppLayerRegisterParser(&parser, alproto);
} else {
SCLogWarning!("DOH2 is not meant to be detection-only.");
}
SCAppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_DOH2);
SCLogDebug!("Rust doh2 parser registered.");
} else {
SCLogNotice!("Protocol detector and parser disabled for DOH2.");
}
}