nullnet-firewall 0.2.2

Rust-based firewall for network drivers
Documentation
use std::fmt::{Display, Formatter};
use std::net::IpAddr;
use std::str::FromStr;

use rusqlite::types::{FromSql, FromSqlResult, ToSqlOutput, ValueRef};
use rusqlite::ToSql;

#[derive(PartialEq, Debug, Clone)]
pub(crate) struct LogIp {
    ip: IpAddr,
}

impl LogIp {
    pub(crate) fn from_ip_addr(ip_addr: Option<IpAddr>) -> Option<LogIp> {
        ip_addr.map(|ip| LogIp { ip })
    }
}

impl ToSql for LogIp {
    fn to_sql(&self) -> rusqlite::Result<ToSqlOutput<'_>> {
        Ok(self.to_string().into())
    }
}

impl FromSql for LogIp {
    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
        FromSqlResult::Ok(LogIp {
            ip: IpAddr::from_str(value.as_str().unwrap()).unwrap(),
        })
    }
}

impl Display for LogIp {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.ip)
    }
}

#[cfg(test)]
mod tests {
    use std::net::IpAddr;
    use std::str::FromStr;

    use rusqlite::types::ToSqlOutput;
    use rusqlite::types::Value::Text;
    use rusqlite::ToSql;

    use crate::logs::log_ip::LogIp;

    #[test]
    fn test_log_ip_from_ip_addr() {
        assert_eq!(LogIp::from_ip_addr(None), None);

        assert_eq!(
            LogIp::from_ip_addr(Some(IpAddr::from_str("1.2.3.4").unwrap())),
            Some(LogIp {
                ip: IpAddr::from_str("1.2.3.4").unwrap()
            })
        );
    }

    #[test]
    fn test_log_ip_to_sql() {
        assert_eq!(
            LogIp::to_sql(&LogIp {
                ip: IpAddr::from_str("8.8.8.8").unwrap()
            }),
            Ok(ToSqlOutput::Owned(Text("8.8.8.8".to_string())))
        );
    }

    #[test]
    fn test_format_ipv4_address() {
        let result = IpAddr::from_str("192.168.1.1").unwrap().to_string();
        assert_eq!(result, "192.168.1.1".to_string());
    }

    #[test]
    fn test_format_ipv6_address() {
        let result = IpAddr::from_str("2001:db8:3333:4444:CCCC:DDDD:EEEE:FFFF")
            .unwrap()
            .to_string();
        assert_eq!(result, "2001:db8:3333:4444:cccc:dddd:eeee:ffff".to_string());
    }

    #[test]
    fn ipv6_simple_test() {
        let result = IpAddr::from([
            255, 10, 10, 255, 255, 10, 10, 255, 255, 10, 10, 255, 255, 10, 10, 255,
        ])
        .to_string();
        assert_eq!(result, "ff0a:aff:ff0a:aff:ff0a:aff:ff0a:aff".to_string());
    }

    #[test]
    fn ipv6_zeros_in_the_middle() {
        let result =
            IpAddr::from([255, 10, 10, 255, 0, 0, 0, 0, 28, 4, 4, 28, 255, 1, 0, 0]).to_string();
        assert_eq!(result, "ff0a:aff::1c04:41c:ff01:0".to_string());
    }

    #[test]
    fn ipv6_leading_zeros() {
        let result =
            IpAddr::from([0, 0, 0, 0, 0, 0, 0, 0, 28, 4, 4, 28, 255, 1, 0, 10]).to_string();
        assert_eq!(result, "::1c04:41c:ff01:a".to_string());
    }

    #[test]
    fn ipv6_tail_one_after_zeros() {
        let result =
            IpAddr::from([28, 4, 4, 28, 255, 1, 0, 10, 0, 0, 0, 0, 0, 0, 0, 1]).to_string();
        assert_eq!(result, "1c04:41c:ff01:a::1".to_string());
    }

    #[test]
    fn ipv6_tail_zeros() {
        let result =
            IpAddr::from([28, 4, 4, 28, 255, 1, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0]).to_string();
        assert_eq!(result, "1c04:41c:ff01:a::".to_string());
    }

    #[test]
    fn ipv6_multiple_zero_sequences_first_longer() {
        let result = IpAddr::from([32, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1]).to_string();
        assert_eq!(result, "2000::101:0:0:1".to_string());
    }

    #[test]
    fn ipv6_multiple_zero_sequences_first_longer_head() {
        let result = IpAddr::from([0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1]).to_string();
        assert_eq!(result, "::101:0:0:1".to_string());
    }

    #[test]
    fn ipv6_multiple_zero_sequences_second_longer() {
        let result = IpAddr::from([1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 3, 118]).to_string();
        assert_eq!(result, "100:0:0:1::376".to_string());
    }

    #[test]
    fn ipv6_multiple_zero_sequences_second_longer_tail() {
        let result = IpAddr::from([32, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0]).to_string();
        assert_eq!(result, "2000:0:0:1:101::".to_string());
    }

    #[test]
    fn ipv6_multiple_zero_sequences_equal_length() {
        let result = IpAddr::from([118, 3, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1]).to_string();
        assert_eq!(result, "7603::1:101:0:0:1".to_string());
    }

    #[test]
    fn ipv6_all_zeros() {
        let result = IpAddr::from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]).to_string();
        assert_eq!(result, "::".to_string());
    }

    #[test]
    fn ipv6_x_all_zeros() {
        let result = IpAddr::from([161, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]).to_string();
        assert_eq!(result, "a100::".to_string());
    }

    #[test]
    fn ipv6_all_zeros_x() {
        let result = IpAddr::from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176]).to_string();
        assert_eq!(result, "::b0".to_string());
    }

    #[test]
    fn ipv6_many_zeros_but_no_compression() {
        let result = IpAddr::from([0, 16, 16, 0, 0, 1, 7, 0, 0, 2, 216, 0, 1, 0, 0, 1]).to_string();
        assert_eq!(result, "10:1000:1:700:2:d800:100:1".to_string());
    }
}