/* Copyright (C) 2021 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 crate::applayer::{self, *};
use crate::core::*;
use crate::flow::Flow;

use std::ffi::CString;

use sawp::error::Error as SawpError;
use sawp::error::ErrorKind as SawpErrorKind;
use sawp::parser::{Direction, Parse};
use sawp::probe::{Probe, Status};
use sawp_modbus::{self, AccessType, ErrorFlags, Flags, Message};
use suricata_sys::sys::{
    AppLayerParserState, AppProto, SCAppLayerParserConfParserEnabled,
    SCAppLayerParserRegisterLogger, SCAppLayerParserStateIssetFlag,
    SCAppLayerProtoDetectConfProtoDetectionEnabledDefault,
};

pub const REQUEST_FLOOD: usize = 500; // Default unreplied Modbus requests are considered a flood
pub const MODBUS_PARSER: sawp_modbus::Modbus = sawp_modbus::Modbus { probe_strict: true };

static mut ALPROTO_MODBUS: AppProto = ALPROTO_UNKNOWN;

#[derive(AppLayerEvent)]
enum ModbusEvent {
    UnsolicitedResponse,
    InvalidFunctionCode,
    InvalidLength,
    InvalidValue,
    InvalidExceptionCode,
    ValueMismatch,
    Flooded,
    InvalidProtocolId,
}
pub struct ModbusTransaction {
    pub id: u64,

    pub request: Option<Message>,
    pub response: Option<Message>,

    pub tx_data: AppLayerTxData,
}

impl Transaction for ModbusTransaction {
    fn id(&self) -> u64 {
        self.id
    }
}

impl ModbusTransaction {
    pub fn new(id: u64) -> Self {
        Self {
            id,
            request: None,
            response: None,
            tx_data: AppLayerTxData::new(),
        }
    }

    fn set_event(&mut self, event: ModbusEvent) {
        self.tx_data.set_event(event as u8);
    }

    fn set_events_from_flags(&mut self, flags: &Flags<ErrorFlags>) {
        if flags.intersects(ErrorFlags::FUNC_CODE) {
            self.set_event(ModbusEvent::InvalidFunctionCode);
        }
        if flags.intersects(ErrorFlags::DATA_VALUE) {
            self.set_event(ModbusEvent::InvalidValue);
        }
        if flags.intersects(ErrorFlags::DATA_LENGTH) {
            self.set_event(ModbusEvent::InvalidLength);
        }
        if flags.intersects(ErrorFlags::EXC_CODE) {
            self.set_event(ModbusEvent::InvalidExceptionCode);
        }
        if flags.intersects(ErrorFlags::PROTO_ID) {
            self.set_event(ModbusEvent::InvalidProtocolId);
        }
    }
}

#[derive(Default)]
pub struct ModbusState {
    state_data: AppLayerStateData,
    pub transactions: Vec<ModbusTransaction>,
    tx_id: u64,
    givenup: bool, // Indicates flood
}

impl State<ModbusTransaction> for ModbusState {
    fn get_transaction_count(&self) -> usize {
        self.transactions.len()
    }

    fn get_transaction_by_index(&self, index: usize) -> Option<&ModbusTransaction> {
        self.transactions.get(index)
    }
}

impl ModbusState {
    pub fn new() -> Self {
        Default::default()
    }

    pub fn get_tx(&mut self, tx_id: u64) -> Option<&mut ModbusTransaction> {
        self.transactions.iter_mut().find(|tx| tx.id == tx_id + 1)
    }

    /// Searches the requests in order to find one matching the given response. Returns the matching
    /// transaction, if it exists
    pub fn find_request_and_validate(
        &mut self, resp: &mut Message,
    ) -> Option<&mut ModbusTransaction> {
        for tx in &mut self.transactions {
            if let Some(req) = &tx.request {
                if tx.response.is_none() && resp.matches(req) {
                    tx.tx_data.updated_tc = true;
                    tx.tx_data.updated_ts = true;
                    return Some(tx);
                }
            }
        }
        None
    }

    /// Searches the responses in order to find one matching the given request. Returns the matching
    /// transaction, if it exists
    pub fn find_response_and_validate(
        &mut self, req: &mut Message,
    ) -> Option<&mut ModbusTransaction> {
        for tx in &mut self.transactions {
            if let Some(resp) = &tx.response {
                if tx.request.is_none() && req.matches(resp) {
                    tx.tx_data.updated_tc = true;
                    tx.tx_data.updated_ts = true;
                    return Some(tx);
                }
            }
        }
        None
    }

    pub fn new_tx(&mut self) -> Option<ModbusTransaction> {
        // Check flood limit
        if self.givenup {
            return None;
        }

        self.tx_id += 1;
        let mut tx = ModbusTransaction::new(self.tx_id);

        if REQUEST_FLOOD != 0 && self.transactions.len() >= REQUEST_FLOOD {
            tx.set_event(ModbusEvent::Flooded);
            self.givenup = true;
        }

        Some(tx)
    }

    pub fn free_tx(&mut self, tx_id: u64) {
        if let Some(index) = self.transactions.iter().position(|tx| tx.id == tx_id + 1) {
            self.transactions.remove(index);

            // Check flood limit
            if self.givenup && REQUEST_FLOOD != 0 && self.transactions.len() < REQUEST_FLOOD {
                self.givenup = false;
            }
        }
    }

    pub fn parse(
        &mut self, flow: *mut Flow, input: &[u8], direction: Direction,
    ) -> AppLayerResult {
        let mut rest = input;
        while !rest.is_empty() {
            match MODBUS_PARSER.parse(rest, direction.clone()) {
                Ok((inner_rest, Some(mut msg))) => {
                    match direction {
                        Direction::ToServer | Direction::Unknown => {
                            match self.find_response_and_validate(&mut msg) {
                                Some(tx) => {
                                    tx.set_events_from_flags(&msg.error_flags);
                                    tx.tx_data.updated_tc = true;
                                    tx.tx_data.updated_ts = true;
                                    tx.request = Some(msg);
                                    if !flow.is_null() {
                                        sc_app_layer_parser_trigger_raw_stream_inspection(
                                            flow,
                                            Direction::ToServer as i32,
                                        );
                                    }
                                }
                                None => {
                                    let mut tx = match self.new_tx() {
                                        Some(tx) => tx,
                                        None => return AppLayerResult::err(),
                                    };
                                    tx.set_events_from_flags(&msg.error_flags);
                                    tx.request = Some(msg);
                                    self.transactions.push(tx);
                                    if !flow.is_null() {
                                        sc_app_layer_parser_trigger_raw_stream_inspection(
                                            flow,
                                            Direction::ToServer as i32,
                                        );
                                    }
                                }
                            }
                        }
                        Direction::ToClient => match self.find_request_and_validate(&mut msg) {
                            Some(tx) => {
                                if msg
                                    .access_type
                                    .intersects(AccessType::READ | AccessType::WRITE)
                                    && msg.error_flags.intersects(
                                        ErrorFlags::DATA_LENGTH | ErrorFlags::DATA_VALUE,
                                    )
                                {
                                    tx.set_event(ModbusEvent::ValueMismatch);
                                } else {
                                    tx.set_events_from_flags(&msg.error_flags);
                                }
                                tx.tx_data.updated_tc = true;
                                tx.tx_data.updated_ts = true;
                                tx.response = Some(msg);
                                if !flow.is_null() {
                                    sc_app_layer_parser_trigger_raw_stream_inspection(
                                        flow,
                                        Direction::ToClient as i32,
                                    );
                                }
                            }
                            None => {
                                let mut tx = match self.new_tx() {
                                    Some(tx) => tx,
                                    None => return AppLayerResult::err(),
                                };
                                if msg
                                    .access_type
                                    .intersects(AccessType::READ | AccessType::WRITE)
                                    && msg.error_flags.intersects(
                                        ErrorFlags::DATA_LENGTH | ErrorFlags::DATA_VALUE,
                                    )
                                {
                                    tx.set_event(ModbusEvent::ValueMismatch);
                                } else {
                                    tx.set_events_from_flags(&msg.error_flags);
                                }
                                tx.response = Some(msg);
                                tx.set_event(ModbusEvent::UnsolicitedResponse);
                                self.transactions.push(tx);
                                if !flow.is_null() {
                                    sc_app_layer_parser_trigger_raw_stream_inspection(
                                        flow,
                                        Direction::ToClient as i32,
                                    );
                                }
                            }
                        },
                    }

                    if inner_rest.len() >= rest.len() {
                        return AppLayerResult::err();
                    }
                    rest = inner_rest;
                }
                Ok((inner_rest, None)) => {
                    return AppLayerResult::incomplete(
                        (input.len() - inner_rest.len()) as u32,
                        inner_rest.len() as u32 + 1,
                    );
                }
                Err(SawpError {
                    kind: SawpErrorKind::Incomplete(sawp::error::Needed::Size(needed)),
                }) => {
                    return AppLayerResult::incomplete(
                        (input.len() - rest.len()) as u32,
                        (rest.len() + needed.get()) as u32,
                    );
                }
                Err(SawpError {
                    kind: SawpErrorKind::Incomplete(sawp::error::Needed::Unknown),
                }) => {
                    return AppLayerResult::incomplete(
                        (input.len() - rest.len()) as u32,
                        rest.len() as u32 + 1,
                    );
                }
                Err(_) => return AppLayerResult::err(),
            }
        }
        AppLayerResult::ok()
    }
}

/// Probe input to see if it looks like Modbus.
extern "C" fn modbus_probe(
    _flow: *const Flow, _direction: u8, input: *const u8, len: u32, _rdir: *mut u8,
) -> AppProto {
    if input.is_null() {
        return ALPROTO_UNKNOWN;
    }
    let slice: &[u8] = unsafe { std::slice::from_raw_parts(input as *mut u8, len as usize) };
    match MODBUS_PARSER.probe(slice, Direction::Unknown) {
        Status::Recognized => unsafe { ALPROTO_MODBUS },
        Status::Incomplete => ALPROTO_UNKNOWN,
        Status::Unrecognized => ALPROTO_FAILED,
    }
}

extern "C" fn modbus_state_new(
    _orig_state: *mut std::os::raw::c_void, _orig_proto: AppProto,
) -> *mut std::os::raw::c_void {
    Box::into_raw(Box::new(ModbusState::new())) as *mut std::os::raw::c_void
}

extern "C" fn modbus_state_free(state: *mut std::os::raw::c_void) {
    let _state: Box<ModbusState> = unsafe { Box::from_raw(state as *mut ModbusState) };
}

unsafe extern "C" fn modbus_state_tx_free(state: *mut std::os::raw::c_void, tx_id: u64) {
    let state = cast_pointer!(state, ModbusState);
    state.free_tx(tx_id);
}

unsafe extern "C" fn modbus_parse_request(
    flow: *mut Flow, state: *mut std::os::raw::c_void, pstate: *mut AppLayerParserState,
    stream_slice: StreamSlice, _data: *const std::os::raw::c_void,
) -> AppLayerResult {
    let buf = stream_slice.as_slice();
    if buf.is_empty() {
        if SCAppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF_TS) > 0 {
            return AppLayerResult::ok();
        } else {
            return AppLayerResult::err();
        }
    }

    let state = cast_pointer!(state, ModbusState);
    state.parse(flow, buf, Direction::ToServer)
}

unsafe extern "C" fn modbus_parse_response(
    flow: *mut Flow, state: *mut std::os::raw::c_void, pstate: *mut AppLayerParserState,
    stream_slice: StreamSlice, _data: *const std::os::raw::c_void,
) -> AppLayerResult {
    let buf = stream_slice.as_slice();
    if buf.is_empty() {
        if SCAppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF_TC) > 0 {
            return AppLayerResult::ok();
        } else {
            return AppLayerResult::err();
        }
    }

    let state = cast_pointer!(state, ModbusState);
    state.parse(flow, buf, Direction::ToClient)
}

#[no_mangle]
pub unsafe extern "C" fn SCModbusStateGetTxCount(state: *mut std::os::raw::c_void) -> u64 {
    let state = cast_pointer!(state, ModbusState);
    state.tx_id
}

unsafe extern "C" fn modbus_state_get_tx(
    state: *mut std::os::raw::c_void, tx_id: u64,
) -> *mut std::os::raw::c_void {
    let state = cast_pointer!(state, ModbusState);
    match state.get_tx(tx_id) {
        Some(tx) => (tx as *mut ModbusTransaction) as *mut std::os::raw::c_void,
        None => std::ptr::null_mut(),
    }
}

unsafe extern "C" fn modbus_tx_get_alstate_progress(
    tx: *mut std::os::raw::c_void, _direction: u8,
) -> std::os::raw::c_int {
    let tx = cast_pointer!(tx, ModbusTransaction);
    tx.response.is_some() as std::os::raw::c_int
}

unsafe extern "C" fn modbus_state_get_tx_data(
    tx: *mut std::os::raw::c_void,
) -> *mut AppLayerTxData {
    let tx = cast_pointer!(tx, ModbusTransaction);
    &mut tx.tx_data
}

export_state_data_get!(modbus_get_state_data, ModbusState);

#[no_mangle]
pub unsafe extern "C" fn SCRegisterModbusParser() {
    let default_port = std::ffi::CString::new("[502]").unwrap();
    let parser = RustParser {
        name: b"modbus\0".as_ptr() as *const std::os::raw::c_char,
        default_port: default_port.as_ptr(),
        ipproto: IPPROTO_TCP,
        probe_ts: Some(modbus_probe),
        probe_tc: Some(modbus_probe),
        min_depth: 0,
        max_depth: 16,
        state_new: modbus_state_new,
        state_free: modbus_state_free,
        tx_free: modbus_state_tx_free,
        parse_ts: modbus_parse_request,
        parse_tc: modbus_parse_response,
        get_tx_count: SCModbusStateGetTxCount,
        get_tx: modbus_state_get_tx,
        tx_comp_st_ts: 1,
        tx_comp_st_tc: 1,
        tx_get_progress: modbus_tx_get_alstate_progress,
        get_eventinfo: Some(ModbusEvent::get_event_info),
        get_eventinfo_byid: Some(ModbusEvent::get_event_info_by_id),
        localstorage_new: None,
        localstorage_free: None,
        get_tx_files: None,
        get_tx_iterator: Some(applayer::state_get_tx_iterator::<ModbusState, ModbusTransaction>),
        get_tx_data: modbus_state_get_tx_data,
        get_state_data: modbus_get_state_data,
        apply_tx_config: None,
        flags: 0,
        get_frame_id_by_name: None,
        get_frame_name_by_id: None,
        get_state_id_by_name: None,
        get_state_name_by_id: None,
    };

    let ip_proto_str = CString::new("tcp").unwrap();
    if SCAppLayerProtoDetectConfProtoDetectionEnabledDefault(
        ip_proto_str.as_ptr(),
        parser.name,
        false,
    ) != 0
    {
        let alproto = AppLayerRegisterProtocolDetection(&parser, 1);
        ALPROTO_MODBUS = alproto;
        if SCAppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
            let _ = AppLayerRegisterParser(&parser, alproto);
        }
        SCAppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_MODBUS);
    }
}

// This struct and accessor functions are used for app-layer-modbus.c tests.
pub mod test {
    use super::ModbusState;
    use sawp_modbus::{Data, Message, Read, Write};
    use std::ffi::c_void;
    #[repr(C)]
    pub struct ModbusMessage(*const c_void);

    #[no_mangle]
    pub unsafe extern "C" fn SCModbusMessageGetFunction(msg: *const ModbusMessage) -> u8 {
        let msg = msg.as_ref().unwrap().0 as *const Message;
        let msg = msg.as_ref().unwrap();
        msg.function.raw
    }

    #[no_mangle]
    pub unsafe extern "C" fn SCModbusMessageGetSubfunction(msg: *const ModbusMessage) -> u16 {
        let msg = msg.as_ref().unwrap().0 as *const Message;
        let msg = msg.as_ref().unwrap();
        if let Data::Diagnostic { func, data: _ } = &msg.data {
            func.raw
        } else {
            panic!("wrong modbus message data type");
        }
    }

    #[no_mangle]
    pub unsafe extern "C" fn SCModbusMessageGetReadRequestAddress(
        msg: *const ModbusMessage,
    ) -> u16 {
        let msg = msg.as_ref().unwrap().0 as *const Message;
        let msg = msg.as_ref().unwrap();
        if let Data::Read(Read::Request {
            address,
            quantity: _,
        }) = &msg.data
        {
            *address
        } else {
            panic!("wrong modbus message data type");
        }
    }

    #[no_mangle]
    pub unsafe extern "C" fn SCModbusMessageGetReadRequestQuantity(
        msg: *const ModbusMessage,
    ) -> u16 {
        let msg = msg.as_ref().unwrap().0 as *const Message;
        let msg = msg.as_ref().unwrap();
        if let Data::Read(Read::Request {
            address: _,
            quantity,
        }) = &msg.data
        {
            *quantity
        } else {
            panic!("wrong modbus message data type");
        }
    }

    #[no_mangle]
    pub unsafe extern "C" fn SCModbusMessageGetRwMultreqReadAddress(
        msg: *const ModbusMessage,
    ) -> u16 {
        let msg = msg.as_ref().unwrap().0 as *const Message;
        let msg = msg.as_ref().unwrap();
        if let Data::ReadWrite {
            read:
                Read::Request {
                    address,
                    quantity: _,
                },
            write: _,
        } = &msg.data
        {
            *address
        } else {
            panic!("wrong modbus message data type");
        }
    }

    #[no_mangle]
    pub unsafe extern "C" fn SCModbusMessageGetRwMultreqReadQuantity(
        msg: *const ModbusMessage,
    ) -> u16 {
        let msg = msg.as_ref().unwrap().0 as *const Message;
        let msg = msg.as_ref().unwrap();
        if let Data::ReadWrite {
            read:
                Read::Request {
                    address: _,
                    quantity,
                },
            write: _,
        } = &msg.data
        {
            *quantity
        } else {
            panic!("wrong modbus message data type");
        }
    }

    #[no_mangle]
    pub unsafe extern "C" fn SCModbusMessageGetRwMultreqWriteAddress(
        msg: *const ModbusMessage,
    ) -> u16 {
        let msg = msg.as_ref().unwrap().0 as *const Message;
        let msg = msg.as_ref().unwrap();
        if let Data::ReadWrite {
            read: _,
            write:
                Write::MultReq {
                    address,
                    quantity: _,
                    data: _,
                },
        } = &msg.data
        {
            *address
        } else {
            panic!("wrong modbus message data type");
        }
    }

    #[no_mangle]
    pub unsafe extern "C" fn SCModbusMessageGetRwMultreqWriteQuantity(
        msg: *const ModbusMessage,
    ) -> u16 {
        let msg = msg.as_ref().unwrap().0 as *const Message;
        let msg = msg.as_ref().unwrap();
        if let Data::ReadWrite {
            read: _,
            write:
                Write::MultReq {
                    address: _,
                    quantity,
                    data: _,
                },
        } = &msg.data
        {
            *quantity
        } else {
            panic!("wrong modbus message data type");
        }
    }

    #[no_mangle]
    pub unsafe extern "C" fn SCModbusMessageGetRwMultreqWriteData(
        msg: *const ModbusMessage, data_len: *mut usize,
    ) -> *const u8 {
        let msg = msg.as_ref().unwrap().0 as *const Message;
        let msg = msg.as_ref().unwrap();
        if let Data::ReadWrite {
            read: _,
            write:
                Write::MultReq {
                    address: _,
                    quantity: _,
                    data,
                },
        } = &msg.data
        {
            *data_len = data.len();
            data.as_slice().as_ptr()
        } else {
            panic!("wrong modbus message data type");
        }
    }

    #[no_mangle]
    pub unsafe extern "C" fn SCModbusMessageGetWriteMultreqAddress(
        msg: *const ModbusMessage,
    ) -> u16 {
        let msg = msg.as_ref().unwrap().0 as *const Message;
        let msg = msg.as_ref().unwrap();
        if let Data::Write(Write::MultReq {
            address,
            quantity: _,
            data: _,
        }) = &msg.data
        {
            *address
        } else {
            panic!("wrong modbus message data type");
        }
    }

    #[no_mangle]
    pub unsafe extern "C" fn SCModbusMessageGetWriteMultreqQuantity(
        msg: *const ModbusMessage,
    ) -> u16 {
        let msg = msg.as_ref().unwrap().0 as *const Message;
        let msg = msg.as_ref().unwrap();
        if let Data::Write(Write::MultReq {
            address: _,
            quantity,
            data: _,
        }) = &msg.data
        {
            *quantity
        } else {
            panic!("wrong modbus message data type");
        }
    }

    #[no_mangle]
    pub unsafe extern "C" fn SCModbusMessageGetWriteMultreqData(
        msg: *const ModbusMessage, data_len: *mut usize,
    ) -> *const u8 {
        let msg = msg.as_ref().unwrap().0 as *const Message;
        let msg = msg.as_ref().unwrap();
        if let Data::Write(Write::MultReq {
            address: _,
            quantity: _,
            data,
        }) = &msg.data
        {
            *data_len = data.len();
            data.as_slice().as_ptr()
        } else {
            panic!("wrong modbus message data type");
        }
    }

    #[no_mangle]
    pub unsafe extern "C" fn SCModbusMessageGetAndMask(msg: *const ModbusMessage) -> u16 {
        let msg = msg.as_ref().unwrap().0 as *const Message;
        let msg = msg.as_ref().unwrap();
        if let Data::Write(Write::Mask {
            address: _,
            and_mask,
            or_mask: _,
        }) = &msg.data
        {
            *and_mask
        } else {
            panic!("wrong modbus message data type");
        }
    }

    #[no_mangle]
    pub unsafe extern "C" fn SCModbusMessageGetOrMask(msg: *const ModbusMessage) -> u16 {
        let msg = msg.as_ref().unwrap().0 as *const Message;
        let msg = msg.as_ref().unwrap();
        if let Data::Write(Write::Mask {
            address: _,
            and_mask: _,
            or_mask,
        }) = &msg.data
        {
            *or_mask
        } else {
            panic!("wrong modbus message data type");
        }
    }

    #[no_mangle]
    pub unsafe extern "C" fn SCModbusMessageGetWriteAddress(msg: *const ModbusMessage) -> u16 {
        let msg = msg.as_ref().unwrap().0 as *const Message;
        let msg = msg.as_ref().unwrap();
        if let Data::Write(Write::Other { address, data: _ }) = &msg.data {
            *address
        } else {
            panic!("wrong modbus message data type");
        }
    }

    #[no_mangle]
    pub unsafe extern "C" fn SCModbusMessageGetWriteData(msg: *const ModbusMessage) -> u16 {
        let msg = msg.as_ref().unwrap().0 as *const Message;
        let msg = msg.as_ref().unwrap();
        if let Data::Write(Write::Other { address: _, data }) = &msg.data {
            *data
        } else {
            panic!("wrong modbus message data type");
        }
    }

    #[no_mangle]
    pub unsafe extern "C" fn SCModbusMessageGetBytevecData(
        msg: *const ModbusMessage, data_len: *mut usize,
    ) -> *const u8 {
        let msg = msg.as_ref().unwrap().0 as *const Message;
        let msg = msg.as_ref().unwrap();
        if let Data::ByteVec(data) = &msg.data {
            *data_len = data.len();
            data.as_slice().as_ptr()
        } else {
            panic!("wrong modbus message data type");
        }
    }

    #[no_mangle]
    pub unsafe extern "C" fn SCModbusStateGetTxRequest(
        state: *mut std::os::raw::c_void, tx_id: u64,
    ) -> ModbusMessage {
        let state = cast_pointer!(state, ModbusState);
        if let Some(tx) = state.get_tx(tx_id) {
            if let Some(request) = &tx.request {
                ModbusMessage((request as *const Message) as *const c_void)
            } else {
                ModbusMessage(std::ptr::null())
            }
        } else {
            ModbusMessage(std::ptr::null())
        }
    }

    #[no_mangle]
    pub unsafe extern "C" fn SCModbusStateGetTxResponse(
        state: *mut std::os::raw::c_void, tx_id: u64,
    ) -> ModbusMessage {
        let state = cast_pointer!(state, ModbusState);
        if let Some(tx) = state.get_tx(tx_id) {
            if let Some(response) = &tx.response {
                ModbusMessage((response as *const Message) as *const c_void)
            } else {
                ModbusMessage(std::ptr::null())
            }
        } else {
            ModbusMessage(std::ptr::null())
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use sawp_modbus::{
        Data, Diagnostic, DiagnosticSubfunction, Exception, ExceptionCode, FunctionCode, Read,
        Write,
    };

    const INVALID_FUNC_CODE: &[u8] = &[
        0x00, 0x00, // Transaction ID
        0x00, 0x00, // Protocol ID
        0x00, 0x02, // Length
        0x00, // Unit ID
        0x00, // Function code
    ];

    const RD_COILS_REQ: &[u8] = &[
        0x00, 0x00, // Transaction ID
        0x00, 0x00, // Protocol ID
        0x00, 0x06, // Length
        0x00, // Unit ID
        0x01, // Function code
        0x78, 0x90, // Starting Address
        0x00, 0x13, // Quantity of coils
    ];

    const RD_COILS_RESP: &[u8] = &[
        0x00, 0x00, // Transaction ID
        0x00, 0x00, // Protocol ID
        0x00, 0x06, // Length
        0x00, // Unit ID
        0x01, // Function code
        0x03, // Byte count
        0xCD, 0x6B, 0x05, // Coil Status
    ];

    const RD_COILS_ERR_RESP: &[u8] = &[
        0x00, 0x00, // Transaction ID
        0x00, 0x00, // Protocol ID
        0x00, 0x03, // Length
        0x00, // Unit ID
        0x81, // Function code
        0xFF, // Exception code
    ];

    const WR_SINGLE_REG_REQ: &[u8] = &[
        0x00, 0x0A, // Transaction ID
        0x00, 0x00, // Protocol ID
        0x00, 0x06, // Length
        0x00, // Unit ID
        0x06, // Function code
        0x00, 0x01, // Register Address
        0x00, 0x03, // Register Value
    ];

    const INVALID_WR_SINGLE_REG_REQ: &[u8] = &[
        0x00, 0x0A, // Transaction ID
        0x00, 0x00, // Protocol ID
        0x00, 0x04, // Length
        0x00, // Unit ID
        0x06, // Function code
        0x00, 0x01, // Register Address
    ];

    const WR_SINGLE_REG_RESP: &[u8] = &[
        0x00, 0x0A, // Transaction ID
        0x00, 0x00, // Protocol ID
        0x00, 0x06, // Length
        0x00, // Unit ID
        0x06, // Function code
        0x00, 0x01, // Register Address
        0x00, 0x03, // Register Value
    ];

    const WR_MULT_REG_REQ: &[u8] = &[
        0x00, 0x0A, // Transaction ID
        0x00, 0x00, // Protocol ID
        0x00, 0x0B, // Length
        0x00, // Unit ID
        0x10, // Function code
        0x00, 0x01, // Starting Address
        0x00, 0x02, // Quantity of Registers
        0x04, // Byte count
        0x00, 0x0A, // Registers Value
        0x01, 0x02,
    ];

    const INVALID_PDU_WR_MULT_REG_REQ: &[u8] = &[
        0x00, 0x0A, // Transaction ID
        0x00, 0x00, // Protocol ID
        0x00, 0x02, // Length
        0x00, // Unit ID
        0x10, // Function code
    ];

    const WR_MULT_REG_RESP: &[u8] = &[
        0x00, 0x0A, // Transaction ID
        0x00, 0x00, // Protocol ID
        0x00, 0x06, // Length
        0x00, // Unit ID
        0x10, // Function code
        0x00, 0x01, // Starting Address
        0x00, 0x02, // Quantity of Registers
    ];

    const MASK_WR_REG_REQ: &[u8] = &[
        0x00, 0x0A, // Transaction ID
        0x00, 0x00, // Protocol ID
        0x00, 0x08, // Length
        0x00, // Unit ID
        0x16, // Function code
        0x00, 0x04, // Reference Address
        0x00, 0xF2, // And_Mask
        0x00, 0x25, // Or_Mask
    ];

    const INVALID_MASK_WR_REG_REQ: &[u8] = &[
        0x00, 0x0A, // Transaction ID
        0x00, 0x00, // Protocol ID
        0x00, 0x06, // Length
        0x00, // Unit ID
        0x16, // Function code
        0x00, 0x04, // Reference Address
        0x00, 0xF2, // And_Mask
    ];

    const MASK_WR_REG_RESP: &[u8] = &[
        0x00, 0x0A, // Transaction ID
        0x00, 0x00, // Protocol ID
        0x00, 0x08, // Length
        0x00, // Unit ID
        0x16, // Function code
        0x00, 0x04, // Reference Address
        0x00, 0xF2, // And_Mask
        0x00, 0x25, // Or_Mask
    ];

    const RD_WR_MULT_REG_REQ: &[u8] = &[
        0x12, 0x34, // Transaction ID
        0x00, 0x00, // Protocol ID
        0x00, 0x11, // Length
        0x00, // Unit ID
        0x17, // Function code
        0x00, 0x03, // Read Starting Address
        0x00, 0x06, // Quantity to Read
        0x00, 0x0E, // Write Starting Address
        0x00, 0x03, // Quantity to Write
        0x06, // Write Byte count
        0x12, 0x34, // Write Registers Value
        0x56, 0x78, 0x9A, 0xBC,
    ];

    // Mismatch value in Byte count 0x0B instead of 0x0C
    const RD_WR_MULT_REG_RESP: &[u8] = &[
        0x12, 0x34, // Transaction ID
        0x00, 0x00, // Protocol ID
        0x00, 0x0E, // Length
        0x00, // Unit ID
        0x17, // Function code
        0x0B, // Byte count
        0x00, 0xFE, // Read Registers Value
        0x0A, 0xCD, 0x00, 0x01, 0x00, 0x03, 0x00, 0x0D, 0x00,
    ];

    const FORCE_LISTEN_ONLY_MODE: &[u8] = &[
        0x0A, 0x00, // Transaction ID
        0x00, 0x00, // Protocol ID
        0x00, 0x06, // Length
        0x00, // Unit ID
        0x08, // Function code
        0x00, 0x04, // Sub-function code
        0x00, 0x00, // Data
    ];

    const INVALID_PROTO_REQ: &[u8] = &[
        0x00, 0x00, // Transaction ID
        0x00, 0x01, // Protocol ID
        0x00, 0x06, // Length
        0x00, // Unit ID
        0x01, // Function code
        0x78, 0x90, // Starting Address
        0x00, 0x13, // Quantity of coils
    ];

    const INVALID_LEN_WR_MULT_REG_REQ: &[u8] = &[
        0x00, 0x0A, // Transaction ID
        0x00, 0x00, // Protocol ID
        0x00, 0x09, // Length
        0x00, // Unit ID
        0x10, // Function code
        0x00, 0x01, // Starting Address
        0x00, 0x02, // Quantity of Registers
        0x04, // Byte count
        0x00, 0x0A, // Registers Value
        0x01, 0x02,
    ];

    const EXCEEDED_LEN_WR_MULT_REG_REQ: &[u8] = &[
        0x00, 0x0A, // Transaction ID
        0x00, 0x00, // Protocol ID
        0xff, 0xfa, // Length
        0x00, // Unit ID
        0x10, // Function code
        0x00, 0x01, // Starting Address
        0x7f, 0xf9, // Quantity of Registers
        0xff, // Byte count
    ];

    #[test]
    fn read_coils() {
        let mut state = ModbusState::new();
        assert_eq!(
            AppLayerResult::ok(),
            state.parse(std::ptr::null_mut(), RD_COILS_REQ, Direction::ToServer)
        );
        assert_eq!(state.transactions.len(), 1);

        let tx = &state.transactions[0];
        let msg = tx.request.as_ref().unwrap();
        assert_eq!(msg.function.code, FunctionCode::RdCoils);
        assert_eq!(
            msg.data,
            Data::Read(Read::Request {
                address: 0x7890,
                quantity: 0x0013
            })
        );

        assert_eq!(
            AppLayerResult::ok(),
            state.parse(std::ptr::null_mut(), RD_COILS_RESP, Direction::ToClient)
        );
        assert_eq!(state.transactions.len(), 1);

        let tx = &state.transactions[0];
        let msg = tx.response.as_ref().unwrap();
        assert_eq!(msg.function.code, FunctionCode::RdCoils);
        assert_eq!(msg.data, Data::Read(Read::Response(vec![0xCD, 0x6B, 0x05])));
    }

    #[test]
    fn write_multiple_registers() {
        let mut state = ModbusState::new();
        assert_eq!(
            AppLayerResult::ok(),
            state.parse(std::ptr::null_mut(), WR_MULT_REG_REQ, Direction::ToServer)
        );
        assert_eq!(state.transactions.len(), 1);

        let tx = &state.transactions[0];
        let msg = tx.request.as_ref().unwrap();
        assert_eq!(msg.function.code, FunctionCode::WrMultRegs);
        assert_eq!(
            msg.data,
            Data::Write(Write::MultReq {
                address: 0x0001,
                quantity: 0x0002,
                data: vec![0x00, 0x0a, 0x01, 0x02],
            })
        );

        assert_eq!(
            AppLayerResult::ok(),
            state.parse(std::ptr::null_mut(), WR_MULT_REG_RESP, Direction::ToClient)
        );
        assert_eq!(state.transactions.len(), 1);

        let tx = &state.transactions[0];
        let msg = tx.response.as_ref().unwrap();
        assert_eq!(msg.function.code, FunctionCode::WrMultRegs);
        assert_eq!(
            msg.data,
            Data::Write(Write::Other {
                address: 0x0001,
                data: 0x0002
            })
        );
    }

    #[test]
    fn read_write_multiple_registers() {
        let mut state = ModbusState::new();
        assert_eq!(
            AppLayerResult::ok(),
            state.parse(std::ptr::null_mut(), RD_WR_MULT_REG_REQ, Direction::ToServer)
        );
        assert_eq!(state.transactions.len(), 1);

        let tx = &state.transactions[0];
        let msg = tx.request.as_ref().unwrap();
        assert_eq!(msg.function.code, FunctionCode::RdWrMultRegs);
        assert_eq!(
            msg.data,
            Data::ReadWrite {
                read: Read::Request {
                    address: 0x0003,
                    quantity: 0x0006,
                },
                write: Write::MultReq {
                    address: 0x000e,
                    quantity: 0x0003,
                    data: vec![0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc]
                }
            }
        );

        assert_eq!(
            AppLayerResult::ok(),
            state.parse(std::ptr::null_mut(), RD_WR_MULT_REG_RESP, Direction::ToClient)
        );
        assert_eq!(state.transactions.len(), 1);

        let tx = &state.transactions[0];
        let msg = tx.response.as_ref().unwrap();
        assert_eq!(msg.function.code, FunctionCode::RdWrMultRegs);
        assert_eq!(
            msg.data,
            Data::Read(Read::Response(vec![
                0x00, 0xFE, 0x0A, 0xCD, 0x00, 0x01, 0x00, 0x03, 0x00, 0x0D, 0x00,
            ]))
        );
    }

    #[test]
    fn force_listen_only_mode() {
        let mut state = ModbusState::new();
        assert_eq!(
            AppLayerResult::ok(),
            state.parse(
                std::ptr::null_mut(),
                FORCE_LISTEN_ONLY_MODE,
                Direction::ToServer
            )
        );
        assert_eq!(state.transactions.len(), 1);

        let tx = &state.transactions[0];
        let msg = tx.request.as_ref().unwrap();
        assert_eq!(msg.function.code, FunctionCode::Diagnostic);
        assert_eq!(
            msg.data,
            Data::Diagnostic {
                func: Diagnostic {
                    raw: 4,
                    code: DiagnosticSubfunction::ForceListenOnlyMode
                },
                data: vec![0x00, 0x00]
            }
        );
    }

    #[test]
    fn invalid_protocol_version() {
        let mut state = ModbusState::new();
        assert_eq!(
            AppLayerResult::ok(),
            state.parse(std::ptr::null_mut(), INVALID_PROTO_REQ, Direction::ToServer)
        );

        assert_eq!(state.transactions.len(), 1);
        let tx = &state.transactions[0];
        let msg = tx.request.as_ref().unwrap();
        assert_eq!(msg.error_flags, ErrorFlags::PROTO_ID);
    }

    #[test]
    fn unsolicited_response() {
        let mut state = ModbusState::new();
        assert_eq!(
            AppLayerResult::ok(),
            state.parse(std::ptr::null_mut(), RD_COILS_RESP, Direction::ToClient)
        );
        assert_eq!(state.transactions.len(), 1);

        let tx = &state.transactions[0];
        let msg = tx.response.as_ref().unwrap();
        assert_eq!(msg.function.code, FunctionCode::RdCoils);
        assert_eq!(msg.data, Data::Read(Read::Response(vec![0xCD, 0x6B, 0x05])));
    }

    #[test]
    fn invalid_length_request() {
        let mut state = ModbusState::new();
        assert_eq!(
            AppLayerResult::incomplete(15, 4),
            state.parse(
                std::ptr::null_mut(),
                INVALID_LEN_WR_MULT_REG_REQ,
                Direction::ToServer
            )
        );
        assert_eq!(state.transactions.len(), 1);

        let tx = &state.transactions[0];
        let msg = tx.request.as_ref().unwrap();
        assert_eq!(msg.function.code, FunctionCode::WrMultRegs);
        assert_eq!(
            msg.data,
            Data::Write(Write::MultReq {
                address: 0x0001,
                quantity: 0x0002,
                data: vec![0x00, 0x0a]
            })
        );
        assert_eq!(msg.error_flags, ErrorFlags::DATA_LENGTH);
    }

    #[test]
    fn exception_code_invalid() {
        let mut state = ModbusState::new();
        assert_eq!(
            AppLayerResult::ok(),
            state.parse(std::ptr::null_mut(), RD_COILS_REQ, Direction::ToServer)
        );
        assert_eq!(state.transactions.len(), 1);

        let tx = &state.transactions[0];
        let msg = tx.request.as_ref().unwrap();
        assert_eq!(msg.function.code, FunctionCode::RdCoils);
        assert_eq!(
            msg.data,
            Data::Read(Read::Request {
                address: 0x7890,
                quantity: 0x0013
            })
        );

        assert_eq!(
            AppLayerResult::ok(),
            state.parse(std::ptr::null_mut(), RD_COILS_ERR_RESP, Direction::ToClient)
        );
        assert_eq!(state.transactions.len(), 1);

        let tx = &state.transactions[0];
        let msg = tx.response.as_ref().unwrap();
        assert_eq!(
            msg.data,
            Data::Exception(Exception {
                raw: 255,
                code: ExceptionCode::Unknown
            })
        );
        assert_eq!(msg.error_flags, ErrorFlags::EXC_CODE);
    }

    #[test]
    fn fragmentation_1_adu_in_2_tcp_packets() {
        let mut state = ModbusState::new();
        assert_eq!(
            AppLayerResult::incomplete(0, 12),
            state.parse(
                std::ptr::null_mut(),
                &RD_COILS_REQ[0..(RD_COILS_REQ.len() - 3)],
                Direction::ToServer
            )
        );
        assert_eq!(state.transactions.len(), 0);
        assert_eq!(
            AppLayerResult::ok(),
            state.parse(std::ptr::null_mut(), RD_COILS_REQ, Direction::ToServer)
        );
        assert_eq!(state.transactions.len(), 1);

        let tx = &state.transactions[0];
        assert!(&tx.request.is_some());
        let msg = tx.request.as_ref().unwrap();
        assert_eq!(msg.function.code, FunctionCode::RdCoils);
        assert_eq!(
            msg.data,
            Data::Read(Read::Request {
                address: 0x7890,
                quantity: 0x0013
            })
        );
    }

    #[test]
    fn fragmentation_2_adu_in_1_tcp_packet() {
        let req = [RD_COILS_REQ, WR_MULT_REG_REQ].concat();
        let resp = [RD_COILS_RESP, WR_MULT_REG_RESP].concat();

        let mut state = ModbusState::new();
        assert_eq!(
            AppLayerResult::ok(),
            state.parse(std::ptr::null_mut(), &req, Direction::ToServer)
        );
        assert_eq!(state.transactions.len(), 2);

        let tx = &state.transactions[0];
        let msg = tx.request.as_ref().unwrap();
        assert_eq!(msg.function.code, FunctionCode::RdCoils);
        assert_eq!(
            msg.data,
            Data::Read(Read::Request {
                address: 0x7890,
                quantity: 0x0013
            })
        );

        let tx = &state.transactions[1];
        let msg = tx.request.as_ref().unwrap();
        assert_eq!(msg.function.code, FunctionCode::WrMultRegs);
        assert_eq!(
            msg.data,
            Data::Write(Write::MultReq {
                address: 0x0001,
                quantity: 0x0002,
                data: vec![0x00, 0x0a, 0x01, 0x02]
            })
        );

        assert_eq!(
            AppLayerResult::ok(),
            state.parse(std::ptr::null_mut(), &resp, Direction::ToClient)
        );
        assert_eq!(state.transactions.len(), 2);

        let tx = &state.transactions[0];
        let msg = tx.response.as_ref().unwrap();
        assert_eq!(msg.function.code, FunctionCode::RdCoils);
        assert_eq!(msg.data, Data::Read(Read::Response(vec![0xCD, 0x6B, 0x05])));

        let tx = &state.transactions[1];
        let msg = tx.response.as_ref().unwrap();
        assert_eq!(msg.function.code, FunctionCode::WrMultRegs);
        assert_eq!(
            msg.data,
            Data::Write(Write::Other {
                address: 0x0001,
                data: 0x0002
            })
        );
    }

    #[test]
    fn exceeded_length_request() {
        let mut state = ModbusState::new();
        assert_eq!(
            AppLayerResult::ok(),
            state.parse(
                std::ptr::null_mut(),
                EXCEEDED_LEN_WR_MULT_REG_REQ,
                Direction::ToServer
            )
        );

        assert_eq!(state.transactions.len(), 1);
        let tx = &state.transactions[0];
        let msg = tx.request.as_ref().unwrap();
        assert_eq!(msg.error_flags, ErrorFlags::DATA_LENGTH);
    }

    #[test]
    fn invalid_pdu_len_req() {
        let mut state = ModbusState::new();
        assert_eq!(
            AppLayerResult::ok(),
            state.parse(
                std::ptr::null_mut(),
                INVALID_PDU_WR_MULT_REG_REQ,
                Direction::ToServer
            )
        );

        assert_eq!(state.transactions.len(), 1);

        let tx = &state.transactions[0];
        let msg = tx.request.as_ref().unwrap();
        assert_eq!(msg.function.code, FunctionCode::WrMultRegs);
        assert_eq!(msg.data, Data::ByteVec(vec![]));
    }

    #[test]
    fn mask_write_register_request() {
        let mut state = ModbusState::new();
        assert_eq!(
            AppLayerResult::ok(),
            state.parse(std::ptr::null_mut(), MASK_WR_REG_REQ, Direction::ToServer)
        );
        assert_eq!(state.transactions.len(), 1);

        let tx = &state.transactions[0];
        let msg = tx.request.as_ref().unwrap();
        assert_eq!(msg.function.code, FunctionCode::MaskWrReg);
        assert_eq!(
            msg.data,
            Data::Write(Write::Mask {
                address: 0x0004,
                and_mask: 0x00f2,
                or_mask: 0x0025
            })
        );

        assert_eq!(
            AppLayerResult::ok(),
            state.parse(std::ptr::null_mut(), MASK_WR_REG_RESP, Direction::ToClient)
        );
        assert_eq!(state.transactions.len(), 1);

        let tx = &state.transactions[0];
        let msg = tx.response.as_ref().unwrap();
        assert_eq!(msg.function.code, FunctionCode::MaskWrReg);
        assert_eq!(
            msg.data,
            Data::Write(Write::Mask {
                address: 0x0004,
                and_mask: 0x00f2,
                or_mask: 0x0025
            })
        );
    }

    #[test]
    fn write_single_register_request() {
        let mut state = ModbusState::new();
        assert_eq!(
            AppLayerResult::ok(),
            state.parse(std::ptr::null_mut(), WR_SINGLE_REG_REQ, Direction::ToServer)
        );
        assert_eq!(state.transactions.len(), 1);

        let tx = &state.transactions[0];
        let msg = tx.request.as_ref().unwrap();
        assert_eq!(msg.function.code, FunctionCode::WrSingleReg);
        assert_eq!(
            msg.data,
            Data::Write(Write::Other {
                address: 0x0001,
                data: 0x0003
            })
        );

        assert_eq!(
            AppLayerResult::ok(),
            state.parse(std::ptr::null_mut(), WR_SINGLE_REG_RESP, Direction::ToClient)
        );
        assert_eq!(state.transactions.len(), 1);

        let tx = &state.transactions[0];
        let msg = tx.response.as_ref().unwrap();
        assert_eq!(msg.function.code, FunctionCode::WrSingleReg);
        assert_eq!(
            msg.data,
            Data::Write(Write::Other {
                address: 0x0001,
                data: 0x0003
            })
        );
    }

    #[test]
    fn invalid_mask_write_register_request() {
        let mut state = ModbusState::new();
        assert_eq!(
            AppLayerResult::ok(),
            state.parse(
                std::ptr::null_mut(),
                INVALID_MASK_WR_REG_REQ,
                Direction::ToServer
            )
        );
        assert_eq!(state.transactions.len(), 1);

        let tx = &state.transactions[0];
        let msg = tx.request.as_ref().unwrap();
        assert_eq!(msg.function.code, FunctionCode::MaskWrReg);
        assert_eq!(msg.error_flags, ErrorFlags::DATA_LENGTH);
        assert_eq!(msg.data, Data::ByteVec(vec![0x00, 0x04, 0x00, 0xF2]));

        assert_eq!(
            AppLayerResult::ok(),
            state.parse(std::ptr::null_mut(), MASK_WR_REG_RESP, Direction::ToClient)
        );
        assert_eq!(state.transactions.len(), 1);

        let tx = &state.transactions[0];
        let msg = tx.response.as_ref().unwrap();
        assert_eq!(msg.function.code, FunctionCode::MaskWrReg);
        assert_eq!(
            msg.data,
            Data::Write(Write::Mask {
                address: 0x0004,
                and_mask: 0x00f2,
                or_mask: 0x0025
            })
        );
    }

    #[test]
    fn invalid_write_single_register_request() {
        let mut state = ModbusState::new();
        assert_eq!(
            AppLayerResult::ok(),
            state.parse(
                std::ptr::null_mut(),
                INVALID_WR_SINGLE_REG_REQ,
                Direction::ToServer
            )
        );
        assert_eq!(state.transactions.len(), 1);

        let tx = &state.transactions[0];
        let msg = tx.request.as_ref().unwrap();
        assert_eq!(msg.function.code, FunctionCode::WrSingleReg);
        assert_eq!(msg.error_flags, ErrorFlags::DATA_LENGTH);
        assert_eq!(msg.data, Data::ByteVec(vec![0x00, 0x01]));

        assert_eq!(
            AppLayerResult::ok(),
            state.parse(std::ptr::null_mut(), WR_SINGLE_REG_RESP, Direction::ToClient)
        );
        assert_eq!(state.transactions.len(), 1);

        let tx = &state.transactions[0];
        let msg = tx.response.as_ref().unwrap();
        assert_eq!(msg.function.code, FunctionCode::WrSingleReg);
        assert_eq!(
            msg.data,
            Data::Write(Write::Other {
                address: 0x0001,
                data: 0x0003
            })
        );
    }

    #[test]
    fn invalid_function_code() {
        let mut state = ModbusState::new();
        assert_eq!(
            AppLayerResult::ok(),
            state.parse(std::ptr::null_mut(), INVALID_FUNC_CODE, Direction::ToServer)
        );
        assert_eq!(state.transactions.len(), 1);

        let tx = &state.transactions[0];
        let msg = tx.request.as_ref().unwrap();
        assert_eq!(msg.function.code, FunctionCode::Unknown);
        assert_eq!(msg.data, Data::ByteVec(vec![]));
    }
}
