From 7b31f41878b557f121d471fe56f3ca0e94ec4a36 Mon Sep 17 00:00:00 2001 From: Jason Ish Date: Fri, 3 Jul 2026 13:06:04 -0600 Subject: [PATCH] smtp: complete transactions by progress state Add directionality to completion states, and replace tx->done by checking for both directions being complete. This means that the transaction is now not complete until the server responds to the clients of data marker, previously the tx was completed when the client send end of data without waiting for the server response. This keeps smtp:response_complete from being exposed before the server response is parsed. Ticket: #8393 --- src/app-layer-smtp.c | 44 +++++++++++++++++++++++++++++++++++++------- src/app-layer-smtp.h | 2 -- 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/src/app-layer-smtp.c b/src/app-layer-smtp.c index e917d93970..ec5f53d537 100644 --- a/src/app-layer-smtp.c +++ b/src/app-layer-smtp.c @@ -232,6 +232,12 @@ static inline void SMTPSetProgressTC(SMTPTransaction *tx, uint8_t progress) } } +static bool SMTPTransactionIsComplete(const SMTPTransaction *tx) +{ + return tx && tx->progress_ts == SMTP_REQUEST_COMPLETE && + tx->progress_tc == SMTP_RESPONSE_COMPLETE; +} + typedef struct SMTPThreadCtx_ { MpmThreadCtx *smtp_mpm_thread_ctx; PrefilterRuleStore *pmq; @@ -763,8 +769,28 @@ static void SetMimeEvents(SMTPState *state, uint32_t events) static inline void SMTPTransactionComplete(SMTPState *state) { DEBUG_VALIDATE_BUG_ON(state->curr_tx == NULL); - if (state->curr_tx) - state->curr_tx->done = true; + if (state->curr_tx) { + SMTPSetProgressTS(state->curr_tx, SMTP_REQUEST_COMPLETE); + SMTPSetProgressTC(state->curr_tx, SMTP_RESPONSE_COMPLETE); + } +} + +static inline void SMTPTransactionCompleteTS(SMTPState *state) +{ + DEBUG_VALIDATE_BUG_ON(state->curr_tx == NULL); + if (state->curr_tx) { + SMTPSetProgressTS(state->curr_tx, SMTP_REQUEST_COMPLETE); + SCLogDebug("marked tx as ts complete"); + } +} + +static inline void SMTPTransactionCompleteTC(SMTPState *state) +{ + DEBUG_VALIDATE_BUG_ON(state->curr_tx == NULL); + if (state->curr_tx) { + SMTPSetProgressTC(state->curr_tx, SMTP_RESPONSE_COMPLETE); + SCLogDebug("marked tx as tc complete"); + } } /** @@ -801,8 +827,7 @@ static int SMTPProcessCommandDATA( FileFlowToFlags(f, STREAM_TOSERVER)); } } - SMTPTransactionComplete(state); - SCLogDebug("marked tx as done"); + SMTPTransactionCompleteTS(state); } else if (smtp_config.raw_extraction) { // message not over, store the line. This is a substitution of // ProcessDataChunk @@ -1007,6 +1032,10 @@ static int SMTPProcessReply( } } else if (IsReplyToCommand(state, SMTP_COMMAND_BDAT)) { SMTPSetProgressTC(state->curr_tx, SMTP_RESPONSE_DATA); + } else if (IsReplyToCommand(state, SMTP_COMMAND_DATA_MODE)) { + if (!(state->parser_state & SMTP_PARSER_STATE_PARSING_MULTILINE_REPLY)) { + SMTPTransactionCompleteTC(state); + } } else if (IsReplyToCommand(state, SMTP_COMMAND_RSET)) { if (reply_code == SMTP_REPLY_250 && state->curr_tx && !(state->parser_state & SMTP_PARSER_STATE_PARSING_MULTILINE_REPLY)) { @@ -1208,7 +1237,8 @@ static int SMTPProcessRequest( if (line->len == 0 && line->delim_len == 0) { return 0; } - if (state->curr_tx == NULL || (state->curr_tx->done && !NoNewTx(state, line))) { + if (state->curr_tx == NULL || + (SMTPTransactionIsComplete(state->curr_tx) && !NoNewTx(state, line))) { tx = SMTPTransactionCreate(state); if (tx == NULL) return -1; @@ -1861,9 +1891,9 @@ static int SMTPStateGetAlstateProgress(void *vtx, uint8_t direction) { SMTPTransaction *tx = vtx; if (direction & STREAM_TOSERVER) { - return tx->done ? SMTP_REQUEST_COMPLETE : tx->progress_ts; + return tx->progress_ts; } - return tx->done ? SMTP_RESPONSE_COMPLETE : tx->progress_tc; + return tx->progress_tc; } static AppLayerGetFileState SMTPGetTxFiles(void *txv, uint8_t direction) diff --git a/src/app-layer-smtp.h b/src/app-layer-smtp.h index cde6671246..041a6cde16 100644 --- a/src/app-layer-smtp.h +++ b/src/app-layer-smtp.h @@ -88,8 +88,6 @@ typedef struct SMTPTransaction_ { AppLayerTxData tx_data; - /** the tx is complete and can be logged and cleaned */ - bool done; /** to-server firewall progress state. */ uint8_t progress_ts; /** to-client firewall progress state. */