From 8857b78f6a4ebe212950cb9d793e58937a6ef415 Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Tue, 20 Jan 2026 22:32:31 +0100 Subject: [PATCH] rust: move AppLayerTxData definition to C and bindgen it to rust Will make easier the bindgen of RustParser structure which uses a callback which uses AppLayerTxData Move also the free function to C SCAppLayerTxDataCleanup As suricata-sys crate defines AppLayerTxData for rust, It must itself implement the Drop trait, and thus, We need to define a feature surest --- rust/Makefile.am | 2 + rust/src/applayer.rs | 206 +++++++------------------- rust/src/applayertemplate/template.rs | 2 +- rust/src/dcerpc/dcerpc.rs | 12 +- rust/src/dcerpc/dcerpc_udp.rs | 12 +- rust/src/dns/dns.rs | 6 +- rust/src/enip/enip.rs | 8 +- rust/src/http2/http2.rs | 20 +-- rust/src/ldap/ldap.rs | 6 +- rust/src/modbus/modbus.rs | 20 +-- rust/src/mqtt/mqtt.rs | 8 +- rust/src/nfs/nfs.rs | 26 ++-- rust/src/pgsql/pgsql.rs | 8 +- rust/src/pop3/pop3.rs | 4 +- rust/src/rfb/rfb.rs | 4 +- rust/src/smb/dcerpc.rs | 4 +- rust/src/smb/files.rs | 16 +- rust/src/smb/session.rs | 4 +- rust/src/smb/smb.rs | 30 ++-- rust/src/ssh/ssh.rs | 4 +- rust/src/tftp/tftp.rs | 6 +- rust/sys/src/sys.rs | 52 ++++++- src/app-layer-parser.c | 13 ++ src/app-layer-parser.h | 58 +++++++- src/tx-bit.c | 1 + 25 files changed, 268 insertions(+), 264 deletions(-) diff --git a/rust/Makefile.am b/rust/Makefile.am index b003af914f..0558725c99 100644 --- a/rust/Makefile.am +++ b/rust/Makefile.am @@ -151,7 +151,9 @@ if HAVE_BINDGEN --allowlist-type 'AppLayerStateData' \ --allowlist-type 'AppLayerGetTxIterTuple' \ --allowlist-type 'AppLayerResult' \ + --allowlist-type 'AppLayerTxData' \ --allowlist-type 'StreamSlice' \ + --no-copy 'AppLayerTxData' \ --allowlist-function 'SC.*' \ --allowlist-var 'SC.*' \ --opaque-type 'SCConfNode' \ diff --git a/rust/src/applayer.rs b/rust/src/applayer.rs index ce1805243b..b4e0c94e27 100644 --- a/rust/src/applayer.rs +++ b/rust/src/applayer.rs @@ -28,20 +28,16 @@ use std::ffi::CStr; // AppLayerEvent from this module. pub use suricata_derive::AppLayerEvent; use suricata_sys::sys::{ - AppLayerDecoderEvents, AppLayerGetTxIterState, AppLayerParserState, AppProto, - DetectEngineState, GenericVar, + AppLayerGetTxIterState, AppLayerParserState, AppProto, }; pub use suricata_sys::sys::{ AppLayerGetFileState, AppLayerGetTxIterTuple, AppLayerResult, AppLayerStateData, - StreamSlice, + AppLayerTxConfig, StreamSlice, }; #[cfg(not(test))] -use suricata_sys::sys::{ - SCAppLayerDecoderEventsFreeEvents, SCAppLayerDecoderEventsSetEventRaw, SCDetectEngineStateFree, - SCGenericVarFree, -}; +use suricata_sys::sys::SCAppLayerDecoderEventsSetEventRaw; /// Cast pointer to a variable, as a mutable reference to an object /// @@ -101,123 +97,18 @@ impl StreamSliceRust for StreamSlice { } } -#[repr(C)] -#[derive(Default, Debug,PartialEq, Eq)] -pub struct AppLayerTxConfig { - /// config: log flags - log_flags: u8, -} - -#[repr(C)] -#[derive(Debug, PartialEq, Eq)] -pub struct AppLayerTxData { - /// config: log flags - pub config: AppLayerTxConfig, - - /// The tx has been updated and needs to be processed : detection, logging, cleaning - /// It can then be skipped until new data arrives. - /// There is a boolean for both directions : to server and to client - pub updated_tc: bool, - pub updated_ts: bool, - - flags: u8, - - /// logger flags for tx logging api - logged: u32, - - /// track file open/logs so we can know how long to keep the tx - pub files_opened: u32, - pub files_logged: u32, - pub files_stored: u32, - - pub file_flags: u16, - - /// Indicated if a file tracking tx, and if so in which direction: - /// 0: not a file tx - /// STREAM_TOSERVER: file tx, files only in toserver dir - /// STREAM_TOCLIENT: file tx , files only in toclient dir - /// STREAM_TOSERVER|STREAM_TOCLIENT: files possible in both dirs - pub file_tx: u8, - /// Number of times this tx data has already been logged for signatures - /// not using application layer keywords - pub guessed_applayer_logged: u8, - - /// detection engine progress tracking for use by detection engine - /// Reflects the "progress" of prefilter engines into this TX, where - /// the value is offset by 1. So if for progress state 0 the engines - /// are done, the value here will be 1. So a value of 0 means, no - /// progress tracked yet. - /// - detect_progress_ts: u8, - detect_progress_tc: u8, - - de_state: *mut DetectEngineState, - pub events: *mut AppLayerDecoderEvents, - txbits: *mut GenericVar, -} - -impl Default for AppLayerTxData { - fn default() -> Self { - Self::new() - } -} - -impl Drop for AppLayerTxData { - fn drop(&mut self) { - self.cleanup(); - } -} - -#[no_mangle] -pub unsafe extern "C" fn SCAppLayerTxDataCleanup(txd: *mut AppLayerTxData) { - let txd = cast_pointer!(txd, AppLayerTxData); - txd.cleanup() -} +#[derive(Debug, Default, Eq, PartialEq)] +pub struct AppLayerTxData(pub suricata_sys::sys::AppLayerTxData); impl AppLayerTxData { - #[cfg(not(test))] - pub fn cleanup(&mut self) { - if !self.de_state.is_null() { - unsafe { - SCDetectEngineStateFree(self.de_state); - } - } - if !self.events.is_null() { - unsafe { - SCAppLayerDecoderEventsFreeEvents(&mut self.events); - } - } - if !self.txbits.is_null() { - unsafe { - SCGenericVarFree(self.txbits); - } - } - } - - #[cfg(test)] - pub fn cleanup(&mut self) {} - /// Create new AppLayerTxData for a transaction that covers both /// directions. pub fn new() -> Self { - Self { - config: AppLayerTxConfig::default(), - logged: 0, - files_opened: 0, - files_logged: 0, - files_stored: 0, - file_flags: 0, - file_tx: 0, - guessed_applayer_logged: 0, + Self (suricata_sys::sys::AppLayerTxData { updated_tc: true, updated_ts: true, - flags: 0, - detect_progress_ts: 0, - detect_progress_tc: 0, - de_state: std::ptr::null_mut(), - events: std::ptr::null_mut(), - txbits: std::ptr::null_mut(), - } + ..Default::default() + }) } /// Create new AppLayerTxData for a transaction in a single @@ -227,62 +118,49 @@ impl AppLayerTxData { Direction::ToServer => (APP_LAYER_TX_SKIP_INSPECT_TC, true, false), Direction::ToClient => (APP_LAYER_TX_SKIP_INSPECT_TS, false, true), }; - Self { - config: AppLayerTxConfig::default(), - logged: 0, - files_opened: 0, - files_logged: 0, - files_stored: 0, - file_flags: 0, - file_tx: 0, - guessed_applayer_logged: 0, + Self (suricata_sys::sys::AppLayerTxData{ updated_tc, updated_ts, - detect_progress_ts: 0, - detect_progress_tc: 0, flags, - de_state: std::ptr::null_mut(), - events: std::ptr::null_mut(), - txbits: std::ptr::null_mut(), - } + ..Default::default() + }) } pub fn init_files_opened(&mut self) { - self.files_opened = 1; + self.0.files_opened = 1; } pub fn incr_files_opened(&mut self) { - self.files_opened += 1; + self.0.files_opened += 1; } pub fn set_event(&mut self, _event: u8) { #[cfg(not(test))] unsafe { - SCAppLayerDecoderEventsSetEventRaw(&mut self.events, _event); + SCAppLayerDecoderEventsSetEventRaw(&mut self.0.events, _event); } } pub fn update_file_flags(&mut self, state_flags: u16) { - if (self.file_flags & state_flags) != state_flags { - SCLogDebug!("updating tx file_flags {:04x} with state flags {:04x}", self.file_flags, state_flags); - let mut nf = state_flags; - // With keyword filestore:both,flow : - // There may be some opened unclosed file in one direction without filestore - // As such it has tx file_flags had FLOWFILE_NO_STORE_TS or TC - // But a new file in the other direction may trigger filestore:both,flow - // And thus set state_flags FLOWFILE_STORE_TS - // If the file was opened without storing it, do not try to store just the end of it - if (self.file_flags & FLOWFILE_NO_STORE_TS) != 0 && (state_flags & FLOWFILE_STORE_TS) != 0 { - nf &= !FLOWFILE_STORE_TS; - } - if (self.file_flags & FLOWFILE_NO_STORE_TC) != 0 && (state_flags & FLOWFILE_STORE_TC) != 0 { - nf &= !FLOWFILE_STORE_TC; - } - self.file_flags |= nf; + unsafe { + SCTxDataUpdateFileFlags(&mut self.0, state_flags); + } + } +} + +#[cfg(not(test))] +use suricata_sys::sys::SCAppLayerTxDataCleanup; + +impl Drop for AppLayerTxData { + fn drop(&mut self) { + #[cfg(not(test))] + unsafe { + SCAppLayerTxDataCleanup(&mut self.0); } } } + // need to keep in sync with C flow.h pub const FLOWFILE_NO_STORE_TS: u16 = BIT_U16!(2); pub const FLOWFILE_NO_STORE_TC: u16 = BIT_U16!(3); @@ -290,18 +168,34 @@ pub const FLOWFILE_STORE_TS: u16 = BIT_U16!(12); pub const FLOWFILE_STORE_TC: u16 = BIT_U16!(13); #[no_mangle] -pub unsafe extern "C" fn SCTxDataUpdateFileFlags(txd: &mut AppLayerTxData, state_flags: u16) { - txd.update_file_flags(state_flags); +pub unsafe extern "C" fn SCTxDataUpdateFileFlags(txd: &mut suricata_sys::sys::AppLayerTxData, state_flags: u16) { + if (txd.file_flags & state_flags) != state_flags { + SCLogDebug!("updating tx file_flags {:04x} with state flags {:04x}", txd.file_flags, state_flags); + let mut nf = state_flags; + // With keyword filestore:both,flow : + // There may be some opened unclosed file in one direction without filestore + // As such it has tx file_flags had FLOWFILE_NO_STORE_TS or TC + // But a new file in the other direction may trigger filestore:both,flow + // And thus set state_flags FLOWFILE_STORE_TS + // If the file was opened without storing it, do not try to store just the end of it + if (txd.file_flags & FLOWFILE_NO_STORE_TS) != 0 && (state_flags & FLOWFILE_STORE_TS) != 0 { + nf &= !FLOWFILE_STORE_TS; + } + if (txd.file_flags & FLOWFILE_NO_STORE_TC) != 0 && (state_flags & FLOWFILE_STORE_TC) != 0 { + nf &= !FLOWFILE_STORE_TC; + } + txd.file_flags |= nf; + } } #[macro_export] macro_rules!export_tx_data_get { ($name:ident, $type:ty) => { unsafe extern "C" fn $name(tx: *mut std::os::raw::c_void) - -> *mut $crate::applayer::AppLayerTxData + -> *mut suricata_sys::sys::AppLayerTxData { let tx = &mut *(tx as *mut $type); - &mut tx.tx_data + &mut tx.tx_data.0 } } } @@ -485,7 +379,7 @@ pub type GetTxIteratorFn = unsafe extern "C" fn (ipproto: u8, alproto: AppPro max_tx_id: u64, istate: *mut AppLayerGetTxIterState) -> AppLayerGetTxIterTuple; -pub type GetTxDataFn = unsafe extern "C" fn(*mut c_void) -> *mut AppLayerTxData; +pub type GetTxDataFn = unsafe extern "C" fn(*mut c_void) -> *mut suricata_sys::sys::AppLayerTxData; pub type GetStateDataFn = unsafe extern "C" fn(*mut c_void) -> *mut AppLayerStateData; pub type ApplyTxConfigFn = unsafe extern "C" fn (*mut c_void, *mut c_void, c_int, AppLayerTxConfig); pub type GetFrameIdByName = unsafe extern "C" fn(*const c_char) -> c_int; diff --git a/rust/src/applayertemplate/template.rs b/rust/src/applayertemplate/template.rs index 6dd5f711f1..99e881f558 100644 --- a/rust/src/applayertemplate/template.rs +++ b/rust/src/applayertemplate/template.rs @@ -210,7 +210,7 @@ impl TemplateState { start = rem; if let Some(tx) = self.find_request() { - tx.tx_data.updated_tc = true; + tx.tx_data.0.updated_tc = true; tx.response = Some(response); SCLogNotice!("Found response for request:"); SCLogNotice!("- Request: {:?}", tx.request); diff --git a/rust/src/dcerpc/dcerpc.rs b/rust/src/dcerpc/dcerpc.rs index 139f0415cd..b13914a572 100644 --- a/rust/src/dcerpc/dcerpc.rs +++ b/rust/src/dcerpc/dcerpc.rs @@ -370,8 +370,8 @@ impl DCERPCState { for tx_old in &mut self.transactions.range_mut(self.tx_index_completed..) { index += 1; if !tx_old.req_done || !tx_old.resp_done { - tx_old.tx_data.updated_tc = true; - tx_old.tx_data.updated_ts = true; + tx_old.tx_data.0.updated_tc = true; + tx_old.tx_data.0.updated_ts = true; tx_old.req_done = true; tx_old.resp_done = true; break; @@ -457,8 +457,8 @@ impl DCERPCState { } } } - tx.tx_data.updated_tc = true; - tx.tx_data.updated_ts = true; + tx.tx_data.0.updated_tc = true; + tx.tx_data.0.updated_ts = true; return Some(tx); } } @@ -1054,10 +1054,10 @@ pub(super) unsafe extern "C" fn get_alstate_progress(tx: *mut std::os::raw::c_vo unsafe extern "C" fn get_tx_data( tx: *mut std::os::raw::c_void) - -> *mut AppLayerTxData + -> *mut suricata_sys::sys::AppLayerTxData { let tx = cast_pointer!(tx, DCERPCTransaction); - return &mut tx.tx_data; + return &mut tx.tx_data.0; } #[no_mangle] diff --git a/rust/src/dcerpc/dcerpc_udp.rs b/rust/src/dcerpc/dcerpc_udp.rs index 5d71159fa6..27766e051a 100644 --- a/rust/src/dcerpc/dcerpc_udp.rs +++ b/rust/src/dcerpc/dcerpc_udp.rs @@ -94,8 +94,8 @@ impl DCERPCUDPState { for tx_old in &mut self.transactions.range_mut(self.tx_index_completed..) { index += 1; if !tx_old.req_done || !tx_old.resp_done { - tx_old.tx_data.updated_tc = true; - tx_old.tx_data.updated_ts = true; + tx_old.tx_data.0.updated_tc = true; + tx_old.tx_data.0.updated_ts = true; tx_old.req_done = true; tx_old.resp_done = true; break; @@ -171,8 +171,8 @@ impl DCERPCUDPState { } if let Some(tx) = otx { - tx.tx_data.updated_tc = true; - tx.tx_data.updated_ts = true; + tx.tx_data.0.updated_tc = true; + tx.tx_data.0.updated_ts = true; let done = (hdr.flags1 & PFCL1_FRAG) == 0 || (hdr.flags1 & PFCL1_LASTFRAG) != 0; let max_size = cfg_max_stub_size() as usize; @@ -277,10 +277,10 @@ unsafe extern "C" fn state_transaction_free( unsafe extern "C" fn get_tx_data( tx: *mut std::os::raw::c_void) - -> *mut AppLayerTxData + -> *mut suricata_sys::sys::AppLayerTxData { let tx = cast_pointer!(tx, DCERPCTransaction); - return &mut tx.tx_data; + return &mut tx.tx_data.0; } unsafe extern "C" fn get_tx( diff --git a/rust/src/dns/dns.rs b/rust/src/dns/dns.rs index 34efc661f6..13e04c2e13 100644 --- a/rust/src/dns/dns.rs +++ b/rust/src/dns/dns.rs @@ -675,7 +675,7 @@ impl DNSState { if let Some(ref mut config) = &mut self.config { if let Some(response) = &tx.response { if let Some(config) = config.remove(&response.header.tx_id) { - tx.tx_data.config = config; + tx.tx_data.0.config = config; } } } @@ -1022,9 +1022,9 @@ pub extern "C" fn SCDnsTxIsResponse(tx: &mut DNSTransaction) -> bool { pub(crate) unsafe extern "C" fn state_get_tx_data( tx: *mut std::os::raw::c_void, -) -> *mut AppLayerTxData { +) -> *mut suricata_sys::sys::AppLayerTxData { let tx = cast_pointer!(tx, DNSTransaction); - return &mut tx.tx_data; + return &mut tx.tx_data.0; } pub(crate) unsafe extern "C" fn dns_get_state_data( diff --git a/rust/src/enip/enip.rs b/rust/src/enip/enip.rs index ca0f9db33c..5e69c1dd71 100644 --- a/rust/src/enip/enip.rs +++ b/rust/src/enip/enip.rs @@ -207,8 +207,8 @@ impl EnipState { fn purge_tx_flood(&mut self) { let mut event_set = false; for tx in self.transactions.iter_mut() { - tx.tx_data.updated_tc = true; - tx.tx_data.updated_ts = true; + tx.tx_data.0.updated_tc = true; + tx.tx_data.0.updated_ts = true; tx.done = true; if !event_set { tx.tx_data.set_event(EnipEvent::TooManyTransactions as u8); @@ -222,8 +222,8 @@ impl EnipState { if let Some(req) = &tx.request { if tx.response.is_none() { tx.done = true; - tx.tx_data.updated_tc = true; - tx.tx_data.updated_ts = true; + tx.tx_data.0.updated_tc = true; + tx.tx_data.0.updated_ts = true; if response_matches_request(req, pdu) { return Some(tx); } diff --git a/rust/src/http2/http2.rs b/rust/src/http2/http2.rs index ce12dfb489..fb651c087e 100644 --- a/rust/src/http2/http2.rs +++ b/rust/src/http2/http2.rs @@ -685,7 +685,7 @@ impl HTTP2State { 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); + tx.update_file_flags(tx.tx_data.0.file_flags); return Some(tx); } } @@ -741,8 +741,8 @@ impl HTTP2State { tx_old.set_event(HTTP2Event::TooManyStreams); // use a distinct state, even if we do not log it tx_old.state = HTTP2TransactionState::HTTP2StateTodrop; - tx_old.tx_data.updated_tc = true; - tx_old.tx_data.updated_ts = true; + tx_old.tx_data.0.updated_tc = true; + tx_old.tx_data.0.updated_ts = true; } return None; } @@ -775,9 +775,9 @@ impl HTTP2State { 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; + tx.update_file_flags(tx.tx_data.0.file_flags); + tx.tx_data.0.updated_tc = true; + tx.tx_data.0.updated_ts = true; return Some(tx); } else { // do not use SETTINGS_MAX_CONCURRENT_STREAMS as it can grow too much @@ -790,8 +790,8 @@ impl HTTP2State { tx_old.set_event(HTTP2Event::TooManyStreams); // use a distinct state, even if we do not log it tx_old.state = HTTP2TransactionState::HTTP2StateTodrop; - tx_old.tx_data.updated_tc = true; - tx_old.tx_data.updated_ts = true; + tx_old.tx_data.0.updated_tc = true; + tx_old.tx_data.0.updated_ts = true; } return None; } @@ -801,8 +801,8 @@ impl HTTP2State { 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; // might hold files in both directions + 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 self.transactions.push_back(tx); return Some(self.transactions.back_mut().unwrap()); } diff --git a/rust/src/ldap/ldap.rs b/rust/src/ldap/ldap.rs index bc41c47cf8..71c3db65a4 100644 --- a/rust/src/ldap/ldap.rs +++ b/rust/src/ldap/ldap.rs @@ -158,8 +158,8 @@ impl LdapState { if self.transactions.len() > unsafe { LDAP_MAX_TX } { for tx_old in &mut self.transactions { if !tx_old.complete { - tx_old.tx_data.updated_tc = true; - tx_old.tx_data.updated_ts = true; + tx_old.tx_data.0.updated_tc = true; + tx_old.tx_data.0.updated_ts = true; tx_old.complete = true; tx_old .tx_data @@ -311,7 +311,7 @@ impl LdapState { if let Some(tx) = self.find_request(response.message_id) { tx.complete |= tx_is_complete(&response.protocol_op, Direction::ToClient); let tx_id = tx.id(); - tx.tx_data.updated_tc = true; + tx.tx_data.0.updated_tc = true; tx.responses.push(response.to_static()); sc_app_layer_parser_trigger_raw_stream_inspection( flow, diff --git a/rust/src/modbus/modbus.rs b/rust/src/modbus/modbus.rs index 2b79f3b79e..98b6da2058 100644 --- a/rust/src/modbus/modbus.rs +++ b/rust/src/modbus/modbus.rs @@ -130,8 +130,8 @@ impl ModbusState { for tx in &mut self.transactions { if let Some(req) = &tx.request { if tx.response.is_none() && resp.matches(req) { - tx.tx_data.updated_tc = true; - tx.tx_data.updated_ts = true; + tx.tx_data.0.updated_tc = true; + tx.tx_data.0.updated_ts = true; return Some(tx); } } @@ -147,8 +147,8 @@ impl ModbusState { for tx in &mut self.transactions { if let Some(resp) = &tx.response { if tx.request.is_none() && req.matches(resp) { - tx.tx_data.updated_tc = true; - tx.tx_data.updated_ts = true; + tx.tx_data.0.updated_tc = true; + tx.tx_data.0.updated_ts = true; return Some(tx); } } @@ -196,8 +196,8 @@ impl ModbusState { match self.find_response_and_validate(&mut msg) { Some(tx) => { tx.set_events_from_flags(&msg.error_flags); - tx.tx_data.updated_tc = true; - tx.tx_data.updated_ts = true; + tx.tx_data.0.updated_tc = true; + tx.tx_data.0.updated_ts = true; tx.request = Some(msg); if !flow.is_null() { sc_app_layer_parser_trigger_raw_stream_inspection( @@ -236,8 +236,8 @@ impl ModbusState { } else { tx.set_events_from_flags(&msg.error_flags); } - tx.tx_data.updated_tc = true; - tx.tx_data.updated_ts = true; + tx.tx_data.0.updated_tc = true; + tx.tx_data.0.updated_ts = true; tx.response = Some(msg); if !flow.is_null() { sc_app_layer_parser_trigger_raw_stream_inspection( @@ -398,9 +398,9 @@ unsafe extern "C" fn modbus_tx_get_alstate_progress( unsafe extern "C" fn modbus_state_get_tx_data( tx: *mut std::os::raw::c_void, -) -> *mut AppLayerTxData { +) -> *mut suricata_sys::sys::AppLayerTxData { let tx = cast_pointer!(tx, ModbusTransaction); - &mut tx.tx_data + &mut tx.tx_data.0 } export_state_data_get!(modbus_get_state_data, ModbusState); diff --git a/rust/src/mqtt/mqtt.rs b/rust/src/mqtt/mqtt.rs index 40dd03670a..754844fcf0 100644 --- a/rust/src/mqtt/mqtt.rs +++ b/rust/src/mqtt/mqtt.rs @@ -178,8 +178,8 @@ impl MQTTState { if !tx.complete { if let Some(mpktid) = tx.pkt_id { if mpktid == pkt_id { - tx.tx_data.updated_tc = true; - tx.tx_data.updated_ts = true; + tx.tx_data.0.updated_tc = true; + tx.tx_data.0.updated_ts = true; return Some(tx); } } @@ -202,8 +202,8 @@ impl MQTTState { for tx_old in &mut self.transactions.range_mut(self.tx_index_completed..) { index += 1; if !tx_old.complete { - tx_old.tx_data.updated_tc = true; - tx_old.tx_data.updated_ts = true; + tx_old.tx_data.0.updated_tc = true; + tx_old.tx_data.0.updated_ts = true; tx_old.complete = true; MQTTState::set_event(tx_old, MQTTEvent::TooManyTransactions); break; diff --git a/rust/src/nfs/nfs.rs b/rust/src/nfs/nfs.rs index f74c03a1e1..8ef4127bcb 100644 --- a/rust/src/nfs/nfs.rs +++ b/rust/src/nfs/nfs.rs @@ -263,8 +263,8 @@ impl NFSTransaction { } pub fn free(&mut self) { - debug_validate_bug_on!(self.tx_data.files_opened > 1); - debug_validate_bug_on!(self.tx_data.files_logged > 1); + debug_validate_bug_on!(self.tx_data.0.files_opened > 1); + debug_validate_bug_on!(self.tx_data.0.files_logged > 1); } } @@ -454,8 +454,8 @@ impl NFSState { // set at least one another transaction to the drop state for tx_old in &mut self.transactions { if !tx_old.request_done || !tx_old.response_done { - tx_old.tx_data.updated_tc = true; - tx_old.tx_data.updated_ts = true; + tx_old.tx_data.0.updated_tc = true; + tx_old.tx_data.0.updated_ts = true; tx_old.request_done = true; tx_old.response_done = true; tx_old.is_file_closed = true; @@ -515,8 +515,8 @@ impl NFSState { &mut self, flow: *mut Flow, xid: u32, rpc_status: u32, nfs_status: u32, resp_handle: &[u8], ) { if let Some(mytx) = self.get_tx_by_xid(xid) { - mytx.tx_data.updated_tc = true; - mytx.tx_data.updated_ts = true; + mytx.tx_data.0.updated_tc = true; + mytx.tx_data.0.updated_ts = true; mytx.response_done = true; mytx.rpc_response_status = rpc_status; mytx.nfs_response_status = nfs_status; @@ -923,10 +923,10 @@ impl NFSState { d.direction = direction; d.file_tracker.tx_id = tx.id - 1; tx.tx_data.update_file_flags(self.state_data.file_flags); - d.update_file_flags(tx.tx_data.file_flags); + d.update_file_flags(tx.tx_data.0.file_flags); } tx.tx_data.init_files_opened(); - tx.tx_data.file_tx = if direction == Direction::ToServer { + tx.tx_data.0.file_tx = if direction == Direction::ToServer { STREAM_TOSERVER } else { STREAM_TOCLIENT @@ -953,10 +953,10 @@ impl NFSState { && tx.file_handle == fh { tx.tx_data.update_file_flags(self.state_data.file_flags); - d.update_file_flags(tx.tx_data.file_flags); + d.update_file_flags(tx.tx_data.0.file_flags); SCLogDebug!("Found NFS file TX with ID {} XID {:04X}", tx.id, tx.xid); - tx.tx_data.updated_tc = true; - tx.tx_data.updated_ts = true; + tx.tx_data.0.updated_tc = true; + tx.tx_data.0.updated_ts = true; return Some(tx); } } @@ -2039,9 +2039,9 @@ unsafe extern "C" fn nfs_tx_get_alstate_progress( } } -unsafe extern "C" fn nfs_get_tx_data(tx: *mut std::os::raw::c_void) -> *mut AppLayerTxData { +unsafe extern "C" fn nfs_get_tx_data(tx: *mut std::os::raw::c_void) -> *mut suricata_sys::sys::AppLayerTxData { let tx = cast_pointer!(tx, NFSTransaction); - return &mut tx.tx_data; + return &mut tx.tx_data.0; } export_state_data_get!(nfs_get_state_data, NFSState); diff --git a/rust/src/pgsql/pgsql.rs b/rust/src/pgsql/pgsql.rs index 2986aede42..7c2b3c88c3 100644 --- a/rust/src/pgsql/pgsql.rs +++ b/rust/src/pgsql/pgsql.rs @@ -231,8 +231,8 @@ impl PgsqlState { for tx_old in &mut self.transactions.range_mut(self.tx_index_completed..) { index += 1; if tx_old.tx_res_state < PgsqlTxProgress::Done { - tx_old.tx_data.updated_tc = true; - tx_old.tx_data.updated_ts = true; + tx_old.tx_data.0.updated_tc = true; + tx_old.tx_data.0.updated_ts = true; // we don't check for TxReqDone for the majority of requests are basically completed // when they're parsed, as of now tx_old.tx_req_state = PgsqlTxProgress::FlushedOut; @@ -413,7 +413,7 @@ impl PgsqlState { // A simplified finite state machine for PostgreSQL v3 can be found at: // https://samadhiweb.com/blog/2013.04.28.graphviz.postgresv3.html if let Some(tx) = self.find_or_create_tx() { - tx.tx_data.updated_ts = true; + tx.tx_data.0.updated_ts = true; if let Some(state) = new_state { if state == PgsqlStateProgress::FirstCopyDataInReceived || state == PgsqlStateProgress::ConsolidatingCopyDataIn { @@ -629,7 +629,7 @@ impl PgsqlState { self.state_progress = state; } if let Some(tx) = self.find_or_create_tx() { - tx.tx_data.updated_tc = true; + tx.tx_data.0.updated_tc = true; if tx.tx_res_state == PgsqlTxProgress::Init { tx.tx_res_state = PgsqlTxProgress::Received; } diff --git a/rust/src/pop3/pop3.rs b/rust/src/pop3/pop3.rs index ee2b277ff1..9c3127f1b3 100644 --- a/rust/src/pop3/pop3.rs +++ b/rust/src/pop3/pop3.rs @@ -197,8 +197,8 @@ impl POP3State { if self.transactions.len() > unsafe { POP3_MAX_TX } { for tx_old in &mut self.transactions { if !tx_old.complete { - tx_old.tx_data.updated_tc = true; - tx_old.tx_data.updated_ts = true; + tx_old.tx_data.0.updated_tc = true; + tx_old.tx_data.0.updated_ts = true; tx_old.complete = true; tx_old .tx_data diff --git a/rust/src/rfb/rfb.rs b/rust/src/rfb/rfb.rs index e834dd22d4..1decbf3c69 100644 --- a/rust/src/rfb/rfb.rs +++ b/rust/src/rfb/rfb.rs @@ -177,8 +177,8 @@ impl RFBState { let tx_id = self.tx_id; let r = self.transactions.iter_mut().find(|tx| tx.tx_id == tx_id); if let Some(tx) = r { - tx.tx_data.updated_tc = true; - tx.tx_data.updated_ts = true; + tx.tx_data.0.updated_tc = true; + tx.tx_data.0.updated_ts = true; return Some(tx); } return None; diff --git a/rust/src/smb/dcerpc.rs b/rust/src/smb/dcerpc.rs index c7560b7a66..54e8513b1a 100644 --- a/rust/src/smb/dcerpc.rs +++ b/rust/src/smb/dcerpc.rs @@ -168,8 +168,8 @@ impl SMBState { _ => { false }, }; if found { - tx.tx_data.updated_tc = true; - tx.tx_data.updated_ts = true; + tx.tx_data.0.updated_tc = true; + tx.tx_data.0.updated_ts = true; return Some(tx); } } diff --git a/rust/src/smb/files.rs b/rust/src/smb/files.rs index 0352eb7f0f..3d91c801e9 100644 --- a/rust/src/smb/files.rs +++ b/rust/src/smb/files.rs @@ -97,10 +97,10 @@ impl SMBState { d.file_name = file_name.to_vec(); d.file_tracker.tx_id = tx.id - 1; tx.tx_data.update_file_flags(self.state_data.file_flags); - d.update_file_flags(tx.tx_data.file_flags); + d.update_file_flags(tx.tx_data.0.file_flags); } tx.tx_data.init_files_opened(); - tx.tx_data.file_tx = if direction == Direction::ToServer { STREAM_TOSERVER } else { STREAM_TOCLIENT }; // TODO direction to flag func? + tx.tx_data.0.file_tx = if direction == Direction::ToServer { STREAM_TOSERVER } else { STREAM_TOCLIENT }; // TODO direction to flag func? SCLogDebug!("SMB: new_file_tx: TX FILE created: ID {} NAME {}", tx.id, String::from_utf8_lossy(file_name)); self.transactions.push_back(tx); @@ -126,10 +126,10 @@ impl SMBState { SCLogDebug!("SMB: Found SMB file TX with ID {}", tx.id); if let Some(SMBTransactionTypeData::FILE(ref mut d)) = tx.type_data { tx.tx_data.update_file_flags(self.state_data.file_flags); - d.update_file_flags(tx.tx_data.file_flags); + d.update_file_flags(tx.tx_data.0.file_flags); } - tx.tx_data.updated_tc = true; - tx.tx_data.updated_ts = true; + tx.tx_data.0.updated_tc = true; + tx.tx_data.0.updated_ts = true; return Some(tx); } } @@ -154,10 +154,10 @@ impl SMBState { SCLogDebug!("SMB: Found SMB file TX with ID {}", tx.id); if let Some(SMBTransactionTypeData::FILE(ref mut d)) = tx.type_data { tx.tx_data.update_file_flags(self.state_data.file_flags); - d.update_file_flags(tx.tx_data.file_flags); + d.update_file_flags(tx.tx_data.0.file_flags); } - tx.tx_data.updated_tc = true; - tx.tx_data.updated_ts = true; + tx.tx_data.0.updated_tc = true; + tx.tx_data.0.updated_ts = true; return Some(tx); } } diff --git a/rust/src/smb/session.rs b/rust/src/smb/session.rs index 241973fc2b..05d6ab4c3c 100644 --- a/rust/src/smb/session.rs +++ b/rust/src/smb/session.rs @@ -61,8 +61,8 @@ impl SMBState { _ => { false }, }; if hit { - tx.tx_data.updated_tc = true; - tx.tx_data.updated_ts = true; + tx.tx_data.0.updated_tc = true; + tx.tx_data.0.updated_ts = true; return Some(tx); } } diff --git a/rust/src/smb/smb.rs b/rust/src/smb/smb.rs index ed7a60e391..b49e583f05 100644 --- a/rust/src/smb/smb.rs +++ b/rust/src/smb/smb.rs @@ -546,8 +546,8 @@ impl SMBTransaction { pub fn free(&mut self) { SCLogDebug!("SMB TX {:p} free ID {}", &self, self.id); - debug_validate_bug_on!(self.tx_data.files_opened > 1); - debug_validate_bug_on!(self.tx_data.files_logged > 1); + debug_validate_bug_on!(self.tx_data.0.files_opened > 1); + debug_validate_bug_on!(self.tx_data.0.files_logged > 1); } } @@ -844,8 +844,8 @@ impl SMBState { for tx_old in &mut self.transactions.range_mut(self.tx_index_completed..) { index += 1; if !tx_old.request_done || !tx_old.response_done { - tx_old.tx_data.updated_tc = true; - tx_old.tx_data.updated_ts = true; + tx_old.tx_data.0.updated_tc = true; + tx_old.tx_data.0.updated_ts = true; tx_old.request_done = true; tx_old.response_done = true; tx_old.set_event(SMBEvent::TooManyTransactions); @@ -904,7 +904,7 @@ impl SMBState { /* hack: apply flow file flags to file tx here to make sure its propagated */ if let Some(SMBTransactionTypeData::FILE(ref mut d)) = tx.type_data { tx.tx_data.update_file_flags(self.state_data.file_flags); - d.update_file_flags(tx.tx_data.file_flags); + d.update_file_flags(tx.tx_data.0.file_flags); } return Some(tx); } @@ -964,8 +964,8 @@ impl SMBState { false }; if found { - tx.tx_data.updated_tc = true; - tx.tx_data.updated_ts = true; + tx.tx_data.0.updated_tc = true; + tx.tx_data.0.updated_ts = true; return Some(tx); } } @@ -990,8 +990,8 @@ impl SMBState { false }; if found { - tx.tx_data.updated_tc = true; - tx.tx_data.updated_ts = true; + tx.tx_data.0.updated_tc = true; + tx.tx_data.0.updated_ts = true; return Some(tx); } } @@ -1030,8 +1030,8 @@ impl SMBState { _ => { false }, }; if found { - tx.tx_data.updated_tc = true; - tx.tx_data.updated_ts = true; + tx.tx_data.0.updated_tc = true; + tx.tx_data.0.updated_ts = true; return Some(tx); } } @@ -1065,8 +1065,8 @@ impl SMBState { _ => { false }, }; if hit { - tx.tx_data.updated_tc = true; - tx.tx_data.updated_ts = true; + tx.tx_data.0.updated_tc = true; + tx.tx_data.0.updated_ts = true; return Some(tx); } } @@ -2261,10 +2261,10 @@ export_state_data_get!(smb_get_state_data, SMBState); unsafe extern "C" fn smb_get_tx_data( tx: *mut std::os::raw::c_void) - -> *mut AppLayerTxData + -> *mut suricata_sys::sys::AppLayerTxData { let tx = cast_pointer!(tx, SMBTransaction); - return &mut tx.tx_data; + return &mut tx.tx_data.0; } unsafe extern "C" fn smb_state_get_event_info_by_id( diff --git a/rust/src/ssh/ssh.rs b/rust/src/ssh/ssh.rs index bcf23b9d76..2adcd1db55 100644 --- a/rust/src/ssh/ssh.rs +++ b/rust/src/ssh/ssh.rs @@ -459,7 +459,7 @@ unsafe extern "C" fn ssh_parse_request( let state = &mut cast_pointer!(state, SSHState); let buf = stream_slice.as_slice(); let hdr = &mut state.transaction.cli_hdr; - state.transaction.tx_data.updated_ts = true; + state.transaction.tx_data.0.updated_ts = true; if hdr.flags < SSHConnectionState::SshStateBannerDone { return state.parse_banner(buf, false, pstate, flow, &stream_slice); } else { @@ -474,7 +474,7 @@ unsafe extern "C" fn ssh_parse_response( let state = &mut cast_pointer!(state, SSHState); let buf = stream_slice.as_slice(); let hdr = &mut state.transaction.srv_hdr; - state.transaction.tx_data.updated_tc = true; + state.transaction.tx_data.0.updated_tc = true; if hdr.flags < SSHConnectionState::SshStateBannerDone { return state.parse_banner(buf, true, pstate, flow, &stream_slice); } else { diff --git a/rust/src/tftp/tftp.rs b/rust/src/tftp/tftp.rs index f6a95cbadd..17b0ec6ba6 100644 --- a/rust/src/tftp/tftp.rs +++ b/rust/src/tftp/tftp.rs @@ -25,7 +25,7 @@ use nom8::combinator::map_res; use nom8::bytes::streaming::{tag, take_while}; use nom8::number::streaming::be_u8; -use crate::applayer::{AppLayerTxData,AppLayerStateData}; +use crate::applayer::{AppLayerTxData, AppLayerStateData}; const READREQUEST: u8 = 1; const WRITEREQUEST: u8 = 2; @@ -176,10 +176,10 @@ pub unsafe extern "C" fn SCTftpParseRequest(state: &mut TFTPState, #[no_mangle] pub unsafe extern "C" fn SCTftpGetTxData( tx: *mut std::os::raw::c_void) - -> *mut AppLayerTxData + -> *mut suricata_sys::sys::AppLayerTxData { let tx = cast_pointer!(tx, TFTPTransaction); - return &mut tx.tx_data; + return &mut tx.tx_data.0; } #[no_mangle] diff --git a/rust/sys/src/sys.rs b/rust/sys/src/sys.rs index 02c905b95a..77d3b31cb9 100644 --- a/rust/sys/src/sys.rs +++ b/rust/sys/src/sys.rs @@ -994,11 +994,6 @@ pub struct File_ { _unused: [u8; 0], } pub type File = File_; -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct AppLayerTxData { - _unused: [u8; 0], -} extern "C" { #[doc = " \\brief Given a protocol name, checks if the parser is enabled in\n the conf file.\n\n \\param alproto_name Name of the app layer protocol.\n\n \\retval 1 If enabled.\n \\retval 0 If disabled."] pub fn SCAppLayerParserConfParserEnabled( @@ -1080,6 +1075,52 @@ pub struct AppLayerResult { pub consumed: u32, pub needed: u32, } +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)] +pub struct AppLayerTxConfig { + #[doc = " config: log flags"] + pub log_flags: u8, +} +pub type GenericVar = GenericVar_; +#[repr(C)] +#[derive(Debug, PartialEq, Eq)] +pub struct AppLayerTxData { + #[doc = " config: log flags"] + pub config: AppLayerTxConfig, + #[doc = " The tx has been updated and needs to be processed : detection, logging, cleaning\n It can then be skipped until new data arrives.\n There is a boolean for both directions : to server and to client"] + pub updated_tc: bool, + pub updated_ts: bool, + pub flags: u8, + #[doc = " logger flags for tx logging api"] + pub logged: u32, + #[doc = " track file open/logs so we can know how long to keep the tx"] + pub files_opened: u32, + pub files_logged: u32, + pub files_stored: u32, + pub file_flags: u16, + #[doc = " Indicated if a file tracking tx, and if so in which direction:\n 0: not a file tx\n STREAM_TOSERVER: file tx, files only in toserver dir\n STREAM_TOCLIENT: file tx , files only in toclient dir\n STREAM_TOSERVER|STREAM_TOCLIENT: files possible in both dirs"] + pub file_tx: u8, + #[doc = " Number of times this tx data has already been logged for signatures\n not using application layer keywords"] + pub guessed_applayer_logged: u8, + #[doc = " detection engine progress tracking for use by detection engine\n Reflects the \"progress\" of prefilter engines into this TX, where\n the value is offset by 1. So if for progress state 0 the engines\n are done, the value here will be 1. So a value of 0 means, no\n progress tracked yet.\n"] + pub detect_progress_ts: u8, + pub detect_progress_tc: u8, + pub de_state: *mut DetectEngineState, + pub events: *mut AppLayerDecoderEvents, + pub txbits: *mut GenericVar, +} +impl Default for AppLayerTxData { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +extern "C" { + pub fn SCAppLayerTxDataCleanup(txd: *mut AppLayerTxData); +} extern "C" { pub fn SCAppLayerParserReallocCtx(alproto: AppProto) -> ::std::os::raw::c_int; } @@ -1417,7 +1458,6 @@ impl Default for GenericVar_ { } } } -pub type GenericVar = GenericVar_; extern "C" { pub fn SCGenericVarFree(arg1: *mut GenericVar); } diff --git a/src/app-layer-parser.c b/src/app-layer-parser.c index 313a55a8fb..0ab84a8269 100644 --- a/src/app-layer-parser.c +++ b/src/app-layer-parser.c @@ -744,6 +744,19 @@ static inline uint32_t GetTxLogged(AppLayerTxData *txd) return txd->logged; } +void SCAppLayerTxDataCleanup(AppLayerTxData *txd) +{ + if (txd->de_state) { + SCDetectEngineStateFree(txd->de_state); + } + if (txd->events) { + SCAppLayerDecoderEventsFreeEvents(&txd->events); + } + if (txd->txbits) { + SCGenericVarFree(txd->txbits); + } +} + void AppLayerParserSetTransactionInspectId(const Flow *f, AppLayerParserState *pstate, void *alstate, const uint8_t flags, bool tag_txs_as_inspected) diff --git a/src/app-layer-parser.h b/src/app-layer-parser.h index 2b35daa2eb..d9d983d880 100644 --- a/src/app-layer-parser.h +++ b/src/app-layer-parser.h @@ -27,6 +27,7 @@ #include "app-layer-protos.h" #include "app-layer-events.h" +#include "detect-engine-state.h" // Forward declarations for bindgen enum ConfigAction; typedef struct Flow_ Flow; @@ -40,8 +41,6 @@ typedef struct StreamSlice StreamSlice; typedef struct AppLayerResult AppLayerResult; typedef struct AppLayerGetTxIterTuple AppLayerGetTxIterTuple; typedef struct AppLayerGetFileState AppLayerGetFileState; -typedef struct AppLayerTxData AppLayerTxData; -typedef struct AppLayerTxConfig AppLayerTxConfig; /* Flags for AppLayerParserState. */ // flag available BIT_U16(0) @@ -171,6 +170,61 @@ typedef struct AppLayerResult { uint32_t needed; } AppLayerResult; +typedef struct AppLayerTxConfig { + /// config: log flags + uint8_t log_flags; +} AppLayerTxConfig; + +typedef struct GenericVar_ GenericVar; + +typedef struct AppLayerTxData { + /// config: log flags + AppLayerTxConfig config; + + /// The tx has been updated and needs to be processed : detection, logging, cleaning + /// It can then be skipped until new data arrives. + /// There is a boolean for both directions : to server and to client + bool updated_tc; + bool updated_ts; + + uint8_t flags; + + /// logger flags for tx logging api + uint32_t logged; + + /// track file open/logs so we can know how long to keep the tx + uint32_t files_opened; + uint32_t files_logged; + uint32_t files_stored; + + uint16_t file_flags; + + /// Indicated if a file tracking tx, and if so in which direction: + /// 0: not a file tx + /// STREAM_TOSERVER: file tx, files only in toserver dir + /// STREAM_TOCLIENT: file tx , files only in toclient dir + /// STREAM_TOSERVER|STREAM_TOCLIENT: files possible in both dirs + uint8_t file_tx; + /// Number of times this tx data has already been logged for signatures + /// not using application layer keywords + uint8_t guessed_applayer_logged; + + /// detection engine progress tracking for use by detection engine + /// Reflects the "progress" of prefilter engines into this TX, where + /// the value is offset by 1. So if for progress state 0 the engines + /// are done, the value here will be 1. So a value of 0 means, no + /// progress tracked yet. + /// + uint8_t detect_progress_ts; + uint8_t detect_progress_tc; + + DetectEngineState *de_state; + AppLayerDecoderEvents *events; + GenericVar *txbits; +} AppLayerTxData; + +void SCAppLayerTxDataCleanup(AppLayerTxData *txd); + /** \brief tx iterator prototype */ typedef AppLayerGetTxIterTuple (*AppLayerGetTxIteratorFunc) (const uint8_t ipproto, const AppProto alproto, diff --git a/src/tx-bit.c b/src/tx-bit.c index 1ffe127254..814d460e8f 100644 --- a/src/tx-bit.c +++ b/src/tx-bit.c @@ -35,6 +35,7 @@ #include "util-var.h" #include "util-debug.h" #include "rust.h" +#include "app-layer-parser.h" static XBit *TxBitGet(AppLayerTxData *txd, uint32_t idx) {