]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
app-layer: track modified/processed txs
authorPhilippe Antoine <pantoine@oisf.net>
Fri, 31 May 2024 08:39:16 +0000 (10:39 +0200)
committerVictor Julien <victor@inliniac.net>
Tue, 10 Dec 2024 13:40:04 +0000 (14:40 +0100)
To optimize detection, and logging, to avoid going through
all the live transactions when only a few were modified.

Two boolean fields are added to the tx data: updated_tc and ts
The app-layer parsers are now responsible to set these when
needed, and the logging and detection uses them to skip
transactions that were not updated.

There may some more optimization remaining by when we set
both updated_tc and updated_ts in functions returning
a mutable transaction, by checking if all the callers
are called in one direction only (request or response)

Ticket: 7087

25 files changed:
rust/src/applayer.rs
rust/src/applayertemplate/template.rs
rust/src/dcerpc/dcerpc.rs
rust/src/dcerpc/dcerpc_udp.rs
rust/src/enip/enip.rs
rust/src/http2/http2.rs
rust/src/ldap/ldap.rs
rust/src/modbus/modbus.rs
rust/src/mqtt/mqtt.rs
rust/src/nfs/nfs.rs
rust/src/pgsql/pgsql.rs
rust/src/rfb/rfb.rs
rust/src/smb/dcerpc.rs
rust/src/smb/files.rs
rust/src/smb/session.rs
rust/src/smb/smb.rs
rust/src/ssh/ssh.rs
src/app-layer-dnp3.c
src/app-layer-ftp.c
src/app-layer-htp.c
src/app-layer-parser.c
src/app-layer-smtp.c
src/app-layer-ssl.c
src/detect.c
src/output-tx.c

index ac9800d634391ecab860e25f0b7f3e4e5b5fa362..4535e563aaaf88b68333d46ab897f0555eceea6a 100644 (file)
@@ -98,6 +98,12 @@ 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,
+
     /// logger flags for tx logging api
     logged: LoggerFlags,
 
@@ -155,6 +161,8 @@ impl AppLayerTxData {
             file_flags: 0,
             file_tx: 0,
             stream_logged: 0,
+            updated_tc: true,
+            updated_ts: true,
             detect_flags_ts: 0,
             detect_flags_tc: 0,
             de_state: std::ptr::null_mut(),
@@ -165,9 +173,9 @@ impl AppLayerTxData {
     /// Create new AppLayerTxData for a transaction in a single
     /// direction.
     pub fn for_direction(direction: Direction) -> Self {
-        let (detect_flags_ts, detect_flags_tc) = match direction {
-            Direction::ToServer => (0, APP_LAYER_TX_SKIP_INSPECT_FLAG),
-            Direction::ToClient => (APP_LAYER_TX_SKIP_INSPECT_FLAG, 0),
+        let (detect_flags_ts, detect_flags_tc, updated_ts, updated_tc) = match direction {
+            Direction::ToServer => (0, APP_LAYER_TX_SKIP_INSPECT_FLAG, true, false),
+            Direction::ToClient => (APP_LAYER_TX_SKIP_INSPECT_FLAG, 0, false, true),
         };
         Self {
             config: AppLayerTxConfig::new(),
@@ -178,6 +186,8 @@ impl AppLayerTxData {
             file_flags: 0,
             file_tx: 0,
             stream_logged: 0,
+            updated_tc,
+            updated_ts,
             detect_flags_ts,
             detect_flags_tc,
             de_state: std::ptr::null_mut(),
index 1580c4fdf3dda472311b4a21765fa4587e1f7423..eebe7385db129cf81a50316a8cfa94229838cd68 100644 (file)
@@ -200,6 +200,7 @@ impl TemplateState {
                     start = rem;
 
                     if let Some(tx) =  self.find_request() {
+                        tx.tx_data.updated_tc = true;
                         tx.response = Some(response);
                         SCLogNotice!("Found response for request:");
                         SCLogNotice!("- Request: {:?}", tx.request);
index 32ebf6990134c42b07ba03629bf1335549474721..9253a2bde8e96f7c8563ae17db867df1b3fd271a 100644 (file)
@@ -357,6 +357,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.req_done = true;
                     tx_old.resp_done = true;
                     break;
@@ -533,6 +535,8 @@ impl DCERPCState {
                         }
                     }
                 }
+                tx.tx_data.updated_tc = true;
+                tx.tx_data.updated_ts = true;
                 return Some(tx);
             }
         }
index ab7f65cafbadf8935fb6197212734cd6caa73a84..fee460f9b3e02def4f21effbdce4a4886d3ec6c6 100644 (file)
@@ -88,6 +88,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.req_done = true;
                     tx_old.resp_done = true;
                     break;
@@ -164,6 +166,8 @@ impl DCERPCUDPState {
         }
 
         if let Some(tx) = otx {
+            tx.tx_data.updated_tc = true;
+            tx.tx_data.updated_ts = true;
             let done = (hdr.flags1 & PFCL1_FRAG) == 0 || (hdr.flags1 & PFCL1_LASTFRAG) != 0;
 
             match hdr.pkt_type {
index 31c9152e5cabe45dff7732aa68e7a4bf9c37f16b..18e728122641d47414442c0b12b1c846c36fddaa 100644 (file)
@@ -203,6 +203,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.done = true;
             if !event_set {
                 tx.tx_data.set_event(EnipEvent::TooManyTransactions as u8);
@@ -216,6 +218,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;
                     if response_matches_request(req, pdu) {
                         return Some(tx);
                     }
index 89b599a9f8efa5dda6a1f934fda02be29c48e0ea..8ed0157922ba406966df3f328b8ac3e84a981c1e 100644 (file)
@@ -752,6 +752,8 @@ 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;
             return Some(tx);
         } else {
             // do not use SETTINGS_MAX_CONCURRENT_STREAMS as it can grow too much
@@ -764,6 +766,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;
                 }
                 return None;
             }
index f86c8d8499d9c6f36501b1bdf068e384827b7b5c..4fb8ccc10b97426b260614bdf85c5cb306338dce 100644 (file)
@@ -156,6 +156,8 @@ impl LdapState {
             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.complete = true;
                     tx_old
                         .tx_data
@@ -308,6 +310,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.responses.push_back(response);
                         let consumed = start.len() - rem.len();
                         self.set_frame_tc(flow, tx_id, consumed as i64);
index 0d0c73371ef03ab033d3db7fa87656ec8bccaf59..bbc191555696e4d3c0f03817c02e97ec930d2c39 100644 (file)
@@ -124,6 +124,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;
                     return Some(tx);
                 }
             }
@@ -139,6 +141,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;
                     return Some(tx);
                 }
             }
@@ -184,6 +188,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.request = Some(msg);
                                 }
                                 None => {
@@ -210,6 +216,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.response = Some(msg);
                             }
                             None => {
index 4079daa6de0591d30f865c8de82cff3d6f26bb7b..e2905a4c603b81aa8f2d50194f478ebe488512c7 100644 (file)
@@ -174,6 +174,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;
                         return Some(tx);
                     }
                 }
@@ -196,6 +198,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.complete = true;
                     MQTTState::set_event(tx_old, MQTTEvent::TooManyTransactions);
                     break;
index d4b472e1f69c4f55ecec1d14bb77c275aa3028b5..15effa4f66f1ae09bb3279dcb2256bef0025efa7 100644 (file)
@@ -431,6 +431,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.request_done = true;
                     tx_old.response_done = true;
                     tx_old.is_file_closed = true;
@@ -484,6 +486,8 @@ impl NFSState {
     pub fn mark_response_tx_done(&mut self, 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.response_done = true;
             mytx.rpc_response_status = rpc_status;
             mytx.nfs_response_status = nfs_status;
@@ -736,6 +740,8 @@ impl NFSState {
                     tx.tx_data.update_file_flags(self.state_data.file_flags);
                     d.update_file_flags(tx.tx_data.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;
                     return Some(tx);
                 }
             }
index 658c2326ffd5aa3e9e4f6fb1d2c49b6a03d7d516..ad57590b3a36cf9ef3e3d62b18da6065a7068563 100644 (file)
@@ -208,6 +208,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::TxDone {
+                    tx_old.tx_data.updated_tc = true;
+                    tx_old.tx_data.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::TxFlushedOut;
@@ -360,6 +362,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.request = Some(request);
                         if let Some(state) = new_state {
                             if Self::request_is_complete(state) {
@@ -518,6 +521,7 @@ impl PgsqlState {
                         self.state_progress = state;
                     }
                     if let Some(tx) = self.find_or_create_tx() {
+                        tx.tx_data.updated_tc = true;
                         if tx.tx_res_state == PgsqlTxProgress::TxInit {
                             tx.tx_res_state = PgsqlTxProgress::TxReceived;
                         }
index 810ed1d85d9a9803bcb18e451f2289fa54d8ec74..5c226af3ab200455b1fc291c0ef97fed02ff3bef 100644 (file)
@@ -166,7 +166,13 @@ impl RFBState {
 
     fn get_current_tx(&mut self) -> Option<&mut RFBTransaction> {
         let tx_id = self.tx_id;
-        self.transactions.iter_mut().find(|tx| tx.tx_id == 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;
+            return Some(tx);
+        }
+        return None;
     }
 
     fn parse_request(&mut self, flow: *const Flow, stream_slice: StreamSlice) -> AppLayerResult {
index 771e2538c2ac427f2042f2f57a7215c479e1a365..d528b372a155206de05ac0559068921a4d46b77d 100644 (file)
@@ -168,6 +168,8 @@ impl SMBState {
                 _ => { false },
             };
             if found {
+                tx.tx_data.updated_tc = true;
+                tx.tx_data.updated_ts = true;
                 return Some(tx);
             }
         }
index bdc2619a173d6ff93c9986a29e70da1434461f04..e1de7d171a0f7e3ccfcb9b1beb8d3b2bb5a49d1c 100644 (file)
@@ -126,6 +126,8 @@ impl SMBState {
                     tx.tx_data.update_file_flags(self.state_data.file_flags);
                     d.update_file_flags(tx.tx_data.file_flags);
                 }
+                tx.tx_data.updated_tc = true;
+                tx.tx_data.updated_ts = true;
                 return Some(tx);
             }
         }
@@ -152,6 +154,8 @@ impl SMBState {
                     tx.tx_data.update_file_flags(self.state_data.file_flags);
                     d.update_file_flags(tx.tx_data.file_flags);
                 }
+                tx.tx_data.updated_tc = true;
+                tx.tx_data.updated_ts = true;
                 return Some(tx);
             }
         }
index be7866976dc9822b9484965a1950fa14fc373325..241973fc2b4d9b47a027c8ec165175669d91f2b2 100644 (file)
@@ -61,6 +61,8 @@ impl SMBState {
                 _ => { false },
             };
             if hit {
+                tx.tx_data.updated_tc = true;
+                tx.tx_data.updated_ts = true;
                 return Some(tx);
             }
         }
index 9bba6e03454f4a137c66e7897ec8f97db87ed9b8..1b34d94462aac2a67b2eebab97567474da3f20e4 100644 (file)
@@ -833,6 +833,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.request_done = true;
                     tx_old.response_done = true;
                     tx_old.set_event(SMBEvent::TooManyTransactions);
@@ -951,6 +953,8 @@ impl SMBState {
                 false
             };
             if found {
+                tx.tx_data.updated_tc = true;
+                tx.tx_data.updated_ts = true;
                 return Some(tx);
             }
         }
@@ -975,6 +979,8 @@ impl SMBState {
                 false
             };
             if found {
+                tx.tx_data.updated_tc = true;
+                tx.tx_data.updated_ts = true;
                 return Some(tx);
             }
         }
@@ -1013,6 +1019,8 @@ impl SMBState {
                 _ => { false },
             };
             if found {
+                tx.tx_data.updated_tc = true;
+                tx.tx_data.updated_ts = true;
                 return Some(tx);
             }
         }
@@ -1046,6 +1054,8 @@ impl SMBState {
                 _ => { false },
             };
             if hit {
+                tx.tx_data.updated_tc = true;
+                tx.tx_data.updated_ts = true;
                 return Some(tx);
             }
         }
index a6a3871a8e6061c53d6ca968bf47901345a9902a..99c88c9d7cf5c8bb8dc91fdb54ab80a866eddd1f 100644 (file)
@@ -415,6 +415,7 @@ pub unsafe extern "C" fn rs_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;
     if hdr.flags < SSHConnectionState::SshStateBannerDone {
         return state.parse_banner(buf, false, pstate, flow, &stream_slice);
     } else {
@@ -431,6 +432,7 @@ pub unsafe extern "C" fn rs_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;
     if hdr.flags < SSHConnectionState::SshStateBannerDone {
         return state.parse_banner(buf, true, pstate, flow, &stream_slice);
     } else {
index c995667ee0e39cab3b6c8e6e9b8300cad6625be1..a1daf3a2e1a4c8a3f5ed2a3b387a9d0b1390ff8d 100644 (file)
@@ -882,6 +882,7 @@ static void DNP3HandleUserDataRequest(DNP3State *dnp3, const uint8_t *input,
         /* Update the saved transport header so subsequent segments
          * will be matched to this sequence number. */
         tx->th = th;
+        tx->tx_data.updated_ts = true;
     }
     else {
         ah = (DNP3ApplicationHeader *)(input + sizeof(DNP3LinkHeader) +
@@ -959,6 +960,7 @@ static void DNP3HandleUserDataResponse(DNP3State *dnp3, const uint8_t *input,
         /* Replace the transport header in the transaction with this
          * one in case there are more frames. */
         tx->th = th;
+        tx->tx_data.updated_tc = true;
     }
     else {
         ah = (DNP3ApplicationHeader *)(input + offset);
index 15238b9f65ef14bc81111e8ebf79cf5047e9c3f4..a6a1f632fcb4c56bf3cc71c5283075f5054dd02b 100644 (file)
@@ -724,6 +724,7 @@ static AppLayerResult FTPParseResponse(Flow *f, void *ftp_state, AppLayerParserS
             SCReturnStruct(APP_LAYER_ERROR);
         }
         lasttx = tx;
+        tx->tx_data.updated_tc = true;
         if (state->command == FTP_COMMAND_UNKNOWN || tx->command_descriptor == NULL) {
             /* unknown */
             tx->command_descriptor = &FtpCommands[FTP_COMMAND_MAX - 1];
@@ -1056,7 +1057,11 @@ static AppLayerResult FTPDataParse(Flow *f, FtpDataState *ftpdata_state,
     SCTxDataUpdateFileFlags(&ftpdata_state->tx_data, ftpdata_state->state_data.file_flags);
     if (ftpdata_state->tx_data.file_tx == 0)
         ftpdata_state->tx_data.file_tx = direction & (STREAM_TOSERVER | STREAM_TOCLIENT);
-
+    if (direction & STREAM_TOSERVER) {
+        ftpdata_state->tx_data.updated_ts = true;
+    } else {
+        ftpdata_state->tx_data.updated_tc = true;
+    }
     /* we depend on detection engine for file pruning */
     const uint16_t flags = FileFlowFlagsToFlags(ftpdata_state->tx_data.file_flags, direction);
     int ret = 0;
index f1d3c56c16afc4ef65f8250716afd27d80894f83..328a10b72cb832b76a591776e6069bf9de29bb81 100644 (file)
@@ -1449,6 +1449,7 @@ static int HTPCallbackRequestBodyData(htp_tx_data_t *d)
     if (tx_ud == NULL) {
         SCReturnInt(HTP_OK);
     }
+    tx_ud->tx_data.updated_ts = true;
     SCTxDataUpdateFileFlags(&tx_ud->tx_data, hstate->state_data.file_flags);
 
     if (!tx_ud->response_body_init) {
@@ -1580,6 +1581,7 @@ static int HTPCallbackResponseBodyData(htp_tx_data_t *d)
     if (tx_ud == NULL) {
         SCReturnInt(HTP_OK);
     }
+    tx_ud->tx_data.updated_tc = true;
     SCTxDataUpdateFileFlags(&tx_ud->tx_data, hstate->state_data.file_flags);
     if (!tx_ud->request_body_init) {
         tx_ud->request_body_init = 1;
@@ -1686,6 +1688,7 @@ static int HTPCallbackRequestHasTrailer(htp_tx_t *tx)
 {
     HtpTxUserData *htud = (HtpTxUserData *)htp_tx_get_user_data(tx);
     if (htud != NULL) {
+        htud->tx_data.updated_ts = true;
         htud->request_has_trailers = 1;
     }
     return HTP_OK;
@@ -1695,6 +1698,7 @@ static int HTPCallbackResponseHasTrailer(htp_tx_t *tx)
 {
     HtpTxUserData *htud = (HtpTxUserData *)htp_tx_get_user_data(tx);
     if (htud != NULL) {
+        htud->tx_data.updated_tc = true;
         htud->response_has_trailers = 1;
     }
     return HTP_OK;
@@ -1737,6 +1741,8 @@ static int HTPCallbackRequestStart(htp_tx_t *tx)
         }
         tx_ud->tx_data.file_tx = STREAM_TOSERVER | STREAM_TOCLIENT; // each http tx may xfer files
         htp_tx_set_user_data(tx, tx_ud);
+    } else {
+        tx_ud->tx_data.updated_ts = true;
     }
     SCReturnInt(HTP_OK);
 }
@@ -1777,6 +1783,8 @@ static int HTPCallbackResponseStart(htp_tx_t *tx)
         tx_ud->tx_data.file_tx =
                 STREAM_TOCLIENT; // each http tx may xfer files. Toserver already missed.
         htp_tx_set_user_data(tx, tx_ud);
+    } else {
+        tx_ud->tx_data.updated_tc = true;
     }
     SCReturnInt(HTP_OK);
 }
@@ -1828,6 +1836,7 @@ static int HTPCallbackRequestComplete(htp_tx_t *tx)
 
     HtpTxUserData *htud = (HtpTxUserData *)htp_tx_get_user_data(tx);
     if (htud != NULL) {
+        htud->tx_data.updated_ts = true;
         if (htud->tsflags & HTP_FILENAME_SET) {
             SCLogDebug("closing file that was being stored");
             (void)HTPFileClose(htud, NULL, 0, 0, STREAM_TOSERVER);
@@ -1883,6 +1892,7 @@ static int HTPCallbackResponseComplete(htp_tx_t *tx)
 
     HtpTxUserData *htud = (HtpTxUserData *) htp_tx_get_user_data(tx);
     if (htud != NULL) {
+        htud->tx_data.updated_tc = true;
         if (htud->tcflags & HTP_FILENAME_SET) {
             SCLogDebug("closing file that was being stored");
             (void)HTPFileClose(htud, NULL, 0, 0, STREAM_TOCLIENT);
@@ -2001,6 +2011,7 @@ static int HTPCallbackRequestHeaderData(htp_tx_data_t *tx_data)
         return HTP_OK;
     }
     tx_ud->request_headers_raw = ptmp;
+    tx_ud->tx_data.updated_ts = true;
 
     memcpy(tx_ud->request_headers_raw + tx_ud->request_headers_raw_len,
            tx_data->data, tx_data->len);
@@ -2023,6 +2034,7 @@ static int HTPCallbackResponseHeaderData(htp_tx_data_t *tx_data)
     if (tx_ud == NULL) {
         return HTP_OK;
     }
+    tx_ud->tx_data.updated_tc = true;
     ptmp = HTPRealloc(tx_ud->response_headers_raw,
                      tx_ud->response_headers_raw_len,
                      tx_ud->response_headers_raw_len + tx_data->len);
index d1cacc572f40f3ba8463e465b57e8e7ac19d036e..045fcb086e1f21a5efa0136b058bb8ad1b1aea5e 100644 (file)
@@ -935,7 +935,14 @@ void AppLayerParserTransactionsCleanup(Flow *f, const uint8_t pkt_dir)
                         (pkt_dir == STREAM_TOSERVER) ? ts_disrupt_flags : tc_disrupt_flags);
             AppLayerParserFileTxHousekeeping(f, tx, pkt_dir, (bool)pkt_dir_trunc);
         }
-
+        if (txd) {
+            // should be reset by parser next time it updates the tx
+            if (pkt_dir & STREAM_TOSERVER) {
+                txd->updated_ts = false;
+            } else {
+                txd->updated_tc = false;
+            }
+        }
         const int tx_progress_tc =
                 AppLayerParserGetStateProgress(ipproto, alproto, tx, tc_disrupt_flags);
         if (tx_progress_tc < tx_end_state_tc) {
index ec4799605cbd36216bfe717d7a4d5102831b2da7..b2bf22a53145001ee0633e299f84590743da6942 100644 (file)
@@ -860,6 +860,9 @@ static int SMTPProcessReply(
         return 0; // to continue processing further
     }
 
+    if (state->curr_tx) {
+        state->curr_tx->tx_data.updated_tc = true;
+    }
     /* the reply code has to contain at least 3 bytes, to hold the 3 digit
      * reply code */
     if (line->len < 3) {
@@ -1164,6 +1167,7 @@ static int SMTPProcessRequest(
     if (frame != NULL && state->curr_tx) {
         AppLayerFrameSetTxId(frame, state->curr_tx->tx_id);
     }
+    tx->tx_data.updated_ts = true;
 
     state->toserver_data_count += (line->len + line->delim_len);
 
index 05ba2239b28123053451f4e54ef624172df61dda..e387c6cc46c42dd9af113bf01712edbcbfff313d 100644 (file)
@@ -2673,6 +2673,8 @@ static AppLayerResult SSLDecode(Flow *f, uint8_t direction, void *alstate,
         AppLayerParserState *pstate, StreamSlice stream_slice)
 {
     SSLState *ssl_state = (SSLState *)alstate;
+    ssl_state->tx_data.updated_tc = true;
+    ssl_state->tx_data.updated_ts = true;
     uint32_t counter = 0;
     ssl_state->f = f;
     const uint8_t *input = StreamSliceGetData(&stream_slice);
index 03fa8437068d88771b0d1c59520d346a1071f104..802cf690ce22c5a6bb51716b1f48d692bae3395e 100644 (file)
@@ -1294,6 +1294,12 @@ static DetectTransaction GetDetectTx(const uint8_t ipproto, const AppProto alpro
         DetectTransaction no_tx = NO_TX;
         return no_tx;
     }
+    const int tx_progress = AppLayerParserGetStateProgress(ipproto, alproto, tx_ptr, flow_flags);
+    bool updated = (flow_flags & STREAM_TOSERVER) ? txd->updated_ts : txd->updated_tc;
+    if (!updated && tx_progress < tx_end_state && ((flow_flags & STREAM_EOF) == 0)) {
+        DetectTransaction no_tx = NO_TX;
+        return no_tx;
+    }
     uint64_t detect_flags =
             (flow_flags & STREAM_TOSERVER) ? txd->detect_flags_ts : txd->detect_flags_tc;
     if (detect_flags & APP_LAYER_TX_INSPECTED_FLAG) {
@@ -1310,7 +1316,6 @@ static DetectTransaction GetDetectTx(const uint8_t ipproto, const AppProto alpro
         return no_tx;
     }
 
-    const int tx_progress = AppLayerParserGetStateProgress(ipproto, alproto, tx_ptr, flow_flags);
     const int dir_int = (flow_flags & STREAM_TOSERVER) ? 0 : 1;
     DetectEngineState *tx_de_state = txd->de_state;
     DetectEngineStateDirection *tx_dir_state = tx_de_state ? &tx_de_state->dir_state[dir_int] : NULL;
index 40b8877067701b2e26c15001dbe53604302bab21..8e37b1b5f2e244c9af3c5e622f734c05a8695f02 100644 (file)
@@ -392,7 +392,7 @@ static TmEcode OutputTxLog(ThreadVars *tv, Packet *p, void *thread_data)
     uint64_t tx_id = AppLayerParserGetTransactionLogId(f->alparser);
     uint64_t max_id = tx_id;
     int logged = 0;
-    int gap = 0;
+    bool gap = false;
     const bool support_files = AppLayerParserSupportsFiles(ipproto, alproto);
     const uint8_t pkt_dir = STREAM_FLAGS_FOR_PACKET(p);
 
@@ -415,15 +415,6 @@ static TmEcode OutputTxLog(ThreadVars *tv, Packet *p, void *thread_data)
         tx_id = ires.tx_id;
         SCLogDebug("STARTING tx_id %" PRIu64 ", tx %p", tx_id, tx);
 
-        const int tx_progress_ts =
-                AppLayerParserGetStateProgress(ipproto, alproto, tx, ts_disrupt_flags);
-        const int tx_progress_tc =
-                AppLayerParserGetStateProgress(ipproto, alproto, tx, tc_disrupt_flags);
-        const bool tx_complete = (tx_progress_ts == complete_ts && tx_progress_tc == complete_tc);
-
-        SCLogDebug("file_thread_data %p filedata_thread_data %p", op_thread_data->file,
-                op_thread_data->filedata);
-
         AppLayerTxData *txd = AppLayerParserGetTxData(ipproto, alproto, tx);
         if (unlikely(txd == NULL)) {
             SCLogDebug("NO TXD");
@@ -433,6 +424,15 @@ static TmEcode OutputTxLog(ThreadVars *tv, Packet *p, void *thread_data)
             goto next_tx;
         }
 
+        const int tx_progress_ts =
+                AppLayerParserGetStateProgress(ipproto, alproto, tx, ts_disrupt_flags);
+        const int tx_progress_tc =
+                AppLayerParserGetStateProgress(ipproto, alproto, tx, tc_disrupt_flags);
+        const bool tx_complete = (tx_progress_ts == complete_ts && tx_progress_tc == complete_tc);
+
+        SCLogDebug("file_thread_data %p filedata_thread_data %p", op_thread_data->file,
+                op_thread_data->filedata);
+
         if (file_logging_active) {
             if (AppLayerParserIsFileTx(txd)) { // need to process each tx that might be a file tx,
                                                // even if there are not files (yet)
@@ -467,6 +467,11 @@ static TmEcode OutputTxLog(ThreadVars *tv, Packet *p, void *thread_data)
             }
         }
         SCLogDebug("logger: expect %08x, have %08x", logger_expectation, txd->logged.flags);
+        if (!txd->updated_tc && !txd->updated_ts && !(tx_progress_ts == complete_ts) &&
+                !(tx_progress_tc == complete_tc) && !ts_eof && !tc_eof) {
+            gap = true;
+            goto next_tx;
+        }
 
         if (list[ALPROTO_UNKNOWN] != 0) {
             OutputTxLogList0(tv, op_thread_data, p, f, tx, tx_id);
@@ -517,7 +522,7 @@ static TmEcode OutputTxLog(ThreadVars *tv, Packet *p, void *thread_data)
             max_id = tx_id;
             SCLogDebug("max_id %" PRIu64, max_id);
         } else {
-            gap = 1;
+            gap = true;
         }
 next_tx:
         if (!ires.has_next)