ftp: convert enumerations to Rust

As part of the effort to convert the FTP/FTPDATA parser to rust, move
the enums from C to rust.

Issue: 4082
pull/12675/head
Jeff Lucovsky 4 weeks ago committed by Victor Julien
parent 8c3bd3e8a0
commit f0410c93d0

@ -82,6 +82,9 @@ include = [
"QuicState",
"QuicTransaction",
"FtpEvent",
"FtpRequestCommand",
"FtpStateValues",
"FtpDataStateValues",
"SCSigTableElmt",
"SCTransformTableElmt",
"DataRepType",

@ -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,
}

@ -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);
}
}

@ -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;

@ -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);
}

@ -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
@ -27,74 +27,7 @@
#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;

@ -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;

@ -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 {

Loading…
Cancel
Save