1use crate::spf::mechanism::Kind;
2use crate::spf::mechanism::MechanismError;
3use ipnetwork::IpNetworkError;
4
5#[derive(Debug, Clone, PartialEq)]
7pub enum SpfError {
8 InvalidSource,
10 InvalidVersion,
12 SourceLengthExceeded,
14 LookupLimitExceeded,
16 HasNotBeenParsed,
18 WhiteSpaceSyntaxError,
21 InvalidSPF,
23 RedirectWithAllMechanism,
27 RedirectNotFinalMechanism,
29 ModifierMayOccurOnlyOnce(Kind),
31 InvalidIPAddr(IpNetworkError),
33 InvalidMechanism(MechanismError),
35 DeprecatedPtrDetected,
39}
40
41impl std::fmt::Display for SpfError {
42 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43 match self {
44 SpfError::InvalidSource => write!(f, "Source string not valid."),
45 SpfError::InvalidVersion => write!(f, "Version string not valid."),
46 SpfError::SourceLengthExceeded => write!(f, "Spf record exceeds 512 characters."),
47 SpfError::LookupLimitExceeded => write!(f, "Too many DNS lookups."),
48 SpfError::HasNotBeenParsed => write!(f, "Source string has not been parsed."),
49 SpfError::WhiteSpaceSyntaxError => {
50 write!(
51 f,
52 "Spf contains two or more consecutive whitespace characters."
53 )
54 }
55 SpfError::InvalidSPF => write!(f, "Spf record is invalid."),
56 SpfError::RedirectWithAllMechanism => {
57 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)")
58 }
59 SpfError::RedirectNotFinalMechanism => write!(f, "Redirect not last mechanism."),
60 SpfError::ModifierMayOccurOnlyOnce(kind) => write!(f, "Mechanism: {} occurred more than once.", kind),
61 SpfError::InvalidIPAddr(err) => write!(f, "{}", err),
63 SpfError::InvalidMechanism(err) => write!(f, "{}", err),
64 SpfError::DeprecatedPtrDetected => write!(
65 f,
66 "Deprecated Ptr mechanism detected.\nThe use of this mechanism is highly discouraged"
67 ),
68 }
69 }
70}
71
72impl From<IpNetworkError> for SpfError {
73 fn from(err: IpNetworkError) -> Self {
74 SpfError::InvalidIPAddr(err)
75 }
76}
77
78impl From<MechanismError> for SpfError {
79 fn from(err: MechanismError) -> Self {
81 SpfError::InvalidMechanism(err)
82 }
83}
84impl std::error::Error for SpfError {}
85
86impl SpfError {
87 pub fn is_spf_error(&self) -> bool {
89 matches!(self, Self::InvalidSource)
90 || matches!(self, Self::InvalidVersion)
91 || matches!(self, Self::SourceLengthExceeded)
92 || matches!(self, Self::LookupLimitExceeded)
93 || matches!(self, Self::HasNotBeenParsed)
94 || matches!(self, Self::InvalidSPF)
95 || matches!(self, Self::RedirectWithAllMechanism)
96 || matches!(self, Self::InvalidIPAddr(_))
97 }
98 pub fn is_invalid_source(&self) -> bool {
100 matches!(self, Self::InvalidSource)
101 }
102 pub fn source_is_invalid(&self) -> bool {
104 matches!(self, Self::InvalidSource)
105 }
106 pub fn version_is_invalid(&self) -> bool {
108 matches!(self, Self::InvalidVersion)
109 }
110 pub fn is_source_length_exceeded(&self) -> bool {
112 matches!(self, Self::SourceLengthExceeded)
113 }
114 pub fn source_length_exceeded(&self) -> bool {
116 matches!(self, Self::SourceLengthExceeded)
117 }
118 pub fn is_lookup_limit_exceeded(&self) -> bool {
120 matches!(self, Self::LookupLimitExceeded)
121 }
122 pub fn lookup_limit_exceeded(&self) -> bool {
124 matches!(self, Self::LookupLimitExceeded)
125 }
126 pub fn is_has_not_been_parsed(&self) -> bool {
128 matches!(self, Self::HasNotBeenParsed)
129 }
130 pub fn has_not_been_parsed(&self) -> bool {
132 matches!(self, Self::HasNotBeenParsed)
133 }
134 pub fn is_invalid_spf(&self) -> bool {
136 matches!(self, Self::InvalidSPF)
137 }
138 pub fn is_redirect_with_all_mechanism(&self) -> bool {
140 matches!(self, Self::RedirectWithAllMechanism)
141 }
142 pub fn is_invalid_ip_addr(&self) -> bool {
144 matches!(
145 self,
146 Self::InvalidMechanism(MechanismError::InvalidIPNetwork(_))
147 )
148 }
149}
150#[derive(Debug, Default, Clone)]
153pub struct SpfErrors {
154 errors: Vec<SpfError>,
155 source: String,
156}
157
158#[allow(dead_code)]
159impl SpfErrors {
160 pub(crate) fn new() -> Self {
161 Self {
162 errors: Vec::new(),
163 source: String::new(),
164 }
165 }
166 pub(crate) fn register_error(&mut self, error: SpfError) {
167 self.errors.push(error);
168 }
169 pub(crate) fn register_source(&mut self, source: String) {
170 self.source = source;
171 }
172 pub fn source(&self) -> &String {
174 &self.source
175 }
176 pub fn errors(&self) -> &Vec<SpfError> {
178 self.errors.as_ref()
179 }
180}
181
182#[test]
183fn create_spf_errors() {
184 let errors = SpfErrors::new();
185 assert_eq!(errors.errors.len(), 0);
186}
187#[test]
188fn is_any_spf_error() {
189 let err = SpfError::InvalidSource;
190 assert_eq!(err.is_spf_error(), true);
191}
192#[test]
193fn is_invalid_source() {
194 let err = SpfError::InvalidSource;
195 assert_eq!(err.is_invalid_source(), true);
196}
197#[test]
198fn is_source_length_exceeded() {
199 let err = SpfError::SourceLengthExceeded;
200 assert_eq!(err.is_source_length_exceeded(), true);
201}
202#[test]
203fn is_lookup_limit_exceeded() {
204 let err = SpfError::LookupLimitExceeded;
205 assert_eq!(err.is_lookup_limit_exceeded(), true)
206}
207#[test]
208fn is_has_not_been_parsed() {
209 let err = SpfError::HasNotBeenParsed;
210 assert_eq!(err.is_has_not_been_parsed(), true)
211}
212#[test]
213fn is_invalid_spf() {
214 let err = SpfError::InvalidSPF;
215 assert_eq!(err.is_invalid_spf(), true)
216}
217#[test]
218fn is_redirect_with_all_mechanism() {
219 let err = SpfError::RedirectWithAllMechanism;
220 assert_eq!(err.is_redirect_with_all_mechanism(), true)
221}