mod alert;
mod dns;
mod file;
mod flow;
mod http;
mod smtp;
mod stats;
mod tcp;
mod tls;

pub use alert::{Alert, AlertFlowInfo, AlertInfo};
pub use dns::{Dns, DnsAnswer, DnsEventType, DnsInfo, DnsQuery};
pub use file::{File, FileInfo, FileState};
pub use flow::{Flow, FlowInfo};
pub use http::Http;
pub use smtp::Smtp;
pub use stats::Stats;
pub use tcp::Tcp;
pub use tls::{Ja3, Tls};

use crate::Error;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(tag = "event_type")]
pub enum EventType {
    #[serde(rename = "alert")]
    Alert(Alert),
    #[serde(rename = "dns")]
    Dns(Dns),
    #[serde(rename = "flow")]
    Flow(Flow),
    #[serde(rename = "fileinfo")]
    File(File),
    #[serde(rename = "http")]
    Http(Http),
    #[serde(rename = "smtp")]
    Smtp(Smtp),
    #[serde(rename = "stats")]
    Stats(Stats),
    #[serde(rename = "tls")]
    Tls(Tls),
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Message {
    pub timestamp: DateTime<Utc>,
    #[serde(flatten)]
    pub event: EventType,
}

#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum State {
    #[serde(rename = "new")]
    New,
    #[serde(rename = "established")]
    Established,
    #[serde(rename = "closed")]
    Closed,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct EventFields {
    pub src_ip: std::net::IpAddr,
    #[serde(default)]
    pub src_port: Option<u16>,
    pub dest_ip: std::net::IpAddr,
    #[serde(default)]
    pub dest_port: Option<u16>,
    pub proto: String,
    pub app_proto: Option<String>,
    pub community_id: Option<String>,
    pub tcp: Option<Tcp>,
}

impl std::convert::TryFrom<&[u8]> for Message {
    type Error = Error;
    fn try_from(v: &[u8]) -> Result<Self, Self::Error> {
        log::trace!("Deserializing {}", String::from_utf8_lossy(v));
        serde_json::from_slice(v).map_err(|e| {
            let s = String::from_utf8_lossy(v);
            log::debug!("Failed to deserialize: {}", s);
            Error::from(e)
        })
    }
}

impl crate::intel::Observable for Message {
    fn timestamp(&self) -> DateTime<Utc> {
        self.timestamp.clone()
    }

    fn key(&self) -> Option<crate::intel::IdsKey> {
        if let EventType::Alert(a) = &self.event {
            Some(crate::intel::IdsKey {
                gid: a.info.gid,
                sid: a.info.signature_id,
            })
        } else {
            None
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use chrono::offset::TimeZone;
    use chrono::Timelike;
    use std::convert::TryFrom;

    #[test]
    fn should_deserialize_eve_alert() {
        let msg = r#"{"timestamp":"2017-12-18T10:48:14.627130-0700","flow_id":2061665895874790,"pcap_cnt":7,"event_type":"alert","src_ip":"10.151.223.136","src_port":26475,"dest_ip":"203.0.113.99","dest_port":80,"proto":"TCP","tx_id":0,"alert":{"action":"allowed","gid":4113433437,"signature_id":600074,"rev":1,"signature":"ProtectWise Canary Test 1.3 - Not Malicious","category":"","severity":3},"app_proto":"http","flow":{"pkts_toserver":4,"pkts_toclient":3,"bytes_toserver":582,"bytes_toclient":302,"start":"2017-12-18T10:48:14.622822-0700"}}"#;

        let eve = Message::try_from(msg.as_bytes().as_ref()).expect("Failed to read eve message");

        let expected_timestamp = Utc
            .ymd(2017, 12, 18)
            .and_hms(17, 48, 14)
            .with_nanosecond(627130000)
            .expect("Invalid time");

        assert_eq!(eve.timestamp, expected_timestamp);

        if let EventType::Alert(eve) = eve.event {
            let expected_src_ip = "10.151.223.136"
                .parse::<std::net::IpAddr>()
                .expect("Failed to parse");

            assert_eq!(eve.event_fields.src_ip, expected_src_ip);
            assert_eq!(eve.info.signature_id, 600074);
        } else {
            panic!("Not an alert");
        }
    }

    #[test]
    fn should_deserialize_eve_alert_with_community_id() {
        let msg = r#"{"timestamp":"2017-12-18T10:48:14.627130-0700","flow_id":2061665895874790,"pcap_cnt":7,"event_type":"alert","src_ip":"10.151.223.136","src_port":26475,"dest_ip":"203.0.113.99","dest_port":80,"proto":"TCP","community_id":"1:3ZcLCpqiJpUyBlL6cvSAzm4Cn94=","tx_id":0,"alert":{"action":"allowed","gid":4113433437,"signature_id":600074,"rev":1,"signature":"ProtectWise Canary Test 1.3 - Not Malicious","category":"","severity":3},"app_proto":"http","flow":{"pkts_toserver":4,"pkts_toclient":3,"bytes_toserver":582,"bytes_toclient":302,"start":"2017-12-18T10:48:14.622822-0700"}}"#;

        let eve = Message::try_from(msg.as_bytes().as_ref()).expect("Failed to read eve message");

        let expected_timestamp = Utc
            .ymd(2017, 12, 18)
            .and_hms(17, 48, 14)
            .with_nanosecond(627130000)
            .expect("Invalid time");

        assert_eq!(eve.timestamp, expected_timestamp);

        if let EventType::Alert(eve) = eve.event {
            let expected_src_ip = "10.151.223.136"
                .parse::<std::net::IpAddr>()
                .expect("Failed to parse");

            assert_eq!(eve.event_fields.src_ip, expected_src_ip);
            assert_eq!(eve.info.signature_id, 600074);
            assert_eq!(
                eve.event_fields.community_id,
                Some("1:3ZcLCpqiJpUyBlL6cvSAzm4Cn94=".to_owned())
            );
        } else {
            panic!("Not an alert");
        }
    }

    #[test]
    fn should_deserialize_eve_stats() {
        let msg = r#"{"timestamp":"2020-04-03T09:37:20.358120-0600","event_type":"stats","stats":{"uptime":8,"decoder":{"pkts":50900,"bytes":3999331,"invalid":0,"ipv4":49685,"ipv6":0,"ethernet":50900,"raw":0,"null":0,"sll":0,"tcp":44592,"udp":4629,"sctp":0,"icmpv4":464,"icmpv6":0,"ppp":0,"pppoe":0,"gre":0,"vlan":0,"vlan_qinq":0,"vxlan":0,"ieee8021ah":0,"teredo":0,"ipv4_in_ipv6":0,"ipv6_in_ipv6":0,"mpls":0,"avg_pkt_size":78,"max_pkt_size":153,"erspan":0,"dce":{"pkt_too_small":0}},"flow":{"memcap":0,"tcp":3152,"udp":425,"icmpv4":0,"icmpv6":0,"spare":0,"emerg_mode_entered":0,"emerg_mode_over":0,"tcp_reuse":0,"memuse":9076480},"defrag":{"ipv4":{"fragments":0,"reassembled":0,"timeouts":0},"ipv6":{"fragments":0,"reassembled":0,"timeouts":0},"max_frag_hits":0},"true":{"ipv4":{"pkt_too_small":0,"hlen_too_small":0,"iplen_smaller_than_hlen":0,"trunc_pkt":0,"opt_invalid":0,"opt_invalid_len":0,"opt_malformed":0,"opt_pad_required":0,"opt_eol_required":0,"opt_duplicate":0,"opt_unknown":0,"wrong_ip_version":0,"icmpv6":0,"frag_pkt_too_large":0,"frag_overlap":0,"frag_ignored":0},"icmpv4":{"pkt_too_small":0,"unknown_type":0,"unknown_code":0,"ipv4_trunc_pkt":0,"ipv4_unknown_ver":0},"icmpv6":{"unknown_type":0,"unknown_code":0,"pkt_too_small":0,"ipv6_unknown_version":0,"ipv6_trunc_pkt":0,"mld_message_with_invalid_hl":0,"unassigned_type":0,"experimentation_type":0},"ipv6":{"pkt_too_small":0,"trunc_pkt":0,"trunc_exthdr":0,"exthdr_dupl_fh":0,"exthdr_useless_fh":0,"exthdr_dupl_rh":0,"exthdr_dupl_hh":0,"exthdr_dupl_dh":0,"exthdr_dupl_ah":0,"exthdr_dupl_eh":0,"exthdr_invalid_optlen":0,"wrong_ip_version":0,"exthdr_ah_res_not_null":0,"hopopts_unknown_opt":0,"hopopts_only_padding":0,"dstopts_unknown_opt":0,"dstopts_only_padding":0,"rh_type_0":0,"zero_len_padn":0,"fh_non_zero_reserved_field":0,"data_after_none_header":0,"unknown_next_header":0,"icmpv4":0,"frag_pkt_too_large":0,"frag_overlap":0,"frag_ignored":0,"ipv4_in_ipv6_too_small":0,"ipv4_in_ipv6_wrong_version":0,"ipv6_in_ipv6_too_small":0,"ipv6_in_ipv6_wrong_version":0},"tcp":{"pkt_too_small":0,"hlen_too_small":0,"invalid_optlen":0,"opt_invalid_len":0,"opt_duplicate":0},"udp":{"pkt_too_small":0,"hlen_too_small":0,"hlen_invalid":0},"sll":{"pkt_too_small":0},"ethernet":{"pkt_too_small":0},"ppp":{"pkt_too_small":0,"vju_pkt_too_small":0,"ip4_pkt_too_small":0,"ip6_pkt_too_small":0,"wrong_type":0,"unsup_proto":0},"pppoe":{"pkt_too_small":0,"wrong_code":0,"malformed_tags":0},"gre":{"pkt_too_small":0,"wrong_version":0,"version0_recur":0,"version0_flags":0,"version0_hdr_too_big":0,"version0_malformed_sre_hdr":0,"version1_chksum":0,"version1_route":0,"version1_ssr":0,"version1_recur":0,"version1_flags":0,"version1_no_key":0,"version1_wrong_protocol":0,"version1_malformed_sre_hdr":0,"version1_hdr_too_big":0},"vlan":{"header_too_small":0,"unknown_type":0,"too_many_layers":0},"ieee8021ah":{"header_too_small":0},"ipraw":{"invalid_ip_version":0},"ltnull":{"pkt_too_small":0,"unsupported_type":0},"sctp":{"pkt_too_small":0},"mpls":{"header_too_small":0,"pkt_too_small":0,"bad_label_router_alert":0,"bad_label_implicit_null":0,"bad_label_reserved":0,"unknown_payload_type":0},"erspan":{"header_too_small":0,"unsupported_version":0,"too_many_vlan_layers":0}},"flow_bypassed":{"local_pkts":0,"local_bytes":0,"local_capture_pkts":0,"local_capture_bytes":0,"closed":0,"pkts":0,"bytes":0},"tcp":{"sessions":3150,"ssn_memcap_drop":0,"pseudo":0,"pseudo_failed":0,"invalid_checksum":0,"no_flow":0,"syn":3067,"synack":3067,"rst":4040,"midstream_pickups":83,"pkt_on_wrong_thread":0,"segment_memcap_drop":0,"stream_depth_reached":0,"reassembly_gap":2,"overlap":6283,"overlap_diff_data":0,"insert_data_normal_fail":0,"insert_data_overlap_fail":0,"insert_list_fail":0,"memuse":6451200,"reassembly_memuse":1458220},"detect":{"engines":[{"id":0,"last_reload":"2020-04-03T09:37:12.330695-0600","rules_loaded":21,"rules_failed":0}],"alert":0},"app_layer":{"flow":{"http":0,"ftp":0,"smtp":0,"tls":0,"ssh":0,"imap":0,"smb":0,"dcerpc_tcp":0,"dns_tcp":0,"nfs_tcp":0,"ntp":1,"ftp-data":0,"tftp":0,"ikev2":0,"krb5_tcp":0,"dhcp":0,"snmp":0,"rfb":0,"failed_tcp":2,"dcerpc_udp":0,"dns_udp":423,"nfs_udp":0,"krb5_udp":0,"failed_udp":1},"tx":{"http":0,"ftp":0,"smtp":0,"tls":0,"ssh":0,"imap":0,"smb":0,"dcerpc_tcp":0,"dns_tcp":0,"nfs_tcp":0,"ntp":1,"ftp-data":0,"tftp":0,"ikev2":0,"krb5_tcp":0,"dhcp":0,"snmp":0,"rfb":0,"dcerpc_udp":0,"dns_udp":4620,"nfs_udp":0,"krb5_udp":0},"expectations":0},"flow_mgr":{"closed_pruned":0,"new_pruned":0,"est_pruned":0,"bypassed_pruned":0,"flows_checked":0,"flows_notimeout":0,"flows_timeout":0,"flows_timeout_inuse":0,"flows_removed":0,"rows_checked":0,"rows_skipped":0,"rows_empty":0,"rows_busy":0,"rows_maxlen":0},"http":{"memuse":0,"memcap":0},"ftp":{"memuse":0,"memcap":0}}}"#;

        let eve = Message::try_from(msg.as_bytes().as_ref()).expect("Failed to read eve message");

        let expected_timestamp = Utc
            .ymd(2020, 4, 3)
            .and_hms(15, 37, 20)
            .with_nanosecond(358120000)
            .expect("Invalid time");

        assert_eq!(eve.timestamp, expected_timestamp);

        if let EventType::Stats(stats) = eve.event {
            assert_eq!(stats.info.decoder.pkts, 50_900)
        } else {
            panic!("Not stats")
        }
    }

    #[test]
    fn should_deserialize_eve_flow() {
        let msg = r#"{"timestamp":"1969-12-31T17:00:00.000000-0700","flow_id":1042873772049837,"event_type":"flow","src_ip":"10.10.10.30","src_port":57656,"dest_ip":"10.10.10.10","dest_port":102,"proto":"TCP","flow":{"pkts_toserver":3,"pkts_toclient":8,"bytes_toserver":186,"bytes_toclient":480,"start":"2015-10-20T11:24:10.230829-0600","end":"2015-10-20T11:24:46.195059-0600","age":36,"state":"closed","reason":"shutdown","alerted":false},"tcp":{"tcp_flags":"16","tcp_flags_ts":"16","tcp_flags_tc":"16","syn":true,"rst":true,"ack":true,"state":"closed"}}"#;

        let eve = Message::try_from(msg.as_bytes().as_ref()).expect("Failed to read eve message");

        if let EventType::Flow(flow) = eve.event {
            assert_eq!(
                flow.event_fields.src_ip,
                std::net::IpAddr::V4(std::net::Ipv4Addr::new(10, 10, 10, 30))
            );
            assert_eq!(flow.event_fields.src_port.unwrap(), 57_656);
            assert_eq!(flow.event_fields.proto.as_str(), "TCP");
            assert!(flow.event_fields.app_proto.is_none());
            let expected_timestamp = Utc
                .ymd(2015, 10, 20)
                .and_hms(17, 24, 10)
                .with_nanosecond(230829000)
                .expect("Invalid time");

            assert_eq!(flow.info.start, expected_timestamp);

            assert_eq!(flow.info.pkts_toserver, 3);
        } else {
            panic!("Not flow");
        }
    }

    #[test]
    fn should_deserialize_eve_icmp_flow() {
        let msg = r#"{"timestamp":"2016-06-16T15:07:24.422364-0600","flow_id":1980961674612149,"event_type":"flow","src_ip":"fe80:0000:0000:0000:7836:ddff:fe67:941f","dest_ip":"ff02:0000:0000:0000:0000:0000:0000:0002","proto":"IPV6-ICMP","icmp_type":133,"icmp_code":0,"flow":{"pkts_toserver":3,"pkts_toclient":0,"bytes_toserver":210,"bytes_toclient":0,"start":"2016-06-16T15:06:53.839093-0600","end":"2016-06-16T15:07:01.859044-0600","age":8,"state":"new","reason":"shutdown","alerted":false},"community_id":"1:ZJ+Tb/l5rNs6uZNnjO05XBTBCYE="}"#;

        let eve = Message::try_from(msg.as_bytes().as_ref()).expect("Failed to read eve message");

        if let EventType::Flow(flow) = eve.event {
            assert_eq!(
                flow.event_fields.src_ip,
                std::net::IpAddr::V6("fe80:0000:0000:0000:7836:ddff:fe67:941f".parse().unwrap())
            );
            assert!(flow.event_fields.src_port.is_none());
            assert_eq!(flow.event_fields.proto.as_str(), "IPV6-ICMP");
            assert!(flow.event_fields.app_proto.is_none());
            let expected_timestamp = Utc
                .ymd(2016, 6, 16)
                .and_hms(21, 6, 53)
                .with_nanosecond(839093000)
                .expect("Invalid time");

            assert_eq!(flow.info.start, expected_timestamp);

            assert_eq!(flow.info.pkts_toserver, 3);
        } else {
            panic!("Not flow");
        }
    }

    #[test]
    fn should_deserialize_eve_flow_with_community_id() {
        let msg = r#"{"timestamp":"1969-12-31T17:00:00.000000-0700","flow_id":1042873772049837,"event_type":"flow","src_ip":"10.10.10.30","src_port":57656,"dest_ip":"10.10.10.10","dest_port":102,"proto":"TCP","flow":{"pkts_toserver":3,"pkts_toclient":8,"bytes_toserver":186,"bytes_toclient":480,"start":"2015-10-20T11:24:10.230829-0600","end":"2015-10-20T11:24:46.195059-0600","age":36,"state":"closed","reason":"shutdown","alerted":false},"tcp":{"tcp_flags":"16","tcp_flags_ts":"16","tcp_flags_tc":"16","syn":true,"rst":true,"ack":true,"state":"closed"},"community_id":"1:3ZcLCpqiJpUyBlL6cvSAzm4Cn94="}"#;

        let eve = Message::try_from(msg.as_bytes().as_ref()).expect("Failed to read eve message");

        if let EventType::Flow(flow) = eve.event {
            assert_eq!(
                flow.event_fields.src_ip,
                std::net::IpAddr::V4(std::net::Ipv4Addr::new(10, 10, 10, 30))
            );
            assert_eq!(flow.event_fields.src_port.unwrap(), 57_656);
            assert_eq!(flow.event_fields.proto.as_str(), "TCP");
            assert!(flow.event_fields.app_proto.is_none());
            let expected_timestamp = Utc
                .ymd(2015, 10, 20)
                .and_hms(17, 24, 10)
                .with_nanosecond(230829000)
                .expect("Invalid time");

            assert_eq!(flow.info.start, expected_timestamp);

            assert_eq!(flow.info.pkts_toserver, 3);
            assert_eq!(
                flow.event_fields.community_id,
                Some("1:3ZcLCpqiJpUyBlL6cvSAzm4Cn94=".to_owned())
            );
        } else {
            panic!("Not stats");
        }
    }

    #[test]
    fn should_deserialize_eve_open_flow() {
        let msg = r#"{"timestamp":"1969-12-31T17:00:00.000000-0700","flow_id":1005493326901652,"event_type":"flow","src_ip":"192.168.89.2","src_port":27554,"dest_ip":"8.8.8.8","dest_port":53,"proto":"UDP","app_proto":"dns","flow":{"pkts_toserver":1,"pkts_toclient":1,"bytes_toserver":69,"bytes_toclient":97,"start":"2015-10-20T14:48:18.979348-0600","end":"2015-10-20T14:48:18.979458-0600","age":0,"state":"new","reason":"shutdown","alerted":false}}"#;

        let eve = Message::try_from(msg.as_bytes().as_ref()).expect("Failed to read eve message");

        if let EventType::Flow(flow) = eve.event {
            assert_eq!(
                flow.event_fields.dest_ip,
                std::net::IpAddr::V4(std::net::Ipv4Addr::new(8, 8, 8, 8))
            );
            assert_eq!(flow.event_fields.dest_port.unwrap(), 53);
            assert_eq!(flow.event_fields.proto.as_str(), "UDP");
            assert_eq!(flow.event_fields.app_proto, Some("dns".to_owned()));

            let expected_timestamp = Utc
                .ymd(2015, 10, 20)
                .and_hms(20, 48, 18)
                .with_nanosecond(979458000)
                .expect("Invalid time");

            assert_eq!(flow.info.end, expected_timestamp);

            assert_eq!(flow.info.bytes_toserver, 69)
        } else {
            panic!("Not flow")
        }
    }

    #[test]
    fn should_decode_eve_dns() {
        let msg = r#"{"timestamp":"2015-10-20T16:07:21.584630-0600","flow_id":482657058359332,"event_type":"dns","src_ip":"192.168.88.61","src_port":949,"dest_ip":"192.168.88.1","dest_port":53,"proto":"UDP","community_id":"1:P7jixReUPBkrfEsrEJysVvyhqKc=","dns":{"version":2,"type":"answer","id":56281,"flags":"8185","qr":true,"rd":true,"ra":true,"rrname":"time.nist.gov","rrtype":"A","rcode":"REFUSED"}}"#;

        let eve = Message::try_from(msg.as_bytes().as_ref()).expect("Failed to read eve message");

        if let EventType::Dns(_d) = eve.event {
        } else {
            panic!("Not dns")
        }
    }

    #[test]
    fn should_decode_complete_stats() {
        let msg = r#"{"timestamp":"2020-07-30T11:02:35.845000-0600","event_type":"stats","stats":{"uptime":15,"decoder":{"pkts":0,"bytes":0,"invalid":0,"ipv4":0,"ipv6":0,"ethernet":0,"chdlc":0,"raw":0,"null":0,"sll":0,"tcp":0,"udp":0,"sctp":0,"icmpv4":0,"icmpv6":0,"ppp":0,"pppoe":0,"gre":0,"vlan":0,"vlan_qinq":0,"vxlan":0,"ieee8021ah":0,"teredo":0,"ipv4_in_ipv6":0,"ipv6_in_ipv6":0,"mpls":0,"avg_pkt_size":0,"max_pkt_size":0,"erspan":0,"event":{"ipv4":{"pkt_too_small":0,"hlen_too_small":0,"iplen_smaller_than_hlen":0,"trunc_pkt":0,"opt_invalid":0,"opt_invalid_len":0,"opt_malformed":0,"opt_pad_required":0,"opt_eol_required":0,"opt_duplicate":0,"opt_unknown":0,"wrong_ip_version":0,"icmpv6":0,"frag_pkt_too_large":0,"frag_overlap":0,"frag_ignored":0},"icmpv4":{"pkt_too_small":0,"unknown_type":0,"unknown_code":0,"ipv4_trunc_pkt":0,"ipv4_unknown_ver":0},"icmpv6":{"unknown_type":0,"unknown_code":0,"pkt_too_small":0,"ipv6_unknown_version":0,"ipv6_trunc_pkt":0,"mld_message_with_invalid_hl":0,"unassigned_type":0,"experimentation_type":0},"ipv6":{"pkt_too_small":0,"trunc_pkt":0,"trunc_exthdr":0,"exthdr_dupl_fh":0,"exthdr_useless_fh":0,"exthdr_dupl_rh":0,"exthdr_dupl_hh":0,"exthdr_dupl_dh":0,"exthdr_dupl_ah":0,"exthdr_dupl_eh":0,"exthdr_invalid_optlen":0,"wrong_ip_version":0,"exthdr_ah_res_not_null":0,"hopopts_unknown_opt":0,"hopopts_only_padding":0,"dstopts_unknown_opt":0,"dstopts_only_padding":0,"rh_type_0":0,"zero_len_padn":0,"fh_non_zero_reserved_field":0,"data_after_none_header":0,"unknown_next_header":0,"icmpv4":0,"frag_pkt_too_large":0,"frag_overlap":0,"frag_ignored":0,"ipv4_in_ipv6_too_small":0,"ipv4_in_ipv6_wrong_version":0,"ipv6_in_ipv6_too_small":0,"ipv6_in_ipv6_wrong_version":0},"tcp":{"pkt_too_small":0,"hlen_too_small":0,"invalid_optlen":0,"opt_invalid_len":0,"opt_duplicate":0},"udp":{"pkt_too_small":0,"hlen_too_small":0,"hlen_invalid":0},"sll":{"pkt_too_small":0},"ethernet":{"pkt_too_small":0},"ppp":{"pkt_too_small":0,"vju_pkt_too_small":0,"ip4_pkt_too_small":0,"ip6_pkt_too_small":0,"wrong_type":0,"unsup_proto":0},"pppoe":{"pkt_too_small":0,"wrong_code":0,"malformed_tags":0},"gre":{"pkt_too_small":0,"wrong_version":0,"version0_recur":0,"version0_flags":0,"version0_hdr_too_big":0,"version0_malformed_sre_hdr":0,"version1_chksum":0,"version1_route":0,"version1_ssr":0,"version1_recur":0,"version1_flags":0,"version1_no_key":0,"version1_wrong_protocol":0,"version1_malformed_sre_hdr":0,"version1_hdr_too_big":0},"vlan":{"header_too_small":0,"unknown_type":0,"too_many_layers":0},"ieee8021ah":{"header_too_small":0},"ipraw":{"invalid_ip_version":0},"ltnull":{"pkt_too_small":0,"unsupported_type":0},"sctp":{"pkt_too_small":0},"mpls":{"header_too_small":0,"pkt_too_small":0,"bad_label_router_alert":0,"bad_label_implicit_null":0,"bad_label_reserved":0,"unknown_payload_type":0},"erspan":{"header_too_small":0,"unsupported_version":0,"too_many_vlan_layers":0},"dce":{"pkt_too_small":0}}},"flow":{"memcap":0,"tcp":0,"udp":0,"icmpv4":0,"icmpv6":0,"spare":10000,"emerg_mode_entered":0,"emerg_mode_over":0,"tcp_reuse":0,"memuse":11910016},"defrag":{"ipv4":{"fragments":0,"reassembled":0,"timeouts":0},"ipv6":{"fragments":0,"reassembled":0,"timeouts":0},"max_frag_hits":0},"flow_bypassed":{"local_pkts":0,"local_bytes":0,"local_capture_pkts":0,"local_capture_bytes":0,"closed":0,"pkts":0,"bytes":0},"tcp":{"sessions":0,"ssn_memcap_drop":0,"pseudo":0,"pseudo_failed":0,"invalid_checksum":0,"no_flow":0,"syn":0,"synack":0,"rst":0,"midstream_pickups":0,"pkt_on_wrong_thread":0,"segment_memcap_drop":0,"stream_depth_reached":0,"reassembly_gap":0,"overlap":0,"overlap_diff_data":0,"insert_data_normal_fail":0,"insert_data_overlap_fail":0,"insert_list_fail":0,"memuse":537600,"reassembly_memuse":98304},"detect":{"engines":[{"id":0,"last_reload":"2020-07-30T11:02:20.833611-0600","rules_loaded":21,"rules_failed":0}],"alert":0},"app_layer":{"flow":{"http":0,"ftp":0,"smtp":0,"tls":0,"ssh":0,"imap":0,"smb":0,"dcerpc_tcp":0,"dns_tcp":0,"nfs_tcp":0,"ntp":0,"ftp-data":0,"tftp":0,"ikev2":0,"krb5_tcp":0,"dhcp":0,"snmp":0,"sip":0,"rfb":0,"rdp":0,"failed_tcp":0,"dcerpc_udp":0,"dns_udp":0,"nfs_udp":0,"krb5_udp":0,"failed_udp":0},"tx":{"http":0,"ftp":0,"smtp":0,"tls":0,"ssh":0,"imap":0,"smb":0,"dcerpc_tcp":0,"dns_tcp":0,"nfs_tcp":0,"ntp":0,"ftp-data":0,"tftp":0,"ikev2":0,"krb5_tcp":0,"dhcp":0,"snmp":0,"sip":0,"rfb":0,"rdp":0,"dcerpc_udp":0,"dns_udp":0,"nfs_udp":0,"krb5_udp":0},"expectations":0},"flow_mgr":{"closed_pruned":0,"new_pruned":0,"est_pruned":0,"bypassed_pruned":0,"flows_checked":0,"flows_notimeout":0,"flows_timeout":0,"flows_timeout_inuse":0,"flows_removed":0,"rows_checked":32768,"rows_skipped":32768,"rows_empty":0,"rows_busy":0,"rows_maxlen":0},"http":{"memuse":0,"memcap":0},"ftp":{"memuse":0,"memcap":0}}}"#;

        let eve = Message::try_from(msg.as_bytes().as_ref()).expect("Failed to read eve message");

        if let EventType::Stats(_s) = eve.event {
        } else {
            panic!("Not stats")
        }
    }

    #[test]
    fn should_decode_dns_query() {
        let msg = r#"{"timestamp":"2015-10-20T16:08:08.083366-0600","flow_id":2002523053901222,"event_type":"dns","src_ip":"192.168.89.2","src_port":36414,"dest_ip":"8.8.8.8","dest_port":53,"proto":"UDP","community_id":"1:6KH31DrKtXiYBf9dKMXt6n2rESo=","dns":{"type":"query","id":25510,"rrname":"localhost","rrtype":"A","tx_id":0}}"#;

        let eve = Message::try_from(msg.as_bytes().as_ref()).expect("Failed to read eve message");

        if let EventType::Dns(d) = eve.event {
            if let DnsEventType::Query(_d) = d.info.event {
            } else {
                panic!("Not query");
            }
        } else {
            panic!("Not dns");
        }
    }

    #[test]
    fn should_decode_dns_answer() {
        let msg = r#"{"timestamp":"2015-10-20T16:10:02.112993-0600","flow_id":2176658129130895,"event_type":"dns","src_ip":"192.168.88.61","src_port":949,"dest_ip":"192.168.88.1","dest_port":53,"proto":"UDP","community_id":"1:P7jixReUPBkrfEsrEJysVvyhqKc=","dns":{"version":2,"type":"answer","id":56361,"flags":"8185","qr":true,"rd":true,"ra":true,"rrname":"time.nist.gov","rrtype":"A","rcode":"REFUSED"}}"#;

        let eve = Message::try_from(msg.as_bytes().as_ref()).expect("Failed to read eve message");

        if let EventType::Dns(d) = eve.event {
            if let DnsEventType::Answer(_d) = d.info.event {
            } else {
                panic!("Not answer");
            }
        } else {
            panic!("Not dns")
        }
    }

    #[test]
    fn should_decode_file() {
        let msg = r#"{"timestamp":"2016-06-16T15:07:06.802717-0600","flow_id":2180082800938789,"event_type":"fileinfo","src_ip":"10.3.1.1","src_port":445,"dest_ip":"10.3.1.2","dest_port":56746,"proto":"TCP","smb":{"id":27,"dialect":"2.02","command":"SMB2_COMMAND_READ","status":"STATUS_END_OF_FILE","status_code":"0xc0000011","session_id":706141969,"tree_id":201977730,"filename":"file69.txt","share":"\\\\10.3.1.1\\public","fuid":"96c8e081-0000-0000-441e-a47e00000000"},"app_proto":"smb","fileinfo":{"filename":"file69.txt","sid":[],"gaps":false,"state":"CLOSED","stored":false,"size":3109,"tx_id":26}}"#;

        let eve = Message::try_from(msg.as_bytes().as_ref()).expect("Failed to read eve message");

        if let EventType::File(v) = eve.event {
            assert!(!v.info.filename.is_empty());
            assert!(!v.info.stored);
        } else {
            panic!("Not http")
        }
    }

    #[test]
    fn should_decode_file_with_missing_name() {
        let msg = r#"{"timestamp":"2016-06-16T15:07:06.802717-0600","flow_id":2180082800938789,"event_type":"fileinfo","src_ip":"10.3.1.1","src_port":445,"dest_ip":"10.3.1.2","dest_port":56746,"proto":"TCP","smb":{"id":27,"dialect":"2.02","command":"SMB2_COMMAND_READ","status":"STATUS_END_OF_FILE","status_code":"0xc0000011","session_id":706141969,"tree_id":201977730,"filename":"","share":"","fuid":"96c8e081-0000-0000-441e-a47e00000000"},"app_proto":"smb","fileinfo":{"sid":[],"gaps":false,"state":"CLOSED","stored":false,"size":3109,"tx_id":26}}"#;

        let eve = Message::try_from(msg.as_bytes().as_ref()).expect("Failed to read eve message");

        if let EventType::File(v) = eve.event {
            assert!(v.info.filename.is_empty());
            assert!(!v.info.stored);
        } else {
            panic!("Not http")
        }
    }

    #[test]
    fn should_decode_http() {
        let msg = r#"{"timestamp":"2020-08-05T13:32:29.341318+0000","flow_id":1925256485615034,"event_type":"http","src_ip":"16.0.0.1","src_port":41668,"dest_ip":"48.0.0.1","dest_port":80,"proto":"TCP","tx_id":0,"community_id":"1:p1ceBUuGcR8ILP4a2kUZp97NUQM=","http":{"hostname":"22.0.0.3","url":"/3384","http_user_agent":"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)","http_content_type":"text/html","request_headers":[{"name":"Host","value":"22.0.0.3"},{"name":"Connection","value":"Keep-Alive"},{"name":"User-Agent","value":"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)"},{"name":"Accept","value":"*/*"},{"name":"Accept-Language","value":"en-us"},{"name":"Accept-Encoding","value":"gzip, deflate, compress"}],"response_headers":[{"name":"Server","value":"Microsoft-IIS/6.0"},{"name":"Content-Type","value":"text/html"},{"name":"Content-Length","value":"32000"}]}}"#;

        let eve = Message::try_from(msg.as_bytes().as_ref()).expect("Failed to read eve message");

        if let EventType::Http(v) = eve.event {
            assert!(!v.info.request_headers.is_empty());
            assert!(!v.info.response_headers.is_empty());
        } else {
            panic!("Not http")
        }
    }

    #[test]
    fn should_decode_tls() {
        let msg = r#"{"timestamp":"2013-09-15T15:52:16.727495-0600","flow_id":1763151386252222,"event_type":"tls","src_ip":"127.0.0.1","src_port":38964,"dest_ip":"127.0.0.1","dest_port":4430,"proto":"TCP","community_id":"1:tO6mWiPaHrt1i+ROisC8pmK8jQ4=","tls":{"subject":"CN=Test Certificate RSA","issuerdn":"CN=Test Certificate RSA"}}"#;

        let eve = Message::try_from(msg.as_bytes().as_ref()).expect("Failed to read eve message");

        if let EventType::Tls(v) = eve.event {
            assert!(v.info.subject.is_some());
            assert!(v.info.issuerdn.is_some());
        } else {
            panic!("Not tls")
        }
    }
}
