use crate::spf::mechanism::Kind;
use crate::spf::mechanism::MechanismError;
use ipnetwork::IpNetworkError;
#[derive(Debug, Clone, PartialEq)]
pub enum SpfError {
InvalidSource,
InvalidVersion,
SourceLengthExceeded,
LookupLimitExceeded,
HasNotBeenParsed,
WhiteSpaceSyntaxError,
InvalidSPF,
RedirectWithAllMechanism,
RedirectNotFinalMechanism,
ModifierMayOccurOnlyOnce(Kind),
InvalidIPAddr(IpNetworkError),
InvalidMechanism(MechanismError),
DeprecatedPtrDetected,
}
impl std::fmt::Display for SpfError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SpfError::InvalidSource => write!(f, "Source string not valid."),
SpfError::InvalidVersion => write!(f, "Version string not valid."),
SpfError::SourceLengthExceeded => write!(f, "Spf record exceeds 512 characters."),
SpfError::LookupLimitExceeded => write!(f, "Too many DNS lookups."),
SpfError::HasNotBeenParsed => write!(f, "Source string has not been parsed."),
SpfError::WhiteSpaceSyntaxError => {
write!(
f,
"Spf contains two or more consecutive whitespace characters."
)
}
SpfError::InvalidSPF => write!(f, "Spf record is invalid."),
SpfError::RedirectWithAllMechanism => {
write!(f, "Spf record contains both a 'REDIRECT' modifier and 'ALL' mechanism.\nAccording to RFC7208 any redirect MUST be ignored in this case.\n[See Section 5.1](https://datatracker.ietf.org/doc/html/rfc7208#section-5.1)")
}
SpfError::RedirectNotFinalMechanism => write!(f, "Redirect not last mechanism."),
SpfError::ModifierMayOccurOnlyOnce(kind) => write!(f, "Mechanism: {} occurred more than once.", kind),
SpfError::InvalidIPAddr(err) => write!(f, "{}", err),
SpfError::InvalidMechanism(err) => write!(f, "{}", err),
SpfError::DeprecatedPtrDetected => write!(
f,
"Deprecated Ptr mechanism detected.\nThe use of this mechanism is highly discouraged"
),
}
}
}
impl From<IpNetworkError> for SpfError {
fn from(err: IpNetworkError) -> Self {
SpfError::InvalidIPAddr(err)
}
}
impl From<MechanismError> for SpfError {
fn from(err: MechanismError) -> Self {
SpfError::InvalidMechanism(err)
}
}
impl std::error::Error for SpfError {}
impl SpfError {
pub fn is_spf_error(&self) -> bool {
matches!(self, Self::InvalidSource)
|| matches!(self, Self::InvalidVersion)
|| matches!(self, Self::SourceLengthExceeded)
|| matches!(self, Self::LookupLimitExceeded)
|| matches!(self, Self::HasNotBeenParsed)
|| matches!(self, Self::InvalidSPF)
|| matches!(self, Self::RedirectWithAllMechanism)
|| matches!(self, Self::InvalidIPAddr(_))
}
pub fn is_invalid_source(&self) -> bool {
matches!(self, Self::InvalidSource)
}
pub fn source_is_invalid(&self) -> bool {
matches!(self, Self::InvalidSource)
}
pub fn version_is_invalid(&self) -> bool {
matches!(self, Self::InvalidVersion)
}
pub fn is_source_length_exceeded(&self) -> bool {
matches!(self, Self::SourceLengthExceeded)
}
pub fn source_length_exceeded(&self) -> bool {
matches!(self, Self::SourceLengthExceeded)
}
pub fn is_lookup_limit_exceeded(&self) -> bool {
matches!(self, Self::LookupLimitExceeded)
}
pub fn lookup_limit_exceeded(&self) -> bool {
matches!(self, Self::LookupLimitExceeded)
}
pub fn is_has_not_been_parsed(&self) -> bool {
matches!(self, Self::HasNotBeenParsed)
}
pub fn has_not_been_parsed(&self) -> bool {
matches!(self, Self::HasNotBeenParsed)
}
pub fn is_invalid_spf(&self) -> bool {
matches!(self, Self::InvalidSPF)
}
pub fn is_redirect_with_all_mechanism(&self) -> bool {
matches!(self, Self::RedirectWithAllMechanism)
}
pub fn is_invalid_ip_addr(&self) -> bool {
matches!(
self,
Self::InvalidMechanism(MechanismError::InvalidIPNetwork(_))
)
}
}
#[derive(Debug, Default, Clone)]
pub struct SpfErrors {
errors: Vec<SpfError>,
source: String,
}
#[allow(dead_code)]
impl SpfErrors {
pub(crate) fn new() -> Self {
Self {
errors: Vec::new(),
source: String::new(),
}
}
pub(crate) fn register_error(&mut self, error: SpfError) {
self.errors.push(error);
}
pub(crate) fn register_source(&mut self, source: String) {
self.source = source;
}
pub fn source(&self) -> &String {
&self.source
}
pub fn errors(&self) -> &Vec<SpfError> {
self.errors.as_ref()
}
}
#[test]
fn create_spf_errors() {
let errors = SpfErrors::new();
assert_eq!(errors.errors.len(), 0);
}
#[test]
fn is_any_spf_error() {
let err = SpfError::InvalidSource;
assert_eq!(err.is_spf_error(), true);
}
#[test]
fn is_invalid_source() {
let err = SpfError::InvalidSource;
assert_eq!(err.is_invalid_source(), true);
}
#[test]
fn is_source_length_exceeded() {
let err = SpfError::SourceLengthExceeded;
assert_eq!(err.is_source_length_exceeded(), true);
}
#[test]
fn is_lookup_limit_exceeded() {
let err = SpfError::LookupLimitExceeded;
assert_eq!(err.is_lookup_limit_exceeded(), true)
}
#[test]
fn is_has_not_been_parsed() {
let err = SpfError::HasNotBeenParsed;
assert_eq!(err.is_has_not_been_parsed(), true)
}
#[test]
fn is_invalid_spf() {
let err = SpfError::InvalidSPF;
assert_eq!(err.is_invalid_spf(), true)
}
#[test]
fn is_redirect_with_all_mechanism() {
let err = SpfError::RedirectWithAllMechanism;
assert_eq!(err.is_redirect_with_all_mechanism(), true)
}