]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
ftp: convert enumerations to Rust
authorJeff Lucovsky <jlucovsky@oisf.net>
Sun, 2 Feb 2025 14:12:59 +0000 (09:12 -0500)
committerVictor Julien <victor@inliniac.net>
Tue, 25 Feb 2025 14:49:29 +0000 (15:49 +0100)
As part of the effort to convert the FTP/FTPDATA parser to rust, move
the enums from C to rust.

Issue: 4082

rust/cbindgen.toml
rust/src/ftp/constant.rs [new file with mode: 0644]
rust/src/ftp/ftp.rs [new file with mode: 0644]
rust/src/ftp/mod.rs
src/app-layer-ftp.c
src/app-layer-ftp.h
src/detect-ftp-command.c
src/output-json-ftp.c

index e2730789c808e0865b892a888331828c5edd09b5..7e20a8411a400cfbee5169ff2f58792416ddbc3a 100644 (file)
@@ -82,6 +82,9 @@ include = [
     "QuicState",
     "QuicTransaction",
     "FtpEvent",
+    "FtpRequestCommand",
+    "FtpStateValues",
+    "FtpDataStateValues",
     "SCSigTableElmt",
     "SCTransformTableElmt",
     "DataRepType",
diff --git a/rust/src/ftp/constant.rs b/rust/src/ftp/constant.rs
new file mode 100644 (file)
index 0000000..2b3e8dd
--- /dev/null
@@ -0,0 +1,89 @@
+/* Copyright (C) 2025 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+// FTP state progress values
+#[repr(u8)]
+#[allow(non_camel_case_types)]
+pub enum FtpStateValues {
+    FTP_STATE_NONE,
+    FTP_STATE_IN_PROGRESS,
+    FTP_STATE_PORT_DONE,
+    FTP_STATE_FINISHED,
+}
+// FTP Data progress values
+#[repr(u8)]
+#[allow(non_camel_case_types)]
+pub enum FtpDataStateValues {
+    FTPDATA_STATE_IN_PROGRESS = 1,
+    FTPDATA_STATE_FINISHED = 2,
+}
+
+// FTP request command values
+#[repr(u8)]
+#[allow(non_camel_case_types)]
+#[derive(Clone, Copy)]
+pub enum FtpRequestCommand {
+    FTP_COMMAND_UNKNOWN,
+    FTP_COMMAND_ABOR,
+    FTP_COMMAND_ACCT,
+    FTP_COMMAND_ALLO,
+    FTP_COMMAND_APPE,
+    FTP_COMMAND_AUTH_TLS,
+    FTP_COMMAND_CDUP,
+    FTP_COMMAND_CHMOD,
+    FTP_COMMAND_CWD,
+    FTP_COMMAND_DELE,
+    FTP_COMMAND_EPSV,
+    FTP_COMMAND_HELP,
+    FTP_COMMAND_IDLE,
+    FTP_COMMAND_LIST,
+    FTP_COMMAND_MAIL,
+    FTP_COMMAND_MDTM,
+    FTP_COMMAND_MKD,
+    FTP_COMMAND_MLFL,
+    FTP_COMMAND_MODE,
+    FTP_COMMAND_MRCP,
+    FTP_COMMAND_MRSQ,
+    FTP_COMMAND_MSAM,
+    FTP_COMMAND_MSND,
+    FTP_COMMAND_MSOM,
+    FTP_COMMAND_NLST,
+    FTP_COMMAND_NOOP,
+    FTP_COMMAND_PASS,
+    FTP_COMMAND_PASV,
+    FTP_COMMAND_PORT,
+    FTP_COMMAND_PWD,
+    FTP_COMMAND_QUIT,
+    FTP_COMMAND_REIN,
+    FTP_COMMAND_REST,
+    FTP_COMMAND_RETR,
+    FTP_COMMAND_RMD,
+    FTP_COMMAND_RNFR,
+    FTP_COMMAND_RNTO,
+    FTP_COMMAND_SITE,
+    FTP_COMMAND_SIZE,
+    FTP_COMMAND_SMNT,
+    FTP_COMMAND_STAT,
+    FTP_COMMAND_STOR,
+    FTP_COMMAND_STOU,
+    FTP_COMMAND_STRU,
+    FTP_COMMAND_SYST,
+    FTP_COMMAND_TYPE,
+    FTP_COMMAND_UMASK,
+    FTP_COMMAND_USER,
+    FTP_COMMAND_EPRT,
+}
diff --git a/rust/src/ftp/ftp.rs b/rust/src/ftp/ftp.rs
new file mode 100644 (file)
index 0000000..2cc4566
--- /dev/null
@@ -0,0 +1,201 @@
+/* Copyright (C) 2025 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+use std;
+use std::ffi::CString;
+use std::os::raw::{c_char, c_int, c_void};
+
+use crate::ftp::constant::*;
+use lazy_static::lazy_static;
+
+/// cbindgen:ignore
+#[repr(C)]
+pub struct FtpCommand {
+    command_name: CString,
+    command: FtpRequestCommand,
+    command_length: u8,
+}
+
+impl FtpCommand {
+    fn new(command_name: &str, command: FtpRequestCommand) -> FtpCommand {
+        let cstring = CString::new(command_name).unwrap();
+        let length = cstring.as_bytes().len();
+        FtpCommand {
+            command_name: cstring,
+            command,
+            command_length: length as u8,
+        }
+    }
+}
+
+lazy_static! {
+    static ref FTP_COMMANDS: Vec<FtpCommand> = vec![
+        FtpCommand::new("PORT", FtpRequestCommand::FTP_COMMAND_PORT),
+        FtpCommand::new("EPRT", FtpRequestCommand::FTP_COMMAND_EPRT),
+        FtpCommand::new("AUTH_TLS", FtpRequestCommand::FTP_COMMAND_AUTH_TLS),
+        FtpCommand::new("PASV", FtpRequestCommand::FTP_COMMAND_PASV),
+        FtpCommand::new("EPSV", FtpRequestCommand::FTP_COMMAND_EPSV),
+        FtpCommand::new("RETR", FtpRequestCommand::FTP_COMMAND_RETR),
+        FtpCommand::new("STOR", FtpRequestCommand::FTP_COMMAND_STOR),
+        FtpCommand::new("ABOR", FtpRequestCommand::FTP_COMMAND_ABOR),
+        FtpCommand::new("ACCT", FtpRequestCommand::FTP_COMMAND_ACCT),
+        FtpCommand::new("ALLO", FtpRequestCommand::FTP_COMMAND_ALLO),
+        FtpCommand::new("APPE", FtpRequestCommand::FTP_COMMAND_APPE),
+        FtpCommand::new("CDUP", FtpRequestCommand::FTP_COMMAND_CDUP),
+        FtpCommand::new("CHMOD", FtpRequestCommand::FTP_COMMAND_CHMOD),
+        FtpCommand::new("CWD", FtpRequestCommand::FTP_COMMAND_CWD),
+        FtpCommand::new("DELE", FtpRequestCommand::FTP_COMMAND_DELE),
+        FtpCommand::new("HELP", FtpRequestCommand::FTP_COMMAND_HELP),
+        FtpCommand::new("IDLE", FtpRequestCommand::FTP_COMMAND_IDLE),
+        FtpCommand::new("LIST", FtpRequestCommand::FTP_COMMAND_LIST),
+        FtpCommand::new("MAIL", FtpRequestCommand::FTP_COMMAND_MAIL),
+        FtpCommand::new("MDTM", FtpRequestCommand::FTP_COMMAND_MDTM),
+        FtpCommand::new("MKD", FtpRequestCommand::FTP_COMMAND_MKD),
+        FtpCommand::new("MLFL", FtpRequestCommand::FTP_COMMAND_MLFL),
+        FtpCommand::new("MODE", FtpRequestCommand::FTP_COMMAND_MODE),
+        FtpCommand::new("MRCP", FtpRequestCommand::FTP_COMMAND_MRCP),
+        FtpCommand::new("MRSQ", FtpRequestCommand::FTP_COMMAND_MRSQ),
+        FtpCommand::new("MSAM", FtpRequestCommand::FTP_COMMAND_MSAM),
+        FtpCommand::new("MSND", FtpRequestCommand::FTP_COMMAND_MSND),
+        FtpCommand::new("MSOM", FtpRequestCommand::FTP_COMMAND_MSOM),
+        FtpCommand::new("NLST", FtpRequestCommand::FTP_COMMAND_NLST),
+        FtpCommand::new("NOOP", FtpRequestCommand::FTP_COMMAND_NOOP),
+        FtpCommand::new("PASS", FtpRequestCommand::FTP_COMMAND_PASS),
+        FtpCommand::new("PWD", FtpRequestCommand::FTP_COMMAND_PWD),
+        FtpCommand::new("QUIT", FtpRequestCommand::FTP_COMMAND_QUIT),
+        FtpCommand::new("REIN", FtpRequestCommand::FTP_COMMAND_REIN),
+        FtpCommand::new("REST", FtpRequestCommand::FTP_COMMAND_REST),
+        FtpCommand::new("RMD", FtpRequestCommand::FTP_COMMAND_RMD),
+        FtpCommand::new("RNFR", FtpRequestCommand::FTP_COMMAND_RNFR),
+        FtpCommand::new("RNTO", FtpRequestCommand::FTP_COMMAND_RNTO),
+        FtpCommand::new("SITE", FtpRequestCommand::FTP_COMMAND_SITE),
+        FtpCommand::new("SIZE", FtpRequestCommand::FTP_COMMAND_SIZE),
+        FtpCommand::new("SMNT", FtpRequestCommand::FTP_COMMAND_SMNT),
+        FtpCommand::new("STAT", FtpRequestCommand::FTP_COMMAND_STAT),
+        FtpCommand::new("STOU", FtpRequestCommand::FTP_COMMAND_STOU),
+        FtpCommand::new("STRU", FtpRequestCommand::FTP_COMMAND_STRU),
+        FtpCommand::new("SYST", FtpRequestCommand::FTP_COMMAND_SYST),
+        FtpCommand::new("TYPE", FtpRequestCommand::FTP_COMMAND_TYPE),
+        FtpCommand::new("UMASK", FtpRequestCommand::FTP_COMMAND_UMASK),
+        FtpCommand::new("USER", FtpRequestCommand::FTP_COMMAND_USER),
+        FtpCommand::new("UNKNOWN", FtpRequestCommand::FTP_COMMAND_UNKNOWN),
+    ];
+}
+
+/// cbindgen:ignore
+extern "C" {
+    pub fn MpmAddPatternCI(
+        ctx: *const c_void, pat: *const libc::c_char, pat_len: c_int, _offset: c_int,
+        _depth: c_int, id: c_int, rule_id: c_int, _flags: c_int,
+    ) -> c_void;
+}
+
+#[allow(non_snake_case)]
+#[no_mangle]
+pub unsafe extern "C" fn SCGetFtpCommandInfo(
+    index: usize, name_ptr: *mut *const c_char, code_ptr: *mut u8, len_ptr: *mut u8,
+) -> bool {
+    if index <= FTP_COMMANDS.len() {
+        unsafe {
+            if !name_ptr.is_null() {
+                *name_ptr = FTP_COMMANDS[index].command_name.as_ptr();
+            }
+            if !code_ptr.is_null() {
+                *code_ptr = FTP_COMMANDS[index].command as u8;
+            }
+            if !len_ptr.is_null() {
+                *len_ptr = FTP_COMMANDS[index].command_length;
+            }
+        }
+        true
+    } else {
+        false
+    }
+}
+
+#[allow(non_snake_case)]
+#[no_mangle]
+pub unsafe extern "C" fn SCFTPSetMpmState(ctx: *const c_void) {
+    for index in 0..FTP_COMMANDS.len() {
+        let name_ptr = FTP_COMMANDS[index].command_name.as_ptr();
+        let len = FTP_COMMANDS[index].command_length;
+        if len > 0 {
+            MpmAddPatternCI(
+                ctx,
+                name_ptr,
+                len as c_int,
+                0,
+                0,
+                index as c_int,
+                index as c_int,
+                0,
+            );
+        }
+    }
+}
+
+#[repr(C)]
+#[allow(dead_code)]
+pub struct FtpTransferCmd {
+    // Must be first -- required by app-layer expectation logic
+    data_free: unsafe extern "C" fn(*mut c_void),
+    pub flow_id: u64,
+    pub file_name: *mut u8,
+    pub file_len: u16,
+    pub direction: u8,
+    pub cmd: u8,
+}
+
+impl Default for FtpTransferCmd {
+    fn default() -> Self {
+        FtpTransferCmd {
+            flow_id: 0,
+            file_name: std::ptr::null_mut(),
+            file_len: 0,
+            direction: 0,
+            cmd: FtpStateValues::FTP_STATE_NONE as u8,
+            data_free: default_free_fn,
+        }
+    }
+}
+
+unsafe extern "C" fn default_free_fn(_ptr: *mut c_void) {}
+impl FtpTransferCmd {
+    pub fn new() -> Self {
+        FtpTransferCmd {
+            ..Default::default()
+        }
+    }
+}
+
+/// Returns *mut FtpTransferCmd
+#[no_mangle]
+pub unsafe extern "C" fn SCFTPTransferCmdNew() -> *mut FtpTransferCmd {
+    SCLogDebug!("allocating ftp transfer cmd");
+    let cmd = FtpTransferCmd::new();
+    Box::into_raw(Box::new(cmd))
+}
+
+/// Params:
+/// - transfer command: *mut FTPTransferCmd as void pointer
+#[no_mangle]
+pub unsafe extern "C" fn SCFTPTransferCmdFree(cmd: *mut FtpTransferCmd) {
+    SCLogDebug!("freeing ftp transfer cmd");
+    if !cmd.is_null() {
+        let _transfer_cmd = Box::from_raw(cmd);
+    }
+}
index a206aa2f350ceae988cdd6d5dc302104eab06541..b7b74732c9e71964ced974c667bd2194aebcdda6 100644 (file)
@@ -26,7 +26,9 @@ use std;
 use std::str;
 use std::str::FromStr;
 
+pub mod constant;
 pub mod event;
+pub mod ftp;
 
 // We transform an integer string into a i64, ignoring surrounding whitespaces
 // We look for a digit suite, and try to convert it.
@@ -111,7 +113,7 @@ pub unsafe extern "C" fn rs_ftp_pasv_response(input: *const u8, len: u32) -> u16
     if input.is_null() {
         return 0;
     }
-    let buf =  build_slice!(input, len as usize);
+    let buf = build_slice!(input, len as usize);
     match ftp_pasv_response(buf) {
         Ok((_, dport)) => {
             return dport;
index a6a1f632fcb4c56bf3cc71c5283075f5054dd02b..682ee2b42a2ce01f197875fca6c1f915f048c289 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2007-2024 Open Information Security Foundation
+/* Copyright (C) 2007-2025 Open Information Security Foundation
  *
  * You can copy, redistribute or modify this Program under the terms of
  * the GNU General Public License version 2 as published by the Free
@@ -47,63 +47,6 @@ typedef struct FTPThreadCtx_ {
 
 static MpmCtx *ftp_mpm_ctx = NULL;
 
-// clang-format off
-const FtpCommand FtpCommands[FTP_COMMAND_MAX + 1] = {
-    /* Parsed and handled */
-    { "PORT",   FTP_COMMAND_PORT,   4 },
-    { "EPRT",   FTP_COMMAND_EPRT,   4 },
-    { "AUTH TLS",   FTP_COMMAND_AUTH_TLS,   8 },
-    { "PASV",   FTP_COMMAND_PASV,   4 },
-    { "RETR",   FTP_COMMAND_RETR,   4 },
-    { "EPSV",   FTP_COMMAND_EPSV,   4 },
-    { "STOR",   FTP_COMMAND_STOR,   4 },
-
-    /* Parsed, but not handled */
-    { "ABOR",   FTP_COMMAND_ABOR,   4 },
-    { "ACCT",   FTP_COMMAND_ACCT,   4 },
-    { "ALLO",   FTP_COMMAND_ALLO,   4 },
-    { "APPE",   FTP_COMMAND_APPE,   4 },
-    { "CDUP",   FTP_COMMAND_CDUP,   4 },
-    { "CHMOD",  FTP_COMMAND_CHMOD,  5 },
-    { "CWD",    FTP_COMMAND_CWD,    3 },
-    { "DELE",   FTP_COMMAND_DELE,   4 },
-    { "HELP",   FTP_COMMAND_HELP,   4 },
-    { "IDLE",   FTP_COMMAND_IDLE,   4 },
-    { "LIST",   FTP_COMMAND_LIST,   4 },
-    { "MAIL",   FTP_COMMAND_MAIL,   4 },
-    { "MDTM",   FTP_COMMAND_MDTM,   4 },
-    { "MKD",    FTP_COMMAND_MKD,    3 },
-    { "MLFL",   FTP_COMMAND_MLFL,   4 },
-    { "MODE",   FTP_COMMAND_MODE,   4 },
-    { "MRCP",   FTP_COMMAND_MRCP,   4 },
-    { "MRSQ",   FTP_COMMAND_MRSQ,   4 },
-    { "MSAM",   FTP_COMMAND_MSAM,   4 },
-    { "MSND",   FTP_COMMAND_MSND,   4 },
-    { "MSOM",   FTP_COMMAND_MSOM,   4 },
-    { "NLST",   FTP_COMMAND_NLST,   4 },
-    { "NOOP",   FTP_COMMAND_NOOP,   4 },
-    { "PASS",   FTP_COMMAND_PASS,   4 },
-    { "PWD",    FTP_COMMAND_PWD,    3 },
-    { "QUIT",   FTP_COMMAND_QUIT,   4 },
-    { "REIN",   FTP_COMMAND_REIN,   4 },
-    { "REST",   FTP_COMMAND_REST,   4 },
-    { "RMD",    FTP_COMMAND_RMD,    3 },
-    { "RNFR",   FTP_COMMAND_RNFR,   4 },
-    { "RNTO",   FTP_COMMAND_RNTO,   4 },
-    { "SITE",   FTP_COMMAND_SITE,   4 },
-    { "SIZE",   FTP_COMMAND_SIZE,   4 },
-    { "SMNT",   FTP_COMMAND_SMNT,   4 },
-    { "STAT",   FTP_COMMAND_STAT,   4 },
-    { "STOU",   FTP_COMMAND_STOU,   4 },
-    { "STRU",   FTP_COMMAND_STRU,   4 },
-    { "SYST",   FTP_COMMAND_SYST,   4 },
-    { "TYPE",   FTP_COMMAND_TYPE,   4 },
-    { "UMASK",  FTP_COMMAND_UMASK,  5 },
-    { "USER",   FTP_COMMAND_USER,   4 },
-    { NULL,     FTP_COMMAND_UNKNOWN,    0 }
-};
-// clang-format on
-
 uint64_t ftp_config_memcap = 0;
 uint32_t ftp_config_maxtx = 1024;
 uint32_t ftp_max_line_len = 4096;
@@ -421,7 +364,7 @@ static AppLayerResult FTPGetLineForDirection(
  * \retval 1 when the command is parsed, 0 otherwise
  */
 static int FTPParseRequestCommand(
-        FTPThreadCtx *td, FtpLineState *line, const FtpCommand **cmd_descriptor)
+        FTPThreadCtx *td, FtpLineState *line, FtpCommandInfo *cmd_descriptor)
 {
     SCEnter();
 
@@ -431,34 +374,40 @@ static int FTPParseRequestCommand(
     int mpm_cnt = mpm_table[FTP_MPM].Search(
             ftp_mpm_ctx, td->ftp_mpm_thread_ctx, td->pmq, line->buf, line->len);
     if (mpm_cnt) {
-        *cmd_descriptor = &FtpCommands[td->pmq->rule_id_array[0]];
-        SCReturnInt(1);
+        uint8_t command_code;
+        if (SCGetFtpCommandInfo(td->pmq->rule_id_array[0], NULL, &command_code, NULL)) {
+            cmd_descriptor->command_code = command_code;
+            /* FTP command indices are expressed in Rust as a u8 */
+            cmd_descriptor->command_index = (uint8_t)td->pmq->rule_id_array[0];
+            SCReturnInt(1);
+        } else {
+            /* Where is out command? */
+            DEBUG_VALIDATE_BUG_ON(1);
+        }
+#ifdef DEBUG
+        if (SCLogDebugEnabled()) {
+            const char *command_name = NULL;
+            (void)SCGetFtpCommandInfo(td->pmq->rule_id_array[0], &command_name, NULL, NULL);
+            SCLogDebug("matching FTP command is %s [code: %d, index %d]", command_name,
+                    command_code, td->pmq->rule_id_array[0]);
+        }
+#endif
     }
 
-    *cmd_descriptor = NULL;
+    cmd_descriptor->command_code = FTP_COMMAND_UNKNOWN;
     SCReturnInt(0);
 }
 
-struct FtpTransferCmd {
-    /** Need to look like a ExpectationData so DFree must
-     *  be first field . */
-    void (*DFree)(void *);
-    uint64_t flow_id;
-    uint8_t *file_name;
-    uint16_t file_len;
-    uint8_t direction; /**< direction in which the data will flow */
-    FtpRequestCommand cmd;
-};
-
 static void FtpTransferCmdFree(void *data)
 {
-    struct FtpTransferCmd *cmd = (struct FtpTransferCmd *) data;
+    FtpTransferCmd *cmd = (FtpTransferCmd *)data;
     if (cmd == NULL)
         return;
     if (cmd->file_name) {
-        FTPFree(cmd->file_name, cmd->file_len + 1);
+        FTPFree((void *)cmd->file_name, cmd->file_len + 1);
     }
-    FTPFree(cmd, sizeof(struct FtpTransferCmd));
+    SCFTPTransferCmdFree(cmd);
+    FTPDecrMemuse((uint64_t)sizeof(FtpTransferCmd));
 }
 
 static uint32_t CopyCommandLine(uint8_t **dest, FtpLineState *line)
@@ -523,14 +472,14 @@ static AppLayerResult FTPParseRequest(Flow *f, void *ftp_state, AppLayerParserSt
         } else if (res.status == -1) {
             break;
         }
-        const FtpCommand *cmd_descriptor;
 
+        FtpCommandInfo cmd_descriptor;
         if (!FTPParseRequestCommand(thread_data, &line, &cmd_descriptor)) {
             state->command = FTP_COMMAND_UNKNOWN;
             continue;
         }
 
-        state->command = cmd_descriptor->command;
+        state->command = cmd_descriptor.command_code;
         FTPTransaction *tx = FTPTransactionCreate(state);
         if (unlikely(tx == NULL))
             SCReturnStruct(APP_LAYER_ERROR);
@@ -588,10 +537,12 @@ static AppLayerResult FTPParseRequest(Flow *f, void *ftp_state, AppLayerParserSt
                 if (state->dyn_port == 0 || line.len < 6) {
                     SCReturnStruct(APP_LAYER_ERROR);
                 }
-                struct FtpTransferCmd *data = FTPCalloc(1, sizeof(struct FtpTransferCmd));
+                FtpTransferCmd *data = SCFTPTransferCmdNew();
                 if (data == NULL)
                     SCReturnStruct(APP_LAYER_ERROR);
-                data->DFree = FtpTransferCmdFree;
+                FTPIncrMemuse((uint64_t)(sizeof *data));
+                data->data_free = FtpTransferCmdFree;
+
                 /*
                  * Min size has been checked in FTPParseRequestCommand
                  * SC_FILENAME_MAX includes the null
@@ -725,9 +676,9 @@ static AppLayerResult FTPParseResponse(Flow *f, void *ftp_state, AppLayerParserS
         }
         lasttx = tx;
         tx->tx_data.updated_tc = true;
-        if (state->command == FTP_COMMAND_UNKNOWN || tx->command_descriptor == NULL) {
+        if (state->command == FTP_COMMAND_UNKNOWN) {
             /* unknown */
-            tx->command_descriptor = &FtpCommands[FTP_COMMAND_MAX - 1];
+            tx->command_descriptor.command_code = FTP_COMMAND_UNKNOWN;
         }
 
         state->curr_tx = tx;
@@ -844,9 +795,17 @@ static void FTPStateFree(void *s)
     FTPTransaction *tx = NULL;
     while ((tx = TAILQ_FIRST(&fstate->tx_list))) {
         TAILQ_REMOVE(&fstate->tx_list, tx, next);
-        SCLogDebug("[%s] state %p id %" PRIu64 ", Freeing %d bytes at %p",
-                tx->command_descriptor->command_name, s, tx->tx_id, tx->request_length,
-                tx->request);
+#ifdef DEBUG
+        if (SCLogDebugEnabled()) {
+            const char *command_name = NULL;
+            (void)SCGetFtpCommandInfo(
+                    tx->command_descriptor.command_index, &command_name, NULL, NULL);
+            SCLogDebug("[%s] state %p id %" PRIu64 ", Freeing %d bytes at %p",
+                    command_name != NULL ? command_name : "n/a", s, tx->tx_id, tx->request_length,
+                    tx->request);
+        }
+#endif
+
         FTPTransactionFree(tx);
     }
 
@@ -957,7 +916,8 @@ static int FTPGetAlstateProgress(void *vtx, uint8_t direction)
     FTPTransaction *tx = vtx;
 
     if (!tx->done) {
-        if (direction == STREAM_TOSERVER && tx->command_descriptor->command == FTP_COMMAND_PORT) {
+        if (direction == STREAM_TOSERVER &&
+                tx->command_descriptor.command_code == FTP_COMMAND_PORT) {
             return FTP_STATE_PORT_DONE;
         }
         return FTP_STATE_IN_PROGRESS;
@@ -1071,8 +1031,8 @@ static AppLayerResult FTPDataParse(Flow *f, FtpDataState *ftpdata_state,
 
     SCLogDebug("FTP-DATA flags %04x dir %d", flags, direction);
     if (input_len && ftpdata_state->files == NULL) {
-        struct FtpTransferCmd *data =
-                (struct FtpTransferCmd *)FlowGetStorageById(f, AppLayerExpectationGetFlowId());
+        FtpTransferCmd *data =
+                (FtpTransferCmd *)FlowGetStorageById(f, AppLayerExpectationGetFlowId());
         if (data == NULL) {
             SCReturnStruct(APP_LAYER_ERROR);
         }
@@ -1283,19 +1243,7 @@ static void FTPSetMpmState(void)
     }
     MpmInitCtx(ftp_mpm_ctx, FTP_MPM);
 
-    uint32_t i = 0;
-    for (i = 0; i < sizeof(FtpCommands)/sizeof(FtpCommand) - 1; i++) {
-        const FtpCommand *cmd = &FtpCommands[i];
-        if (cmd->command_length == 0)
-            continue;
-
-        MpmAddPatternCI(ftp_mpm_ctx,
-                       (uint8_t *)cmd->command_name,
-                       cmd->command_length,
-                       0 /* defunct */, 0 /* defunct */,
-                       i /*  id */, i /* rule id */ , 0 /* no flags */);
-    }
-
+    SCFTPSetMpmState(ftp_mpm_ctx);
     mpm_table[FTP_MPM].Prepare(ftp_mpm_ctx);
 
 }
index e69415d8cf134776b882f6ec1f2f80d8d511860a..99d42b8b97ef75462153ad11714b1061235e7ef3 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2007-2021 Open Information Security Foundation
+/* Copyright (C) 2007-2025 Open Information Security Foundation
  *
  * You can copy, redistribute or modify this Program under the terms of
  * the GNU General Public License version 2 as published by the Free
 
 #include "rust.h"
 
-enum {
-    FTP_STATE_IN_PROGRESS,
-    FTP_STATE_PORT_DONE,
-    FTP_STATE_FINISHED,
-};
-
-typedef enum {
-    FTP_COMMAND_UNKNOWN = 0,
-    FTP_COMMAND_ABOR,
-    FTP_COMMAND_ACCT,
-    FTP_COMMAND_ALLO,
-    FTP_COMMAND_APPE,
-    FTP_COMMAND_AUTH_TLS,
-    FTP_COMMAND_CDUP,
-    FTP_COMMAND_CHMOD,
-    FTP_COMMAND_CWD,
-    FTP_COMMAND_DELE,
-    FTP_COMMAND_EPSV,
-    FTP_COMMAND_HELP,
-    FTP_COMMAND_IDLE,
-    FTP_COMMAND_LIST,
-    FTP_COMMAND_MAIL,
-    FTP_COMMAND_MDTM,
-    FTP_COMMAND_MKD,
-    FTP_COMMAND_MLFL,
-    FTP_COMMAND_MODE,
-    FTP_COMMAND_MRCP,
-    FTP_COMMAND_MRSQ,
-    FTP_COMMAND_MSAM,
-    FTP_COMMAND_MSND,
-    FTP_COMMAND_MSOM,
-    FTP_COMMAND_NLST,
-    FTP_COMMAND_NOOP,
-    FTP_COMMAND_PASS,
-    FTP_COMMAND_PASV,
-    FTP_COMMAND_PORT,
-    FTP_COMMAND_PWD,
-    FTP_COMMAND_QUIT,
-    FTP_COMMAND_REIN,
-    FTP_COMMAND_REST,
-    FTP_COMMAND_RETR,
-    FTP_COMMAND_RMD,
-    FTP_COMMAND_RNFR,
-    FTP_COMMAND_RNTO,
-    FTP_COMMAND_SITE,
-    FTP_COMMAND_SIZE,
-    FTP_COMMAND_SMNT,
-    FTP_COMMAND_STAT,
-    FTP_COMMAND_STOR,
-    FTP_COMMAND_STOU,
-    FTP_COMMAND_STRU,
-    FTP_COMMAND_SYST,
-    FTP_COMMAND_TYPE,
-    FTP_COMMAND_UMASK,
-    FTP_COMMAND_USER,
-    FTP_COMMAND_EPRT,
-
-    /* must be last */
-    FTP_COMMAND_MAX
-    /** \todo more if missing.. */
-} FtpRequestCommand;
-
-typedef struct FtpCommand_ {
-    const char *command_name;
-    FtpRequestCommand command;
-    const uint8_t command_length;
-} FtpCommand;
-extern const FtpCommand FtpCommands[FTP_COMMAND_MAX + 1];
+struct FtpCommand;
 
 typedef uint32_t FtpRequestCommandArgOfs;
 
@@ -115,6 +48,17 @@ typedef struct FTPString_ {
     TAILQ_ENTRY(FTPString_) next;
 } FTPString;
 
+/*
+ * These are the values for the table index value and the FTP command
+ * enum value. These *should* be the same if the enum and command insertion
+ * order remain the same. However, we store each value to protect against
+ * drift between enum and insertion order.
+ */
+typedef struct FtpCommandInfo_ {
+    uint8_t command_index;
+    FtpRequestCommand command_code;
+} FtpCommandInfo;
+
 typedef struct FTPTransaction_  {
     /** id of this tx, starting at 0 */
     uint64_t tx_id;
@@ -127,7 +71,7 @@ typedef struct FTPTransaction_  {
     bool request_truncated;
 
     /* for the command description */
-    const FtpCommand *command_descriptor;
+    FtpCommandInfo command_descriptor;
 
     uint16_t dyn_port; /* dynamic port, if applicable */
     bool done; /* transaction complete? */
@@ -163,11 +107,6 @@ typedef struct FtpState_ {
     AppLayerStateData state_data;
 } FtpState;
 
-enum {
-    FTPDATA_STATE_IN_PROGRESS,
-    FTPDATA_STATE_FINISHED,
-};
-
 /** FTP Data State for app layer parser */
 typedef struct FtpDataState_ {
     uint8_t *input;
index b106ed4ed847fcf93f78428b2149e20d452defaa..f06e46cccb772e93252f01e1d4cc22f81516cae1 100644 (file)
@@ -67,13 +67,16 @@ static InspectionBuffer *GetData(DetectEngineThreadCtx *det_ctx,
     if (buffer->inspect == NULL) {
         FTPTransaction *tx = (FTPTransaction *)txv;
 
-        if (tx->command_descriptor->command_name == NULL ||
-                tx->command_descriptor->command_length == 0)
+        if (tx->command_descriptor.command_code == FTP_COMMAND_UNKNOWN)
             return NULL;
 
-        InspectionBufferSetupAndApplyTransforms(det_ctx, list_id, buffer,
-                (const uint8_t *)tx->command_descriptor->command_name,
-                tx->command_descriptor->command_length, transforms);
+        const char *b = NULL;
+        uint8_t b_len = 0;
+
+        if (SCGetFtpCommandInfo(tx->command_descriptor.command_index, &b, NULL, &b_len)) {
+            InspectionBufferSetupAndApplyTransforms(
+                    det_ctx, list_id, buffer, (const uint8_t *)b, b_len, transforms);
+        }
     }
 
     return buffer;
index 14232bdfe3937ee6eb727e78cd6890be68d09bac..f7fae9983b76599ffb4a256f4efc9049168d9e95 100644 (file)
@@ -58,18 +58,28 @@ bool EveFTPLogCommand(void *vtx, JsonBuilder *jb)
             return false;
         }
     }
+    const char *command_name = NULL;
+    uint8_t command_name_length;
+    if (tx->command_descriptor.command_code != FTP_COMMAND_UNKNOWN) {
+        if (!SCGetFtpCommandInfo(tx->command_descriptor.command_index, &command_name, NULL,
+                    &command_name_length)) {
+            SCLogDebug("Unable to fetch info for FTP command code %d [index %d]",
+                    tx->command_descriptor.command_code, tx->command_descriptor.command_index);
+            return false;
+        }
+    }
     jb_open_object(jb, "ftp");
-    jb_set_string(jb, "command", tx->command_descriptor->command_name);
-    uint32_t min_length = tx->command_descriptor->command_length + 1; /* command + space */
-    if (tx->request_length > min_length) {
-        jb_set_string_from_bytes(jb,
-                "command_data",
-                (const uint8_t *)tx->request + min_length,
-                tx->request_length - min_length - 1);
-        if (tx->request_truncated) {
-            JB_SET_TRUE(jb, "command_truncated");
-        } else {
-            JB_SET_FALSE(jb, "command_truncated");
+    if (command_name) {
+        jb_set_string(jb, "command", command_name);
+        uint32_t min_length = command_name_length + 1; /* command + space */
+        if (tx->request_length > min_length) {
+            jb_set_string_from_bytes(jb, "command_data", (const uint8_t *)tx->request + min_length,
+                    tx->request_length - min_length - 1);
+            if (tx->request_truncated) {
+                JB_SET_TRUE(jb, "command_truncated");
+            } else {
+                JB_SET_FALSE(jb, "command_truncated");
+            }
         }
     }
 
@@ -131,8 +141,8 @@ bool EveFTPLogCommand(void *vtx, JsonBuilder *jb)
         jb_set_uint(jb, "dynamic_port", tx->dyn_port);
     }
 
-    if (tx->command_descriptor->command == FTP_COMMAND_PORT ||
-        tx->command_descriptor->command == FTP_COMMAND_EPRT) {
+    if (tx->command_descriptor.command_code == FTP_COMMAND_PORT ||
+            tx->command_descriptor.command_code == FTP_COMMAND_EPRT) {
         if (tx->active) {
             JB_SET_STRING(jb, "mode", "active");
         } else {