*/
use super::decompression;
+use super::detect;
use super::parser;
+use super::range;
+
use crate::applayer::{self, *};
-use crate::core::{
- self, AppProto, Flow, SuricataFileContext, ALPROTO_FAILED, ALPROTO_UNKNOWN, IPPROTO_TCP,
- STREAM_TOCLIENT, STREAM_TOSERVER,
-};
+use crate::core::{self, *};
use crate::filecontainer::*;
use crate::filetracker::*;
use nom;
pub frames_ts: Vec<HTTP2Frame>,
decoder: decompression::HTTP2Decoder,
+ pub file_range: *mut HttpRangeContainerBlock,
- de_state: Option<*mut core::DetectEngineState>,
events: *mut core::AppLayerDecoderEvents,
tx_data: AppLayerTxData,
- ft_tc: FileTransferTracker,
+ pub ft_tc: FileTransferTracker,
ft_ts: FileTransferTracker,
//temporary escaped header for detection
pub escaped: Vec<Vec<u8>>,
}
+impl Transaction for HTTP2Transaction {
+ fn id(&self) -> u64 {
+ self.tx_id
+ }
+}
+
impl HTTP2Transaction {
pub fn new() -> HTTP2Transaction {
HTTP2Transaction {
frames_tc: Vec::new(),
frames_ts: Vec::new(),
decoder: decompression::HTTP2Decoder::new(),
- de_state: None,
+ file_range: std::ptr::null_mut(),
events: std::ptr::null_mut(),
tx_data: AppLayerTxData::new(),
ft_tc: FileTransferTracker::new(),
}
pub fn free(&mut self) {
- if self.events != std::ptr::null_mut() {
+ if !self.events.is_null() {
core::sc_app_layer_decoder_events_free_events(&mut self.events);
}
- if let Some(state) = self.de_state {
- core::sc_detect_engine_state_free(state);
+ if !self.file_range.is_null() {
+ match unsafe { SC } {
+ None => panic!("BUG no suricata_config"),
+ Some(c) => {
+ //TODO get a file container instead of NULL
+ (c.HTPFileCloseHandleRange)(
+ std::ptr::null_mut(),
+ 0,
+ self.file_range,
+ std::ptr::null_mut(),
+ 0,
+ );
+ (c.HttpRangeFreeBlock)(self.file_range);
+ }
+ }
}
}
- fn handle_headers(&mut self, blocks: &Vec<parser::HTTP2FrameHeaderBlock>, dir: u8) {
+ pub fn set_event(&mut self, event: HTTP2Event) {
+ let ev = event as u8;
+ core::sc_app_layer_decoder_events_set_event_raw(&mut self.events, ev);
+ }
+
+ fn handle_headers(&mut self, blocks: &Vec<parser::HTTP2FrameHeaderBlock>, dir: Direction) {
for i in 0..blocks.len() {
if blocks[i].name == "content-encoding".as_bytes().to_vec() {
self.decoder.http2_encoding_fromvec(&blocks[i].value, dir);
}
fn decompress<'a>(
- &'a mut self, input: &'a [u8], dir: u8, sfcm: &'static SuricataFileContext, over: bool,
- files: &mut FileContainer, flags: u16,
+ &'a mut self, input: &'a [u8], dir: Direction, sfcm: &'static SuricataFileContext,
+ over: bool, files: &mut FileContainer, flags: u16, flow: *const Flow,
) -> io::Result<()> {
let mut output = Vec::with_capacity(decompression::HTTP2_DECOMPRESSION_CHUNK_SIZE);
let decompressed = self.decoder.decompress(input, &mut output, dir)?;
let xid: u32 = self.tx_id as u32;
- if dir == STREAM_TOCLIENT {
+ if dir == Direction::ToClient {
+ self.ft_tc.tx_id = self.tx_id - 1;
+ if !self.ft_tc.file_open {
+ // we are now sure that new_chunk will open a file
+ // even if it may close it right afterwards
+ 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, flags, decompressed);
+ if over && !self.file_range.is_null() {
+ range::http2_range_close(self, files, flags, &[])
+ }
+ }
+ _ => {
+ self.set_event(HTTP2Event::InvalidRange);
+ }
+ }
+ }
+ } else {
+ if !self.file_range.is_null() {
+ if over {
+ range::http2_range_close(self, files, flags, decompressed)
+ } else {
+ range::http2_range_append(self.file_range, decompressed)
+ }
+ }
+ }
self.ft_tc.new_chunk(
sfcm,
files,
&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,
files,
}
fn handle_frame(
- &mut self, header: &parser::HTTP2FrameHeader, data: &HTTP2FrameTypeData, dir: u8,
+ &mut self, header: &parser::HTTP2FrameHeader, data: &HTTP2FrameTypeData, dir: Direction,
) {
//handle child_stream_id changes
match data {
HTTP2FrameTypeData::PUSHPROMISE(hs) => {
- if dir == STREAM_TOCLIENT {
+ if dir == Direction::ToClient {
//we could set an event if self.child_stream_id != 0
if header.flags & parser::HTTP2_FLAG_HEADER_END_HEADERS == 0 {
self.child_stream_id = hs.stream_id;
self.handle_headers(&hs.blocks, dir);
}
HTTP2FrameTypeData::CONTINUATION(hs) => {
- if dir == STREAM_TOCLIENT
+ if dir == Direction::ToClient
&& header.flags & parser::HTTP2_FLAG_HEADER_END_HEADERS != 0
{
self.child_stream_id = 0;
self.handle_headers(&hs.blocks, dir);
}
HTTP2FrameTypeData::HEADERS(hs) => {
- if dir == STREAM_TOCLIENT {
+ if dir == Direction::ToClient {
self.child_stream_id = 0;
}
self.handle_headers(&hs.blocks, dir);
match self.state {
HTTP2TransactionState::HTTP2StateHalfClosedClient
| HTTP2TransactionState::HTTP2StateDataServer => {
- if dir == STREAM_TOCLIENT {
+ if dir == Direction::ToClient {
self.state = HTTP2TransactionState::HTTP2StateClosed;
}
}
HTTP2TransactionState::HTTP2StateHalfClosedServer => {
- if dir == STREAM_TOSERVER {
+ if dir == Direction::ToServer {
self.state = HTTP2TransactionState::HTTP2StateClosed;
}
}
HTTP2TransactionState::HTTP2StateClosed => {}
HTTP2TransactionState::HTTP2StateGlobal => {}
_ => {
- if dir == STREAM_TOCLIENT {
+ if dir == Direction::ToClient {
self.state = HTTP2TransactionState::HTTP2StateHalfClosedServer;
} else {
self.state = HTTP2TransactionState::HTTP2StateHalfClosedClient;
}
} else if header.ftype == parser::HTTP2FrameType::DATA as u8 {
//not end of stream
- if dir == STREAM_TOSERVER {
+ if dir == Direction::ToServer {
if self.state < HTTP2TransactionState::HTTP2StateDataClient {
self.state = HTTP2TransactionState::HTTP2StateDataClient;
}
StreamIdReuse,
InvalidHTTP1Settings,
FailedDecompression,
+ InvalidRange,
}
pub struct HTTP2DynTable {
pub files: Files,
}
+impl State<HTTP2Transaction> for HTTP2State {
+ fn get_transactions(&self) -> &[HTTP2Transaction] {
+ &self.transactions
+ }
+}
+
impl HTTP2State {
pub fn new() -> Self {
Self {
}
pub fn find_or_create_tx(
- &mut self, header: &parser::HTTP2FrameHeader, data: &HTTP2FrameTypeData, dir: u8,
+ &mut self, header: &parser::HTTP2FrameHeader, data: &HTTP2FrameTypeData, dir: Direction,
) -> &mut HTTP2Transaction {
if header.stream_id == 0 {
return self.create_global_tx();
//yes, the right stream_id for Suricata is not the header one
HTTP2FrameTypeData::PUSHPROMISE(hs) => hs.stream_id,
HTTP2FrameTypeData::CONTINUATION(_) => {
- if dir == STREAM_TOCLIENT {
+ if dir == Direction::ToClient {
//continuation of a push promise
self.find_child_stream_id(header.stream_id)
} else {
}
}
- fn process_headers(&mut self, blocks: &Vec<parser::HTTP2FrameHeaderBlock>, dir: u8) {
+ fn process_headers(&mut self, blocks: &Vec<parser::HTTP2FrameHeaderBlock>, dir: Direction) {
let (mut update, mut sizeup) = (false, 0);
for i in 0..blocks.len() {
if blocks[i].error >= parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeError {
}
if update {
//borrow checker forbids to pass directly dyn_headers
- let dyn_headers = if dir == STREAM_TOCLIENT {
+ let dyn_headers = if dir == Direction::ToClient {
&mut self.dynamic_headers_tc
} else {
&mut self.dynamic_headers_ts
}
fn parse_frame_data(
- &mut self, ftype: u8, input: &[u8], complete: bool, hflags: u8, dir: u8,
+ &mut self, ftype: u8, input: &[u8], complete: bool, hflags: u8, dir: Direction,
) -> HTTP2FrameTypeData {
match num::FromPrimitive::from_u8(ftype) {
Some(parser::HTTP2FrameType::GOAWAY) => {
for i in 0..set.len() {
if set[i].id == parser::HTTP2SettingsId::SETTINGSHEADERTABLESIZE {
//reverse order as this is what we accept from the other endpoint
- let dyn_headers = if dir == STREAM_TOCLIENT {
+ let dyn_headers = if dir == Direction::ToClient {
&mut self.dynamic_headers_ts
} else {
&mut self.dynamic_headers_tc
}
}
Some(parser::HTTP2FrameType::PUSHPROMISE) => {
- let dyn_headers = if dir == STREAM_TOCLIENT {
+ let dyn_headers = if dir == Direction::ToClient {
&mut self.dynamic_headers_tc
} else {
&mut self.dynamic_headers_ts
return HTTP2FrameTypeData::DATA;
}
Some(parser::HTTP2FrameType::CONTINUATION) => {
- let dyn_headers = if dir == STREAM_TOCLIENT {
+ let dyn_headers = if dir == Direction::ToClient {
&mut self.dynamic_headers_tc
} else {
&mut self.dynamic_headers_ts
}
}
Some(parser::HTTP2FrameType::HEADERS) => {
- let dyn_headers = if dir == STREAM_TOCLIENT {
+ let dyn_headers = if dir == Direction::ToClient {
&mut self.dynamic_headers_tc
} else {
&mut self.dynamic_headers_ts
}
}
- fn parse_frames(&mut self, mut input: &[u8], il: usize, dir: u8) -> AppLayerResult {
+ fn parse_frames(
+ &mut self, mut input: &[u8], il: usize, dir: Direction, flow: *const Flow,
+ ) -> AppLayerResult {
while input.len() > 0 {
match parser::http2_parse_frame_header(input) {
Ok((rem, head)) => {
let over = head.flags & parser::HTTP2_FLAG_HEADER_EOS != 0;
let ftype = head.ftype;
let sid = head.stream_id;
- if dir == STREAM_TOSERVER {
+ if dir == Direction::ToServer {
tx.frames_ts.push(HTTP2Frame {
header: head,
data: txdata,
//borrow checker forbids to reuse directly tx
let index = self.find_tx_index(sid);
if index > 0 {
- let mut tx_same = &mut self.transactions[index - 1];
- if dir == STREAM_TOCLIENT {
- tx_same.ft_tc.tx_id = tx_same.tx_id - 1;
- } else {
- tx_same.ft_ts.tx_id = tx_same.tx_id - 1;
- }
+ let tx_same = &mut self.transactions[index - 1];
let (files, flags) = self.files.get(dir);
match tx_same.decompress(
&rem[..hlsafe],
over,
files,
flags,
+ flow,
) {
Err(_e) => {
self.set_event(HTTP2Event::FailedDecompression);
return AppLayerResult::ok();
}
- fn parse_ts(&mut self, mut input: &[u8]) -> AppLayerResult {
+ fn parse_ts(&mut self, mut input: &[u8], flow: *const Flow) -> AppLayerResult {
//very first : skip magic
let mut magic_consumed = 0;
if self.progress < HTTP2ConnectionState::Http2StateMagicDone {
}
//then parse all we can
- let r = self.parse_frames(input, il, STREAM_TOSERVER);
+ let r = self.parse_frames(input, il, Direction::ToServer, flow);
if r.status == 1 {
//adds bytes consumed by banner to incomplete result
return AppLayerResult::incomplete(r.consumed + magic_consumed as u32, r.needed);
}
}
- fn parse_tc(&mut self, mut input: &[u8]) -> AppLayerResult {
+ fn parse_tc(&mut self, mut input: &[u8], flow: *const Flow) -> AppLayerResult {
//first consume frame bytes
let il = input.len();
if self.response_frame_size > 0 {
}
}
//then parse all we can
- return self.parse_frames(input, il, STREAM_TOCLIENT);
- }
-
- fn tx_iterator(
- &mut self, min_tx_id: u64, state: &mut u64,
- ) -> Option<(&HTTP2Transaction, u64, bool)> {
- let mut index = *state as usize;
- let len = self.transactions.len();
-
- while index < len {
- let tx = &self.transactions[index];
- if tx.tx_id < min_tx_id + 1 {
- index += 1;
- continue;
- }
- *state = index as u64;
- return Some((tx, tx.tx_id - 1, (len - index) > 1));
- }
-
- return None;
+ return self.parse_frames(input, il, Direction::ToClient, flow);
}
}
// C exports.
-export_tx_get_detect_state!(rs_http2_tx_get_detect_state, HTTP2Transaction);
-export_tx_set_detect_state!(rs_http2_tx_set_detect_state, HTTP2Transaction);
-
export_tx_data_get!(rs_http2_get_tx_data, HTTP2Transaction);
/// C entry point for a probing parser.
pub unsafe extern "C" fn rs_http2_probing_parser_tc(
_flow: *const Flow, _direction: u8, input: *const u8, input_len: u32, _rdir: *mut u8,
) -> AppProto {
- if input != std::ptr::null_mut() {
+ if !input.is_null() {
let slice = build_slice!(input, input_len as usize);
match parser::http2_parse_frame_header(slice) {
Ok((_, header)) => {
return ALPROTO_UNKNOWN;
}
Err(_) => {
- return ALPROTO_FAILED ;
+ return ALPROTO_FAILED;
}
}
}
let state = HTTP2State::new();
let boxed = Box::new(state);
let r = Box::into_raw(boxed) as *mut _;
- if orig_state != std::ptr::null_mut() {
+ if !orig_state.is_null() {
//we could check ALPROTO_HTTP1 == orig_proto
unsafe {
HTTP2MimicHttp1Request(orig_state, r);
let state = cast_pointer!(state, HTTP2State);
let buf = build_slice!(input, input_len as usize);
- state.files.flags_ts = FileFlowToFlags(flow, STREAM_TOSERVER);
+ state.files.flags_ts = FileFlowToFlags(flow, Direction::ToServer.into());
state.files.flags_ts = state.files.flags_ts | FILE_USE_DETECT;
- return state.parse_ts(buf);
+ return state.parse_ts(buf, flow);
}
#[no_mangle]
) -> AppLayerResult {
let state = cast_pointer!(state, HTTP2State);
let buf = build_slice!(input, input_len as usize);
- state.files.flags_tc = FileFlowToFlags(flow, STREAM_TOCLIENT);
+ state.files.flags_tc = FileFlowToFlags(flow, Direction::ToClient.into());
state.files.flags_tc = state.files.flags_tc | FILE_USE_DETECT;
- return state.parse_tc(buf);
+ return state.parse_tc(buf, flow);
}
#[no_mangle]
}
#[no_mangle]
-pub unsafe extern "C" fn rs_http2_tx_get_state(tx: *mut std::os::raw::c_void) -> HTTP2TransactionState {
+pub unsafe extern "C" fn rs_http2_tx_get_state(
+ tx: *mut std::os::raw::c_void,
+) -> HTTP2TransactionState {
let tx = cast_pointer!(tx, HTTP2Transaction);
return tx.state;
}
return tx.events;
}
-#[no_mangle]
-pub unsafe extern "C" fn rs_http2_state_get_tx_iterator(
- _ipproto: u8, _alproto: AppProto, state: *mut std::os::raw::c_void, min_tx_id: u64,
- _max_tx_id: u64, istate: &mut u64,
-) -> applayer::AppLayerGetTxIterTuple {
- let state = cast_pointer!(state, HTTP2State);
- match state.tx_iterator(min_tx_id, istate) {
- Some((tx, out_tx_id, has_next)) => {
- let c_tx = tx as *const _ as *mut _;
- let ires = applayer::AppLayerGetTxIterTuple::with_values(c_tx, out_tx_id, has_next);
- return ires;
- }
- None => {
- return applayer::AppLayerGetTxIterTuple::not_found();
- }
- }
-}
-
#[no_mangle]
pub unsafe extern "C" fn rs_http2_getfiles(
state: *mut std::os::raw::c_void, direction: u8,
) -> *mut FileContainer {
let state = cast_pointer!(state, HTTP2State);
- if direction == STREAM_TOCLIENT {
+ if direction == Direction::ToClient.into() {
&mut state.files.files_tc as *mut FileContainer
} else {
&mut state.files.files_ts as *mut FileContainer
tx_comp_st_ts: HTTP2TransactionState::HTTP2StateClosed as i32,
tx_comp_st_tc: HTTP2TransactionState::HTTP2StateClosed as i32,
tx_get_progress: rs_http2_tx_get_alstate_progress,
- get_de_state: rs_http2_tx_get_detect_state,
- set_de_state: rs_http2_tx_set_detect_state,
get_events: Some(rs_http2_state_get_events),
get_eventinfo: Some(HTTP2Event::get_event_info),
get_eventinfo_byid: Some(HTTP2Event::get_event_info_by_id),
localstorage_new: None,
localstorage_free: None,
get_files: Some(rs_http2_getfiles),
- get_tx_iterator: Some(rs_http2_state_get_tx_iterator),
+ get_tx_iterator: Some(applayer::state_get_tx_iterator::<HTTP2State, HTTP2Transaction>),
get_tx_data: rs_http2_get_tx_data,
apply_tx_config: None,
flags: 0,