"QuicState",
"QuicTransaction",
"FtpEvent",
+ "FtpRequestCommand",
+ "FtpStateValues",
+ "FtpDataStateValues",
"SCSigTableElmt",
"SCTransformTableElmt",
"DataRepType",
--- /dev/null
+/* 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,
+}
--- /dev/null
+/* 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);
+ }
+}
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.
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;
-/* 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
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;
* \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();
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)
} 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);
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
}
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;
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);
}
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;
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);
}
}
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);
}
-/* 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;
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;
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? */
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;
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;
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");
+ }
}
}
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 {