From d0fb65af3560cd2eeae812172614b3a004edc045 Mon Sep 17 00:00:00 2001 From: Jeff Lucovsky Date: Thu, 16 Apr 2026 09:50:39 -0400 Subject: [PATCH 1/4] ftp: raise too_many_transactions event on overflow Other parsers (SMB, POP3, ENIP) already raise a too_many_transactions event when a flow exceeds its per-flow transaction limit. FTP had the limit wired up through app-layer.protocols.ftp.max-tx but just dropped the excess transaction on the floor with a "FTP does not set events yet..." TODO, so no event was raised when the limit was reached. Add the FtpEventTooManyTransactions variant, a stock rule at sid 2232002, and raise the event from FTPTransactionCreate when the live-tx count goes past ftp_config_maxtx. Issue: 8489 --- rust/src/ftp/event.rs | 2 ++ src/app-layer-ftp.c | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/rust/src/ftp/event.rs b/rust/src/ftp/event.rs index d7ee6969d64c..ce25fd2ce12c 100644 --- a/rust/src/ftp/event.rs +++ b/rust/src/ftp/event.rs @@ -25,6 +25,8 @@ pub enum FtpEvent { FtpEventRequestCommandTooLong, #[name("response_command_too_long")] FtpEventResponseCommandTooLong, + #[name("too_many_transactions")] + FtpEventTooManyTransactions, } /// Wrapper around the Rust generic function for get_event_info. diff --git a/src/app-layer-ftp.c b/src/app-layer-ftp.c index d1eb32e62b22..de3c512c13de 100644 --- a/src/app-layer-ftp.c +++ b/src/app-layer-ftp.c @@ -223,7 +223,10 @@ static FTPTransaction *FTPTransactionCreate(FtpState *state) SCEnter(); FTPTransaction *firsttx = TAILQ_FIRST(&state->tx_list); if (firsttx && state->tx_cnt - firsttx->tx_id > ftp_config_maxtx) { - // FTP does not set events yet... + FTPTransaction *event_tx = state->curr_tx ? state->curr_tx : firsttx; + event_tx->done = true; + event_tx->tx_data.updated_ts = true; + SCAppLayerDecoderEventsSetEventRaw(&event_tx->tx_data.events, FtpEventTooManyTransactions); return NULL; } FTPTransaction *tx = FTPCalloc(1, sizeof(*tx)); From abbaf0ec9dfbc807a352e8ceeb4fd6870a3647ae Mon Sep 17 00:00:00 2001 From: Jeff Lucovsky Date: Tue, 21 Apr 2026 10:45:18 -0400 Subject: [PATCH 2/4] ftp: don't halt the flow when raising too_many_transactions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The first version of the event set done=true on state->curr_tx and returned NULL when the limit hit. curr_tx is usually the active command just received — marking it done mid-request throws off response matching, and subsequent commands in the flow stop getting logged. SMB behavior is mirrored here: Walk the tx list, find the oldest tx that isn't done, mark it done and tag it with the event, then fall through and create the new tx so the flow parsing continues. One stale tx gets reaped per overflow so memory stays bounded. Issue: 8489 --- src/app-layer-ftp.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/app-layer-ftp.c b/src/app-layer-ftp.c index de3c512c13de..6b90683eee92 100644 --- a/src/app-layer-ftp.c +++ b/src/app-layer-ftp.c @@ -223,11 +223,17 @@ static FTPTransaction *FTPTransactionCreate(FtpState *state) SCEnter(); FTPTransaction *firsttx = TAILQ_FIRST(&state->tx_list); if (firsttx && state->tx_cnt - firsttx->tx_id > ftp_config_maxtx) { - FTPTransaction *event_tx = state->curr_tx ? state->curr_tx : firsttx; - event_tx->done = true; - event_tx->tx_data.updated_ts = true; - SCAppLayerDecoderEventsSetEventRaw(&event_tx->tx_data.events, FtpEventTooManyTransactions); - return NULL; + FTPTransaction *tx_old; + TAILQ_FOREACH (tx_old, &state->tx_list, next) { + if (!tx_old->done) { + tx_old->done = true; + tx_old->tx_data.updated_ts = true; + tx_old->tx_data.updated_tc = true; + SCAppLayerDecoderEventsSetEventRaw( + &tx_old->tx_data.events, FtpEventTooManyTransactions); + break; + } + } } FTPTransaction *tx = FTPCalloc(1, sizeof(*tx)); if (tx == NULL) { From 45304b6ae097d6043aceaf8a5d9a9975212013ef Mon Sep 17 00:00:00 2001 From: alinse-pltzr Date: Wed, 15 Apr 2026 21:30:20 +0200 Subject: [PATCH 3/4] conf: add comment for ftp.max-tx Add missing FTP configuration value to suricata.yaml.in showing the default value. Issue: 8489 --- suricata.yaml.in | 1 + 1 file changed, 1 insertion(+) diff --git a/suricata.yaml.in b/suricata.yaml.in index 859c443903de..964245d2fba4 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -1000,6 +1000,7 @@ app-layer: ftp: enabled: yes # memcap: 64 MiB + # max-tx: 1024 websocket: #enabled: yes # Maximum used payload size, the rest is skipped From 4a25805afeb0dc37309a6aadecf4e85b64cfa4a4 Mon Sep 17 00:00:00 2001 From: alinse-pltzr Date: Wed, 15 Apr 2026 21:28:35 +0200 Subject: [PATCH 4/4] ftp: add rule for too many transactions Issue: 8489 --- rules/ftp-events.rules | 1 + 1 file changed, 1 insertion(+) diff --git a/rules/ftp-events.rules b/rules/ftp-events.rules index d32c93f32759..501fe1016555 100644 --- a/rules/ftp-events.rules +++ b/rules/ftp-events.rules @@ -4,3 +4,4 @@ alert ftp any any -> any any (msg:"SURICATA FTP Request command too long"; flow:to_server; app-layer-event:ftp.request_command_too_long; classtype:protocol-command-decode; sid:2232000; rev:1;) alert ftp any any -> any any (msg:"SURICATA FTP Response command too long"; flow:to_client; app-layer-event:ftp.response_command_too_long; classtype:protocol-command-decode; sid:2232001; rev:1;) +alert ftp any any -> any any (msg:"SURICATA FTP too many transactions"; app-layer-event:ftp.too_many_transactions; classtype:protocol-command-decode; sid:2232002; rev:1;)