use std;
use std::ptr;
use std::ffi::{CStr, CString};
use std::os::raw::{c_char, c_void};
use crate::conf::{conf_get, get_memval};
use crate::core::*;
use crate::ftp::constant::*;
use lazy_static::lazy_static;
#[repr(C)]
pub struct DetectFtpModeData {
pub active: bool,
}
#[repr(C)]
pub struct DetectFtpReplyReceivedData {
pub received: bool,
}
#[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),
];
}
#[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 u16,
0,
0,
index as u32,
index as u32,
0,
);
}
}
}
#[repr(C)]
pub struct FtpTransferCmd {
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: 0,
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()
}
}
}
#[no_mangle]
pub unsafe extern "C" fn SCFTPGetConfigValues(
memcap: *mut u64, max_tx: *mut u32, max_line_len: *mut u32,
) {
if let Some(val) = conf_get("app-layer.protocols.ftp.memcap") {
if let Ok(v) = get_memval(val) {
*memcap = v;
SCLogConfig!("FTP memcap: {}", v);
} else {
SCLogWarning!(
"Invalid value {} for ftp.memcap; defaulting to {}",
val,
*memcap
);
}
}
if let Some(val) = conf_get("app-layer.protocols.ftp.max-tx") {
if let Ok(v) = val.parse::<u32>() {
*max_tx = v;
SCLogConfig!("FTP max tx: {}", v);
} else {
SCLogWarning!(
"Invalid value {} for ftp.max-tx; defaulting to {}",
val,
*max_tx
);
}
}
if let Some(val) = conf_get("app-layer.protocols.ftp.max-line-length") {
if let Ok(v) = get_memval(val) {
*max_line_len = v as u32;
SCLogConfig!("FTP max line length: {}", v);
} else {
SCLogWarning!(
"Invalid value {} for ftp.max-line-length; defaulting to {}",
val,
*max_line_len
);
}
}
}
#[no_mangle]
pub unsafe extern "C" fn SCFTPParseReplyReceived(c_str: *const c_char) -> *mut DetectFtpReplyReceivedData {
if c_str.is_null() {
return ptr::null_mut();
}
let Ok(input_str) = CStr::from_ptr(c_str).to_str() else {
return ptr::null_mut();
};
let received_val = match input_str.trim().to_ascii_lowercase().as_str() {
"true" | "1" | "yes" | "on" => true,
"false" | "0" | "no" | "off"=> false,
_ => return ptr::null_mut(), };
let boxed = Box::new(DetectFtpReplyReceivedData { received: received_val });
Box::into_raw(boxed)
}
#[no_mangle]
pub unsafe extern "C" fn SCFTPFreeReplyReceivedData(ptr: *mut DetectFtpReplyReceivedData) {
if !ptr.is_null() {
drop(Box::from_raw(ptr));
}
}
#[no_mangle]
pub unsafe extern "C" fn SCFTPParseMode(c_str: *const c_char) -> *mut DetectFtpModeData {
if c_str.is_null() {
return ptr::null_mut();
}
let Ok(input_str) = CStr::from_ptr(c_str).to_str() else {
return ptr::null_mut();
};
let is_active = match input_str.trim().to_ascii_lowercase().as_str() {
"active" => true,
"passive" => false,
_ => return ptr::null_mut(), };
let boxed = Box::new(DetectFtpModeData { active: is_active });
Box::into_raw(boxed)
}
#[no_mangle]
pub unsafe extern "C" fn SCFTPFreeModeData(ptr: *mut DetectFtpModeData) {
if !ptr.is_null() {
drop(Box::from_raw(ptr));
}
}
#[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))
}
#[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);
}
}