decon-spf 0.3.3

This is a simple tool that allows you to deconstruct an existing SPF record that might be retreived in a normal DNS TXT lookup. With version 0.2.0 you can now also construct a new Spf record.
Documentation
use crate::core::{DNS_LOOKUP_LIMIT, MAX_SPF_STRING_LENGTH};
use crate::spf::validate::Validate;
use crate::{SpfBuilder, SpfError};

impl<State> Validate for SpfBuilder<State> {
    fn validate_length(&self) -> Result<(), SpfError> {
        let mut length = 0;
        length += self.version().len();
        for _m in self.iter() {
            length += 1 + _m.raw().len();
        }
        match length > MAX_SPF_STRING_LENGTH {
            true => Err(SpfError::SourceLengthExceeded),
            false => Ok(()),
        }
    }

    #[cfg(feature = "ptr")]
    fn validate_ptr(&self) -> Result<(), SpfError> {
        match self.ptr().is_none() {
            true => Ok(()),
            false => Err(SpfError::DeprecatedPtrDetected),
        }
    }

    fn validate_redirect_all(&self) -> Result<(), SpfError> {
        if self.redirect().is_some() && self.all().is_some() {
            Err(SpfError::RedirectWithAllMechanism)
        } else {
            Ok(())
        }
    }
    fn validate_lookup_count(&self) -> Result<(), SpfError> {
        match self.get_lookup_count() <= DNS_LOOKUP_LIMIT {
            true => Ok(()),
            false => Err(SpfError::LookupLimitExceeded),
        }
    }
}

#[cfg(test)]
#[cfg(feature = "builder")]
mod tests {
    use crate::mechanism::Qualifier;
    use crate::spf::validate::Validate;
    use crate::spf::Mechanism;
    use crate::{Builder, SpfBuilder, SpfError};
    use std::convert::TryInto;

    #[test]
    fn building() {
        let mut spf = SpfBuilder::new_builder();
        let input = ["a", "mx", "-all"];
        for m in input.iter() {
            spf.append_mechanism(m.parse::<Mechanism<String>>().unwrap());
        }
        spf.set_v1();
        assert_eq!(spf.version(), "v=spf1");
        assert!(spf.validate_length().is_ok());

        let mut spf2 = SpfBuilder::new_builder();
        spf2.set_v1().add_a(
            Mechanism::a(Qualifier::Pass)
                .with_rrdata("test.com")
                .unwrap(),
        );
        let spf2 = spf2.add_all(Mechanism::all());
        assert_eq!(spf2.to_string(), "v=spf1 a:test.com -all");
    }
    #[test]
    fn test_validate_version() {
        let mut spf: SpfBuilder<Builder> = SpfBuilder::default();
        spf.set_v1();
        assert!(spf.validate_version().is_ok());
    }
    #[test]
    fn test_validate_lookup_count_below_10() {
        let mut spf: SpfBuilder<Builder> = SpfBuilder::default();
        spf.set_v1();
        spf.append_mechanism("a".parse::<Mechanism<String>>().unwrap());
        spf.append_mechanism("mx".parse::<Mechanism<String>>().unwrap());
        spf.append_mechanism("mx:a.test.com".parse::<Mechanism<String>>().unwrap());
        spf.append_mechanism("mx:b.test.com".parse::<Mechanism<String>>().unwrap());
        spf.append_mechanism("mx:c.test.com".parse::<Mechanism<String>>().unwrap());
        spf.append_mechanism("mx:d.test.com".parse::<Mechanism<String>>().unwrap());
        spf.append_mechanism("mx:e.test.com".parse::<Mechanism<String>>().unwrap());
        spf.append_mechanism("mx:f.test.com".parse::<Mechanism<String>>().unwrap());
        spf.append_mechanism("mx:g.test.com".parse::<Mechanism<String>>().unwrap());
        assert!(matches!(spf.validate_lookup_count(), Ok(_)));
        let spf = spf.build().unwrap();
        assert_eq!(spf.lookup_count(), 9);
        assert_eq!(spf.source, "");
        assert_eq!(
            spf.to_string(),
            String::from("v=spf1 a mx mx:a.test.com mx:b.test.com mx:c.test.com mx:d.test.com mx:e.test.com mx:f.test.com mx:g.test.com")
        );
    }
    #[test]
    fn test_validate_lookup_count_above_10() {
        let mut spf: SpfBuilder<Builder> = SpfBuilder::default();
        spf.set_v1();
        spf.append_mechanism("a".parse::<Mechanism<String>>().unwrap());
        spf.append_mechanism("a:test.com".parse::<Mechanism<String>>().unwrap());
        spf.append_mechanism("mx".parse::<Mechanism<String>>().unwrap());
        spf.append_mechanism("mx:test.com".parse::<Mechanism<String>>().unwrap());
        spf.append_mechanism("mx:a.test.com".parse::<Mechanism<String>>().unwrap());
        spf.append_mechanism("mx:b.test.com".parse::<Mechanism<String>>().unwrap());
        spf.append_mechanism("mx:c.test.com".parse::<Mechanism<String>>().unwrap());
        spf.append_mechanism("mx:d.test.com".parse::<Mechanism<String>>().unwrap());
        spf.append_mechanism("mx:e.test.com".parse::<Mechanism<String>>().unwrap());
        spf.append_mechanism("mx:f.test.com".parse::<Mechanism<String>>().unwrap());
        spf.append_mechanism("mx:g.test.com".parse::<Mechanism<String>>().unwrap());
        spf.append_mechanism("mx:h.test.com".parse::<Mechanism<String>>().unwrap());
        assert!(matches!(spf.validate_lookup_count(), Err(_)));
    }
    #[test]
    fn test_length_ok() {
        let mut spf: SpfBuilder<Builder> = SpfBuilder::default();
        spf.set_v1();
        spf.append_mechanism("a".parse::<Mechanism<String>>().unwrap());
        spf.append_mechanism("mx".parse::<Mechanism<String>>().unwrap());
        let spf = spf.add_all(
            "-all"
                .parse::<Mechanism<String>>()
                .unwrap()
                .try_into()
                .expect("Should be All"),
        );
        assert_eq!(spf.validate_length(), Ok(()));
    }
    #[test]
    fn test_length_not_ok() {
        let mut spf: SpfBuilder<Builder> = SpfBuilder::default();
        spf.set_v1();
        spf.append_mechanism(
            "a:testaasdfadadadsfaefdasdfadsfaf.dasdfadfadfafasdfadsfadadsfadf.com"
                .parse::<Mechanism<String>>()
                .unwrap(),
        );
        spf.append_mechanism(
            "mx:sfgsfgsfgsbogusonetest.exsfgsfgsdfgsdgample.com"
                .parse::<Mechanism<String>>()
                .unwrap(),
        );
        spf.append_mechanism(
            "mx:bogusonet235435ersfdgsfgsfest.exa345345wrgsdfgsfsfmple.com"
                .parse::<Mechanism<String>>()
                .unwrap(),
        );
        spf.append_mechanism(
            "mx:adfalkjadsjflajdladbogusonetest.esfgsjlsjfdlsfgsfgxample.com"
                .parse::<Mechanism<String>>()
                .unwrap(),
        );
        spf.append_mechanism(
            "mx:bogusonetessdfgsfgsdfgsfgsdfgsdfgt.exasfgsfgsfdgsfdgsfgsfgmple.com"
                .parse::<Mechanism<String>>()
                .unwrap(),
        );
        spf.append_mechanism(
            "mx:bogusonetsfgs43sfsfsgs6sdgdest.examsfdgsfgsfsgfdgsfgple.com"
                .parse::<Mechanism<String>>()
                .unwrap(),
        );
        spf.append_mechanism(
            "mx:bogusonetesiadsfadft.adfadsfadexample.com"
                .parse::<Mechanism<String>>()
                .unwrap(),
        );
        spf.append_mechanism(
            "mx:bogusonetest123123132.adfffeasfexample.com"
                .parse::<Mechanism<String>>()
                .unwrap(),
        );
        spf.append_mechanism(
            "mx:bogusonetesadfadfwr134wadsft.eadfadfadfasdfasdfasdfaxample.com"
                .parse::<Mechanism<String>>()
                .unwrap(),
        );
        let spf = spf.add_all(
            "-all"
                .parse::<Mechanism<String>>()
                .unwrap()
                .try_into()
                .expect("Should be All"),
        );
        assert_eq!(spf.validate_length(), Err(SpfError::SourceLengthExceeded));
    }
    #[test]
    #[cfg(feature = "ptr")]
    fn test_ptr_ok() {
        let mut spf: SpfBuilder<Builder> = SpfBuilder::default();
        spf.set_v1();
        spf.append_mechanism("a".parse::<Mechanism<String>>().unwrap());
        let spf = spf.add_all(
            "-all"
                .parse::<Mechanism<String>>()
                .unwrap()
                .try_into()
                .expect("Should be All"),
        );
        assert_eq!(spf.validate_ptr(), Ok(()));
    }
    #[test]
    #[cfg(feature = "ptr")]
    fn test_ptr_not_ok() {
        let mut spf: SpfBuilder<Builder> = SpfBuilder::default();
        spf.set_v1();
        spf.append_mechanism("a".parse::<Mechanism<String>>().unwrap());
        spf.append_mechanism("ptr".parse::<Mechanism<String>>().unwrap());
        let spf = spf.add_all(
            "-all"
                .parse::<Mechanism<String>>()
                .unwrap()
                .try_into()
                .expect("Should be All"),
        );
        assert_eq!(spf.validate_ptr(), Err(SpfError::DeprecatedPtrDetected));
    }
    #[test]
    fn test_redirect_all_ok() {
        let mut spf = SpfBuilder::new_builder();
        spf.set_v1();
        spf.append_mechanism("a".parse::<Mechanism<String>>().unwrap());
        let spf = spf.add_all(
            "-all"
                .parse::<Mechanism<String>>()
                .unwrap()
                .try_into()
                .expect("Should be All"),
        );
        assert_eq!(spf.validate_redirect_all(), Ok(()));
    }
    #[test]
    fn test_redirect_all_not_ok() {
        let mut spf: SpfBuilder<Builder> = SpfBuilder::default();
        spf.set_v1();
        spf.append_mechanism("a".parse::<Mechanism<String>>().unwrap());
        spf.append_mechanism(
            "redirect=test.example.com"
                .parse::<Mechanism<String>>()
                .unwrap(),
        );
        spf.append_mechanism("-all".parse::<Mechanism<String>>().unwrap());
        assert_eq!(
            spf.validate_redirect_all(),
            Err(SpfError::RedirectWithAllMechanism)
        );
    }
}