Commit
Author: Mauro D [mauro@stalw.art]
Hash: 5036682b8988655e071534853a6febe586744f24
Timestamp: Mon, 05 Dec 2022 12:56:39 +0000 (2 years ago)

+619 -587 +/-36 browse
Fixes for #31.
1diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
2index 5c2da0c..387b9c4 100644
3--- a/.github/workflows/rust.yml
4+++ b/.github/workflows/rust.yml
5 @@ -15,8 +15,19 @@ jobs:
6 runs-on: ubuntu-latest
7
8 steps:
9- - uses: actions/checkout@v2
10- - name: Build
11- run: cargo build --verbose
12- - name: Run tests
13- run: cargo test --verbose
14+ - name: Checkout
15+ uses: actions/checkout@v2
16+ - name: cargo fmt -- --check
17+ uses: actions-rs/cargo@v1
18+ with:
19+ command: fmt
20+ args: --all -- --check
21+ - run: rustup component add clippy
22+ - uses: actions-rs/clippy-check@v1
23+ with:
24+ token: ${{ secrets.GITHUB_TOKEN }}
25+ args: --all-features
26+ - name: Build
27+ run: cargo build --verbose
28+ - name: Run tests
29+ run: cargo test --verbose
30 diff --git a/CHANGELOG.md b/CHANGELOG.md
31index 173f38e..f72c9b4 100644
32--- a/CHANGELOG.md
33+++ b/CHANGELOG.md
34 @@ -1,3 +1,8 @@
35+ mail-auth 0.2.0
36+ ================================
37+ - Fixed: Acronyms in type names do not match the recommended spelling from RFC 430 (#31)
38+ - Fixed: Inconsistent use of '.' at the end of strings on fmt::Display impl for Error (#31)
39+
40 mail-auth 0.1.0
41 ================================
42 - Initial release.
43 diff --git a/Cargo.toml b/Cargo.toml
44index 051b5f9..7050854 100644
45--- a/Cargo.toml
46+++ b/Cargo.toml
47 @@ -1,7 +1,7 @@
48 [package]
49 name = "mail-auth"
50 description = "DKIM, ARC, SPF and DMARC library for Rust"
51- version = "0.1.0"
52+ version = "0.2.0"
53 edition = "2021"
54 authors = [ "Stalwart Labs <hello@stalw.art>"]
55 license = "Apache-2.0 OR MIT"
56 diff --git a/README.md b/README.md
57index abcaec8..ea051a6 100644
58--- a/README.md
59+++ b/README.md
60 @@ -43,7 +43,7 @@ Features:
61 let result = resolver.verify_dkim(&authenticated_message).await;
62
63 // Make sure all signatures passed verification
64- assert!(result.iter().all(|s| s.result() == &DKIMResult::Pass));
65+ assert!(result.iter().all(|s| s.result() == &DkimResult::Pass));
66 ```
67
68 ### DKIM Signing
69 @@ -93,7 +93,7 @@ Features:
70 let result = resolver.verify_arc(&authenticated_message).await;
71
72 // Make sure ARC passed verification
73- assert_eq!(result.result(), &DKIMResult::Pass);
74+ assert_eq!(result.result(), &DkimResult::Pass);
75 ```
76
77 ### ARC Chain Sealing
78 @@ -118,7 +118,7 @@ Features:
79 if arc_result.can_be_sealed() {
80 // Seal the e-mail message using RSA-SHA256
81 let pk_rsa = PrivateKey::from_rsa_pkcs1_pem(RSA_PRIVATE_KEY).unwrap();
82- let arc_set = ARC::new(&auth_results)
83+ let arc_set = ArcSet::new(&auth_results)
84 .domain("example.org")
85 .selector("default")
86 .headers(["From", "To", "Subject", "DKIM-Signature"])
87 @@ -142,13 +142,13 @@ Features:
88 let result = resolver
89 .verify_spf_helo("127.0.0.1".parse().unwrap(), "gmail.com")
90 .await;
91- assert_eq!(result.result(), SPFResult::Fail);
92+ assert_eq!(result.result(), SpfResult::Fail);
93
94 // Verify MAIL-FROM identity
95 let result = resolver
96 .verify_spf_sender("::1".parse().unwrap(), "gmail.com", "sender@gmail.com")
97 .await;
98- assert_eq!(result.result(), SPFResult::Fail);
99+ assert_eq!(result.result(), SpfResult::Fail);
100 ```
101
102 ### DMARC Policy Evaluation
103 @@ -175,8 +175,8 @@ Features:
104 &spf_result,
105 )
106 .await;
107- assert_eq!(dmarc_result.dkim_result(), &DMARCResult::Pass);
108- assert_eq!(dmarc_result.spf_result(), &DMARCResult::Pass);
109+ assert_eq!(dmarc_result.dkim_result(), &DmarcResult::Pass);
110+ assert_eq!(dmarc_result.spf_result(), &DmarcResult::Pass);
111 ```
112
113 More examples available under the [examples](examples) directory.
114 @@ -192,7 +192,7 @@ To run the testsuite:
115 To fuzz the library with `cargo-fuzz`:
116
117 ```bash
118- $ cargo +nightly fuzz run mail_parser
119+ $ cargo +nightly fuzz run mail_auth
120 ```
121
122 ## Conformed RFCs
123 diff --git a/examples/arc_seal.rs b/examples/arc_seal.rs
124index 7a93c9c..7364ede 100644
125--- a/examples/arc_seal.rs
126+++ b/examples/arc_seal.rs
127 @@ -9,7 +9,7 @@
128 */
129
130 use mail_auth::{
131- arc::ARC, common::headers::HeaderWriter, AuthenticatedMessage, AuthenticationResults,
132+ arc::ArcSet, common::headers::HeaderWriter, AuthenticatedMessage, AuthenticationResults,
133 PrivateKey, Resolver,
134 };
135
136 @@ -52,7 +52,7 @@ async fn main() {
137 if arc_result.can_be_sealed() {
138 // Seal the e-mail message using RSA-SHA256
139 let pk_rsa = PrivateKey::from_rsa_pkcs1_pem(RSA_PRIVATE_KEY).unwrap();
140- let arc_set = ARC::new(&auth_results)
141+ let arc_set = ArcSet::new(&auth_results)
142 .domain("example.org")
143 .selector("default")
144 .headers(["From", "To", "Subject", "DKIM-Signature"])
145 diff --git a/examples/arc_verify.rs b/examples/arc_verify.rs
146index e04cf22..9b9e2a4 100644
147--- a/examples/arc_verify.rs
148+++ b/examples/arc_verify.rs
149 @@ -8,7 +8,7 @@
150 * except according to those terms.
151 */
152
153- use mail_auth::{AuthenticatedMessage, DKIMResult, Resolver};
154+ use mail_auth::{AuthenticatedMessage, DkimResult, Resolver};
155
156 const TEST_MESSAGE: &str = include_str!("../resources/arc/001.txt");
157
158 @@ -24,5 +24,5 @@ async fn main() {
159 let result = resolver.verify_arc(&authenticated_message).await;
160
161 // Make sure ARC passed verification
162- assert_eq!(result.result(), &DKIMResult::Pass);
163+ assert_eq!(result.result(), &DkimResult::Pass);
164 }
165 diff --git a/examples/dkim_verify.rs b/examples/dkim_verify.rs
166index 622fec9..7aa98ad 100644
167--- a/examples/dkim_verify.rs
168+++ b/examples/dkim_verify.rs
169 @@ -8,7 +8,7 @@
170 * except according to those terms.
171 */
172
173- use mail_auth::{AuthenticatedMessage, DKIMResult, Resolver};
174+ use mail_auth::{AuthenticatedMessage, DkimResult, Resolver};
175
176 const TEST_MESSAGE: &str = r#"DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed;
177 d=football.example.com; i=@football.example.com;
178 @@ -49,5 +49,5 @@ async fn main() {
179 let result = resolver.verify_dkim(&authenticated_message).await;
180
181 // Make sure all signatures passed verification
182- assert!(result.iter().all(|s| s.result() == &DKIMResult::Pass));
183+ assert!(result.iter().all(|s| s.result() == &DkimResult::Pass));
184 }
185 diff --git a/examples/dmarc_verify.rs b/examples/dmarc_verify.rs
186index cbf1049..b43a7ec 100644
187--- a/examples/dmarc_verify.rs
188+++ b/examples/dmarc_verify.rs
189 @@ -8,7 +8,7 @@
190 * except according to those terms.
191 */
192
193- use mail_auth::{AuthenticatedMessage, DMARCResult, Resolver};
194+ use mail_auth::{AuthenticatedMessage, DmarcResult, Resolver};
195
196 const TEST_MESSAGE: &str = r#"DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed;
197 d=football.example.com; i=@football.example.com;
198 @@ -60,6 +60,6 @@ async fn main() {
199 &spf_result,
200 )
201 .await;
202- assert_eq!(dmarc_result.dkim_result(), &DMARCResult::Pass);
203- assert_eq!(dmarc_result.spf_result(), &DMARCResult::Pass);
204+ assert_eq!(dmarc_result.dkim_result(), &DmarcResult::Pass);
205+ assert_eq!(dmarc_result.spf_result(), &DmarcResult::Pass);
206 }
207 diff --git a/examples/report_dmarc_generate.rs b/examples/report_dmarc_generate.rs
208index 8e1fcd4..b730eb8 100644
209--- a/examples/report_dmarc_generate.rs
210+++ b/examples/report_dmarc_generate.rs
211 @@ -9,8 +9,8 @@
212 */
213
214 use mail_auth::report::{
215- ActionDisposition, Alignment, DKIMAuthResult, DKIMResult, DMARCResult, Disposition,
216- PolicyOverride, PolicyOverrideReason, Record, Report, SPFAuthResult, SPFDomainScope, SPFResult,
217+ ActionDisposition, Alignment, DKIMAuthResult, Disposition, DkimResult, DmarcResult,
218+ PolicyOverride, PolicyOverrideReason, Record, Report, SPFAuthResult, SPFDomainScope, SpfResult,
219 };
220
221 fn main() {
222 @@ -36,8 +36,8 @@ fn main() {
223 .with_source_ip("192.168.1.2".parse().unwrap())
224 .with_count(3)
225 .with_action_disposition(ActionDisposition::Pass)
226- .with_dmarc_dkim_result(DMARCResult::Pass)
227- .with_dmarc_spf_result(DMARCResult::Fail)
228+ .with_dmarc_dkim_result(DmarcResult::Pass)
229+ .with_dmarc_spf_result(DmarcResult::Fail)
230 .with_policy_override_reason(
231 PolicyOverrideReason::new(PolicyOverride::Forwarded)
232 .with_comment("it was forwarded"),
233 @@ -53,14 +53,14 @@ fn main() {
234 DKIMAuthResult::new()
235 .with_domain("test.org")
236 .with_selector("my-selector")
237- .with_result(DKIMResult::PermError)
238+ .with_result(DkimResult::PermError)
239 .with_human_result("failed to parse record"),
240 )
241 .with_spf_auth_result(
242 SPFAuthResult::new()
243 .with_domain("test.org")
244 .with_scope(SPFDomainScope::Helo)
245- .with_result(SPFResult::SoftFail)
246+ .with_result(SpfResult::SoftFail)
247 .with_human_result("dns timed out"),
248 ),
249 )
250 @@ -69,8 +69,8 @@ fn main() {
251 .with_source_ip("a:b:c::e:f".parse().unwrap())
252 .with_count(99)
253 .with_action_disposition(ActionDisposition::Reject)
254- .with_dmarc_dkim_result(DMARCResult::Fail)
255- .with_dmarc_spf_result(DMARCResult::Pass)
256+ .with_dmarc_dkim_result(DmarcResult::Fail)
257+ .with_dmarc_spf_result(DmarcResult::Pass)
258 .with_policy_override_reason(
259 PolicyOverrideReason::new(PolicyOverride::LocalPolicy)
260 .with_comment("on the white list"),
261 @@ -86,14 +86,14 @@ fn main() {
262 DKIMAuthResult::new()
263 .with_domain("test2.org")
264 .with_selector("my-other-selector")
265- .with_result(DKIMResult::Neutral)
266+ .with_result(DkimResult::Neutral)
267 .with_human_result("something went wrong"),
268 )
269 .with_spf_auth_result(
270 SPFAuthResult::new()
271 .with_domain("test.org")
272 .with_scope(SPFDomainScope::MailFrom)
273- .with_result(SPFResult::None)
274+ .with_result(SpfResult::None)
275 .with_human_result("no policy found"),
276 ),
277 )
278 diff --git a/examples/spf_verify.rs b/examples/spf_verify.rs
279index 93930f7..f960b52 100644
280--- a/examples/spf_verify.rs
281+++ b/examples/spf_verify.rs
282 @@ -8,7 +8,7 @@
283 * except according to those terms.
284 */
285
286- use mail_auth::{Resolver, SPFResult};
287+ use mail_auth::{Resolver, SpfResult};
288
289 #[tokio::main]
290 async fn main() {
291 @@ -19,11 +19,11 @@ async fn main() {
292 let result = resolver
293 .verify_spf_helo("127.0.0.1".parse().unwrap(), "gmail.com")
294 .await;
295- assert_eq!(result.result(), SPFResult::Fail);
296+ assert_eq!(result.result(), SpfResult::Fail);
297
298 // Verify MAIL-FROM identity
299 let result = resolver
300 .verify_spf_sender("::1".parse().unwrap(), "gmail.com", "sender@gmail.com")
301 .await;
302- assert_eq!(result.result(), SPFResult::Fail);
303+ assert_eq!(result.result(), SpfResult::Fail);
304 }
305 diff --git a/fuzz/fuzz_targets/mail_auth.rs b/fuzz/fuzz_targets/mail_auth.rs
306index b4b36cf..3d5d520 100644
307--- a/fuzz/fuzz_targets/mail_auth.rs
308+++ b/fuzz/fuzz_targets/mail_auth.rs
309 @@ -15,9 +15,9 @@ use mail_auth::{
310 arc,
311 common::parse::TxtRecordParser,
312 dkim::{self, Atps, DomainKey, DomainKeyReport},
313- dmarc::DMARC,
314+ dmarc::Dmarc,
315 report::{Feedback, Report},
316- spf::{Macro, SPF},
317+ spf::{Macro, Spf},
318 AuthenticatedMessage,
319 };
320
321 @@ -53,11 +53,11 @@ fuzz_target!(|data: &[u8]| {
322 Atps::parse(data).ok();
323 Atps::parse(&data_txt).ok();
324
325- DMARC::parse(data).ok();
326- DMARC::parse(&data_txt).ok();
327+ Dmarc::parse(data).ok();
328+ Dmarc::parse(&data_txt).ok();
329
330- SPF::parse(data).ok();
331- SPF::parse(&data_txt).ok();
332+ Spf::parse(data).ok();
333+ Spf::parse(&data_txt).ok();
334
335 Macro::parse(data).ok();
336 Macro::parse(&data_txt).ok();
337 diff --git a/src/arc/headers.rs b/src/arc/headers.rs
338index 15c6515..381477b 100644
339--- a/src/arc/headers.rs
340+++ b/src/arc/headers.rs
341 @@ -8,7 +8,7 @@
342 * except according to those terms.
343 */
344
345- use std::io::Write;
346+ use std::io;
347
348 use crate::{
349 common::headers::HeaderWriter,
350 @@ -16,10 +16,10 @@ use crate::{
351 AuthenticationResults,
352 };
353
354- use super::{ChainValidation, Seal, Signature, ARC};
355+ use super::{ArcSet, ChainValidation, Seal, Signature};
356
357 impl<'x> Signature<'x> {
358- pub(crate) fn write(&self, mut writer: impl Write, as_header: bool) -> std::io::Result<()> {
359+ pub(crate) fn write(&self, mut writer: impl io::Write, as_header: bool) -> io::Result<()> {
360 let (header, new_line) = match self.ch {
361 Canonicalization::Relaxed if !as_header => (&b"arc-message-signature:"[..], &b" "[..]),
362 _ => (&b"ARC-Message-Signature: "[..], &b"\r\n\t"[..]),
363 @@ -99,7 +99,7 @@ impl<'x> Signature<'x> {
364 }
365
366 impl<'x> Seal<'x> {
367- pub(crate) fn write(&self, mut writer: impl Write, as_header: bool) -> std::io::Result<()> {
368+ pub(crate) fn write(&self, mut writer: impl io::Write, as_header: bool) -> io::Result<()> {
369 let (header, new_line) = if !as_header {
370 (&b"arc-seal:"[..], &b" "[..])
371 } else {
372 @@ -156,10 +156,10 @@ impl<'x> Seal<'x> {
373 impl<'x> AuthenticationResults<'x> {
374 pub(crate) fn write(
375 &self,
376- mut writer: impl Write,
377+ mut writer: impl io::Write,
378 i: u32,
379 as_header: bool,
380- ) -> std::io::Result<()> {
381+ ) -> io::Result<()> {
382 writer.write_all(if !as_header {
383 b"arc-authentication-results:"
384 } else {
385 @@ -189,8 +189,8 @@ impl<'x> AuthenticationResults<'x> {
386 }
387 }
388
389- impl<'x> HeaderWriter for ARC<'x> {
390- fn write_header(&self, mut writer: impl Write) -> std::io::Result<()> {
391+ impl<'x> HeaderWriter for ArcSet<'x> {
392+ fn write_header(&self, mut writer: impl io::Write) -> io::Result<()> {
393 self.seal.write(&mut writer, true)?;
394 self.signature.write(&mut writer, true)?;
395 self.results.write(&mut writer, self.seal.i, true)
396 diff --git a/src/arc/mod.rs b/src/arc/mod.rs
397index ed4485a..01326f0 100644
398--- a/src/arc/mod.rs
399+++ b/src/arc/mod.rs
400 @@ -18,7 +18,7 @@ use std::borrow::Cow;
401 use crate::{
402 common::{headers::Header, verify::VerifySignature},
403 dkim::{Algorithm, Canonicalization},
404- ARCOutput, AuthenticationResults, DKIMResult,
405+ ArcOutput, AuthenticationResults, DkimResult,
406 };
407
408 #[derive(Debug, PartialEq, Eq, Clone, Default)]
409 @@ -55,7 +55,7 @@ pub struct Results {
410 }
411
412 #[derive(Debug, Clone, PartialEq, Eq)]
413- pub struct ARC<'x> {
414+ pub struct ArcSet<'x> {
415 pub(crate) signature: Signature<'x>,
416 pub(crate) seal: Seal<'x>,
417 pub(crate) results: &'x AuthenticationResults<'x>,
418 @@ -117,8 +117,8 @@ impl<'x> VerifySignature for Seal<'x> {
419 }
420 }
421
422- impl<'x> ARCOutput<'x> {
423- pub(crate) fn with_result(mut self, result: DKIMResult) -> Self {
424+ impl<'x> ArcOutput<'x> {
425+ pub(crate) fn with_result(mut self, result: DkimResult) -> Self {
426 self.result = result;
427 self
428 }
429 @@ -128,10 +128,10 @@ impl<'x> ARCOutput<'x> {
430 }
431 }
432
433- impl<'x> Default for ARCOutput<'x> {
434+ impl<'x> Default for ArcOutput<'x> {
435 fn default() -> Self {
436 Self {
437- result: DKIMResult::None,
438+ result: DkimResult::None,
439 set: Vec::new(),
440 }
441 }
442 diff --git a/src/arc/seal.rs b/src/arc/seal.rs
443index d60aa39..f963ca5 100644
444--- a/src/arc/seal.rs
445+++ b/src/arc/seal.rs
446 @@ -18,14 +18,14 @@ use sha2::Sha256;
447
448 use crate::{
449 dkim::{Algorithm, Canonicalization},
450- ARCOutput, AuthenticatedMessage, AuthenticationResults, DKIMResult, Error, PrivateKey,
451+ ArcOutput, AuthenticatedMessage, AuthenticationResults, DkimResult, Error, PrivateKey,
452 };
453
454- use super::{ChainValidation, Seal, Signature, ARC};
455+ use super::{ArcSet, ChainValidation, Seal, Signature};
456
457- impl<'x> ARC<'x> {
458+ impl<'x> ArcSet<'x> {
459 pub fn new(results: &'x AuthenticationResults) -> Self {
460- ARC {
461+ ArcSet {
462 signature: Signature::default(),
463 seal: Seal::default(),
464 results,
465 @@ -35,7 +35,7 @@ impl<'x> ARC<'x> {
466 pub fn seal(
467 mut self,
468 message: &'x AuthenticatedMessage<'x>,
469- arc_output: &ARCOutput,
470+ arc_output: &ArcOutput,
471 with_key: &PrivateKey,
472 ) -> crate::Result<Self> {
473 if !arc_output.can_be_sealed() {
474 @@ -61,7 +61,7 @@ impl<'x> ARC<'x> {
475 self.signature.i = i;
476 self.seal.i = i;
477 self.seal.cv = match &arc_output.result {
478- DKIMResult::Pass => ChainValidation::Pass,
479+ DkimResult::Pass => ChainValidation::Pass,
480 _ => ChainValidation::Fail,
481 };
482 }
483 @@ -246,10 +246,10 @@ mod test {
484 use mail_parser::decoders::base64::base64_decode;
485
486 use crate::{
487- arc::ARC,
488+ arc::ArcSet,
489 common::{headers::HeaderWriter, parse::TxtRecordParser},
490 dkim::{DomainKey, Signature},
491- AuthenticatedMessage, AuthenticationResults, DKIMResult, PrivateKey, Resolver,
492+ AuthenticatedMessage, AuthenticationResults, DkimResult, PrivateKey, Resolver,
493 };
494
495 const RSA_PRIVATE_KEY: &str = r#"-----BEGIN RSA PRIVATE KEY-----
496 @@ -344,12 +344,12 @@ GMot/L2x0IYyMLAz6oLWh2hm7zwtb0CgOrPo1ke44hFYnfc=
497 let dkim_result = resolver.verify_dkim(&message).await;
498 let arc_result = resolver.verify_arc(&message).await;
499 assert!(
500- matches!(arc_result.result(), DKIMResult::Pass | DKIMResult::None),
501+ matches!(arc_result.result(), DkimResult::Pass | DkimResult::None),
502 "ARC validation failed: {:?}",
503 arc_result.result()
504 );
505 let auth_results = AuthenticationResults::new(d).with_dkim_result(&dkim_result, d);
506- let arc = ARC::new(&auth_results)
507+ let arc = ArcSet::new(&auth_results)
508 .domain(d)
509 .selector(s)
510 .headers(["From", "To", "Subject", "DKIM-Signature"])
511 diff --git a/src/arc/verify.rs b/src/arc/verify.rs
512index db793d3..e78abd1 100644
513--- a/src/arc/verify.rs
514+++ b/src/arc/verify.rs
515 @@ -16,22 +16,23 @@ use sha2::Sha256;
516 use crate::{
517 common::{headers::Header, verify::VerifySignature},
518 dkim::{verify::Verifier, Algorithm, Canonicalization, DomainKey, HashAlgorithm},
519- ARCOutput, AuthenticatedMessage, DKIMResult, Error, Resolver,
520+ ArcOutput, AuthenticatedMessage, DkimResult, Error, Resolver,
521 };
522
523 use super::{ChainValidation, Set};
524
525 impl Resolver {
526- pub async fn verify_arc<'x>(&self, message: &'x AuthenticatedMessage<'x>) -> ARCOutput<'x> {
527+ /// Verifies ARC headers of an RFC5322 message.
528+ pub async fn verify_arc<'x>(&self, message: &'x AuthenticatedMessage<'x>) -> ArcOutput<'x> {
529 let arc_headers = message.ams_headers.len();
530 if arc_headers == 0 {
531- return ARCOutput::default();
532+ return ArcOutput::default();
533 } else if arc_headers > 50 {
534- return ARCOutput::default().with_result(DKIMResult::Fail(Error::ARCChainTooLong));
535+ return ArcOutput::default().with_result(DkimResult::Fail(Error::ARCChainTooLong));
536 } else if (arc_headers != message.as_headers.len())
537 || (arc_headers != message.aar_headers.len())
538 {
539- return ARCOutput::default().with_result(DKIMResult::Fail(Error::ARCBrokenChain));
540+ return ArcOutput::default().with_result(DkimResult::Fail(Error::ARCBrokenChain));
541 }
542
543 let now = SystemTime::now()
544 @@ -39,8 +40,8 @@ impl Resolver {
545 .map(|d| d.as_secs())
546 .unwrap_or(0);
547
548- let mut output = ARCOutput {
549- result: DKIMResult::None,
550+ let mut output = ArcOutput {
551+ result: DkimResult::None,
552 set: Vec::with_capacity(message.aar_headers.len() / 3),
553 };
554
555 @@ -54,27 +55,27 @@ impl Resolver {
556 {
557 let seal = match &seal_.header {
558 Ok(seal) => seal,
559- Err(err) => return output.with_result(DKIMResult::Neutral(err.clone())),
560+ Err(err) => return output.with_result(DkimResult::Neutral(err.clone())),
561 };
562 let signature = match &signature_.header {
563 Ok(signature) => signature,
564- Err(err) => return output.with_result(DKIMResult::Neutral(err.clone())),
565+ Err(err) => return output.with_result(DkimResult::Neutral(err.clone())),
566 };
567 let results = match &results_.header {
568 Ok(results) => results,
569- Err(err) => return output.with_result(DKIMResult::Neutral(err.clone())),
570+ Err(err) => return output.with_result(DkimResult::Neutral(err.clone())),
571 };
572
573- if output.result == DKIMResult::None {
574+ if output.result == DkimResult::None {
575 if (seal.i as usize != (pos + 1))
576 || (signature.i as usize != (pos + 1))
577 || (results.i as usize != (pos + 1))
578 {
579- output.result = DKIMResult::Fail(Error::ARCInvalidInstance((pos + 1) as u32));
580+ output.result = DkimResult::Fail(Error::ARCInvalidInstance((pos + 1) as u32));
581 } else if (pos == 0 && seal.cv != ChainValidation::None)
582 || (pos > 0 && seal.cv != ChainValidation::Pass)
583 {
584- output.result = DKIMResult::Fail(Error::ARCInvalidCV);
585+ output.result = DkimResult::Fail(Error::ARCInvalidCV);
586 } else if pos == arc_headers - 1 {
587 // Validate last signature in the chain
588 if signature.x == 0 || (signature.x > signature.t && signature.x > now) {
589 @@ -89,10 +90,10 @@ impl Resolver {
590 .unwrap()
591 .3;
592 if bh != &signature.bh {
593- output.result = DKIMResult::Neutral(Error::FailedBodyHashMatch);
594+ output.result = DkimResult::Neutral(Error::FailedBodyHashMatch);
595 }
596 } else {
597- output.result = DKIMResult::Neutral(Error::SignatureExpired);
598+ output.result = DkimResult::Neutral(Error::SignatureExpired);
599 }
600 }
601 }
602 @@ -104,7 +105,7 @@ impl Resolver {
603 });
604 }
605
606- if output.result != DKIMResult::None {
607+ if output.result != DkimResult::None {
608 return output;
609 }
610
611 @@ -134,7 +135,7 @@ impl Resolver {
612
613 // Verify signature
614 if let Err(err) = signature.verify(record.as_ref(), &hh) {
615- return output.with_result(DKIMResult::Fail(err));
616+ return output.with_result(DkimResult::Fail(err));
617 }
618
619 // Validate ARC Seals
620 @@ -178,12 +179,12 @@ impl Resolver {
621
622 // Verify ARC Seal
623 if let Err(err) = seal.verify(record.as_ref(), &hh) {
624- return output.with_result(DKIMResult::Fail(err));
625+ return output.with_result(DkimResult::Fail(err));
626 }
627 }
628
629 // ARC Validation successful
630- output.with_result(DKIMResult::Pass)
631+ output.with_result(DkimResult::Pass)
632 }
633 }
634
635 @@ -196,7 +197,7 @@ mod test {
636 };
637
638 use crate::{
639- common::parse::TxtRecordParser, dkim::DomainKey, AuthenticatedMessage, DKIMResult, Resolver,
640+ common::parse::TxtRecordParser, dkim::DomainKey, AuthenticatedMessage, DkimResult, Resolver,
641 };
642
643 #[tokio::test]
644 @@ -219,10 +220,10 @@ mod test {
645 let message = AuthenticatedMessage::parse(raw_message.as_bytes()).unwrap();
646
647 let arc = resolver.verify_arc(&message).await;
648- assert_eq!(arc.result(), &DKIMResult::Pass);
649+ assert_eq!(arc.result(), &DkimResult::Pass);
650
651 let dkim = resolver.verify_dkim(&message).await;
652- assert!(dkim.iter().any(|o| o.result() == &DKIMResult::Pass));
653+ assert!(dkim.iter().any(|o| o.result() == &DkimResult::Pass));
654 }
655 }
656
657 diff --git a/src/common/auth_results.rs b/src/common/auth_results.rs
658index 301b728..3e727ca 100644
659--- a/src/common/auth_results.rs
660+++ b/src/common/auth_results.rs
661 @@ -8,13 +8,13 @@
662 * except according to those terms.
663 */
664
665- use std::{borrow::Cow, fmt::Write, net::IpAddr};
666+ use std::{borrow::Cow, fmt::Write, io, net::IpAddr};
667
668 use mail_builder::encoders::base64::base64_encode;
669
670 use crate::{
671- ARCOutput, AuthenticationResults, DKIMOutput, DKIMResult, DMARCOutput, DMARCResult, Error,
672- ReceivedSPF, SPFOutput, SPFResult,
673+ ArcOutput, AuthenticationResults, DkimOutput, DkimResult, DmarcOutput, DmarcResult, Error,
674+ ReceivedSpf, SpfOutput, SpfResult,
675 };
676
677 use super::headers::HeaderWriter;
678 @@ -27,7 +27,7 @@ impl<'x> AuthenticationResults<'x> {
679 }
680 }
681
682- pub fn with_dkim_result(mut self, dkim: &[DKIMOutput], header_from: &str) -> Self {
683+ pub fn with_dkim_result(mut self, dkim: &[DkimOutput], header_from: &str) -> Self {
684 for dkim in dkim {
685 if !dkim.is_atps {
686 self.auth_results.push_str(";\r\n\tdkim=");
687 @@ -63,7 +63,7 @@ impl<'x> AuthenticationResults<'x> {
688
689 pub fn with_spf_result(
690 mut self,
691- spf: &SPFOutput,
692+ spf: &SpfOutput,
693 ip_addr: IpAddr,
694 helo: &str,
695 mail_from: &str,
696 @@ -89,23 +89,23 @@ impl<'x> AuthenticationResults<'x> {
697 self
698 }
699
700- pub fn with_arc_result(mut self, arc: &ARCOutput, remote_ip: IpAddr) -> Self {
701+ pub fn with_arc_result(mut self, arc: &ArcOutput, remote_ip: IpAddr) -> Self {
702 self.auth_results.push_str(";\r\n\tarc=");
703 arc.result.as_auth_result(&mut self.auth_results);
704 write!(self.auth_results, " smtp.remote-ip={}", remote_ip).ok();
705 self
706 }
707
708- pub fn with_dmarc_result(mut self, dmarc: &DMARCOutput) -> Self {
709+ pub fn with_dmarc_result(mut self, dmarc: &DmarcOutput) -> Self {
710 self.auth_results.push_str(";\r\n\tdmarc=");
711- if dmarc.spf_result == DMARCResult::Pass || dmarc.dkim_result == DMARCResult::Pass {
712- DMARCResult::Pass.as_auth_result(&mut self.auth_results);
713- } else if dmarc.spf_result != DMARCResult::None {
714+ if dmarc.spf_result == DmarcResult::Pass || dmarc.dkim_result == DmarcResult::Pass {
715+ DmarcResult::Pass.as_auth_result(&mut self.auth_results);
716+ } else if dmarc.spf_result != DmarcResult::None {
717 dmarc.spf_result.as_auth_result(&mut self.auth_results);
718- } else if dmarc.dkim_result != DMARCResult::None {
719+ } else if dmarc.dkim_result != DmarcResult::None {
720 dmarc.dkim_result.as_auth_result(&mut self.auth_results);
721 } else {
722- DMARCResult::None.as_auth_result(&mut self.auth_results);
723+ DmarcResult::None.as_auth_result(&mut self.auth_results);
724 }
725 write!(
726 self.auth_results,
727 @@ -118,7 +118,7 @@ impl<'x> AuthenticationResults<'x> {
728 }
729
730 impl<'x> HeaderWriter for AuthenticationResults<'x> {
731- fn write_header(&self, mut writer: impl std::io::Write) -> std::io::Result<()> {
732+ fn write_header(&self, mut writer: impl io::Write) -> io::Result<()> {
733 writer.write_all(b"Authentication-Results: ")?;
734 writer.write_all(self.hostname.as_bytes())?;
735 if !self.auth_results.is_empty() {
736 @@ -130,17 +130,17 @@ impl<'x> HeaderWriter for AuthenticationResults<'x> {
737 }
738 }
739
740- impl HeaderWriter for ReceivedSPF {
741- fn write_header(&self, mut writer: impl std::io::Write) -> std::io::Result<()> {
742+ impl HeaderWriter for ReceivedSpf {
743+ fn write_header(&self, mut writer: impl io::Write) -> io::Result<()> {
744 writer.write_all(b"Received-SPF: ")?;
745 writer.write_all(self.received_spf.as_bytes())?;
746 writer.write_all(b"\r\n")
747 }
748 }
749
750- impl ReceivedSPF {
751+ impl ReceivedSpf {
752 pub fn new(
753- spf: &SPFOutput,
754+ spf: &SpfOutput,
755 ip_addr: IpAddr,
756 helo: &str,
757 mail_from: &str,
758 @@ -163,44 +163,44 @@ impl ReceivedSPF {
759 )
760 .ok();
761
762- ReceivedSPF { received_spf }
763+ ReceivedSpf { received_spf }
764 }
765 }
766
767- impl SPFResult {
768+ impl SpfResult {
769 fn as_spf_result(&self, header: &mut String, hostname: &str, mail_from: &str, ip_addr: IpAddr) {
770 match &self {
771- SPFResult::Pass => write!(
772+ SpfResult::Pass => write!(
773 header,
774 "pass ({}: domain of {} designates {} as permitted sender)",
775 hostname, mail_from, ip_addr
776 ),
777- SPFResult::Fail => write!(
778+ SpfResult::Fail => write!(
779 header,
780 "fail ({}: domain of {} does not designate {} as permitted sender)",
781 hostname, mail_from, ip_addr
782 ),
783- SPFResult::SoftFail => write!(
784+ SpfResult::SoftFail => write!(
785 header,
786 "softfail ({}: domain of {} reports soft fail for {})",
787 hostname, mail_from, ip_addr
788 ),
789- SPFResult::Neutral => write!(
790+ SpfResult::Neutral => write!(
791 header,
792 "neutral ({}: domain of {} reports neutral for {})",
793 hostname, mail_from, ip_addr
794 ),
795- SPFResult::TempError => write!(
796+ SpfResult::TempError => write!(
797 header,
798 "temperror ({}: temporary dns error validating {})",
799 hostname, mail_from
800 ),
801- SPFResult::PermError => write!(
802+ SpfResult::PermError => write!(
803 header,
804 "permerror ({}: unable to verify SPF record for {})",
805 hostname, mail_from,
806 ),
807- SPFResult::None => write!(
808+ SpfResult::None => write!(
809 header,
810 "none ({}: no SPF records found for {})",
811 hostname, mail_from
812 @@ -214,48 +214,48 @@ pub trait AsAuthResult {
813 fn as_auth_result(&self, header: &mut String);
814 }
815
816- impl AsAuthResult for DMARCResult {
817+ impl AsAuthResult for DmarcResult {
818 fn as_auth_result(&self, header: &mut String) {
819 match &self {
820- DMARCResult::Pass => header.push_str("pass"),
821- DMARCResult::Fail(err) => {
822+ DmarcResult::Pass => header.push_str("pass"),
823+ DmarcResult::Fail(err) => {
824 header.push_str("fail");
825 err.as_auth_result(header);
826 }
827- DMARCResult::PermError(err) => {
828+ DmarcResult::PermError(err) => {
829 header.push_str("permerror");
830 err.as_auth_result(header);
831 }
832- DMARCResult::TempError(err) => {
833+ DmarcResult::TempError(err) => {
834 header.push_str("temperror");
835 err.as_auth_result(header);
836 }
837- DMARCResult::None => header.push_str("none"),
838+ DmarcResult::None => header.push_str("none"),
839 }
840 }
841 }
842
843- impl AsAuthResult for DKIMResult {
844+ impl AsAuthResult for DkimResult {
845 fn as_auth_result(&self, header: &mut String) {
846 match &self {
847- DKIMResult::Pass => header.push_str("pass"),
848- DKIMResult::Neutral(err) => {
849+ DkimResult::Pass => header.push_str("pass"),
850+ DkimResult::Neutral(err) => {
851 header.push_str("neutral");
852 err.as_auth_result(header);
853 }
854- DKIMResult::Fail(err) => {
855+ DkimResult::Fail(err) => {
856 header.push_str("fail");
857 err.as_auth_result(header);
858 }
859- DKIMResult::PermError(err) => {
860+ DkimResult::PermError(err) => {
861 header.push_str("permerror");
862 err.as_auth_result(header);
863 }
864- DKIMResult::TempError(err) => {
865+ DkimResult::TempError(err) => {
866 header.push_str("temperror");
867 err.as_auth_result(header);
868 }
869- DKIMResult::None => header.push_str("none"),
870+ DkimResult::None => header.push_str("none"),
871 }
872 }
873 }
874 @@ -300,8 +300,8 @@ impl AsAuthResult for Error {
875 #[cfg(test)]
876 mod test {
877 use crate::{
878- dkim::Signature, dmarc::Policy, ARCOutput, AuthenticationResults, DKIMOutput, DKIMResult,
879- DMARCOutput, DMARCResult, Error, ReceivedSPF, SPFOutput, SPFResult,
880+ dkim::Signature, dmarc::Policy, ArcOutput, AuthenticationResults, DkimOutput, DkimResult,
881+ DmarcOutput, DmarcResult, Error, ReceivedSpf, SpfOutput, SpfResult,
882 };
883
884 #[test]
885 @@ -311,8 +311,8 @@ mod test {
886 for (expected_auth_results, dkim) in [
887 (
888 "dkim=pass header.d=example.org header.s=myselector",
889- DKIMOutput {
890- result: DKIMResult::Pass,
891+ DkimOutput {
892+ result: DkimResult::Pass,
893 signature: (&Signature {
894 d: "example.org".into(),
895 s: "myselector".into(),
896 @@ -328,8 +328,8 @@ mod test {
897 "dkim=fail (verification failed) header.d=example.org ",
898 "header.s=myselector header.b=MTIzNDU2"
899 ),
900- DKIMOutput {
901- result: DKIMResult::Fail(Error::FailedVerification),
902+ DkimOutput {
903+ result: DkimResult::Fail(Error::FailedVerification),
904 signature: (&Signature {
905 d: "example.org".into(),
906 s: "myselector".into(),
907 @@ -346,8 +346,8 @@ mod test {
908 "dkim-atps=temperror (dns error) header.d=atps.example.org ",
909 "header.s=otherselctor header.b=YWJjZGVm header.from=jdoe@example.org"
910 ),
911- DKIMOutput {
912- result: DKIMResult::TempError(Error::DNSError),
913+ DkimOutput {
914+ result: DkimResult::TempError(Error::DNSError),
915 signature: (&Signature {
916 d: "atps.example.org".into(),
917 s: "otherselctor".into(),
918 @@ -386,7 +386,7 @@ mod test {
919 "permitted sender)\r\n\treceiver=localhost; client-ip=192.168.1.1; ",
920 "envelope-from=\"jdoe@example.org\"; helo=example.org;"
921 ),
922- SPFResult::Pass,
923+ SpfResult::Pass,
924 "192.168.1.1".parse().unwrap(),
925 "localhost",
926 "example.org",
927 @@ -404,7 +404,7 @@ mod test {
928 "client-ip=a:b:c::f; envelope-from=\"sender@otherdomain.org\"; ",
929 "helo=otherdomain.org;"
930 ),
931- SPFResult::Fail,
932+ SpfResult::Fail,
933 "a:b:c::f".parse().unwrap(),
934 "mx.domain.org",
935 "otherdomain.org",
936 @@ -420,7 +420,7 @@ mod test {
937 "a:b:c::f)\r\n\treceiver=mx.domain.org; client-ip=a:b:c::f; ",
938 "envelope-from=\"postmaster@example.org\"; helo=example.org;"
939 ),
940- SPFResult::Neutral,
941+ SpfResult::Neutral,
942 "a:b:c::f".parse().unwrap(),
943 "mx.domain.org",
944 "example.org",
945 @@ -429,7 +429,7 @@ mod test {
946 ] {
947 auth_results.hostname = receiver;
948 auth_results = auth_results.with_spf_result(
949- &SPFOutput {
950+ &SpfOutput {
951 result,
952 domain: "".to_string(),
953 report: None,
954 @@ -439,8 +439,8 @@ mod test {
955 helo,
956 mail_from,
957 );
958- let received_spf = ReceivedSPF::new(
959- &SPFOutput {
960+ let received_spf = ReceivedSpf::new(
961+ &SpfOutput {
962 result,
963 domain: "".to_string(),
964 report: None,
965 @@ -461,9 +461,9 @@ mod test {
966 for (expected_auth_results, dmarc) in [
967 (
968 "dmarc=pass header.from=example.org policy.dmarc=none",
969- DMARCOutput {
970- spf_result: DMARCResult::Pass,
971- dkim_result: DMARCResult::None,
972+ DmarcOutput {
973+ spf_result: DmarcResult::Pass,
974+ dkim_result: DmarcResult::None,
975 domain: "example.org".to_string(),
976 policy: Policy::None,
977 record: None,
978 @@ -471,9 +471,9 @@ mod test {
979 ),
980 (
981 "dmarc=fail (dmarc not aligned) header.from=example.com policy.dmarc=quarantine",
982- DMARCOutput {
983- dkim_result: DMARCResult::Fail(Error::DMARCNotAligned),
984- spf_result: DMARCResult::None,
985+ DmarcOutput {
986+ dkim_result: DmarcResult::Fail(Error::DMARCNotAligned),
987+ spf_result: DmarcResult::None,
988 domain: "example.com".to_string(),
989 policy: Policy::Quarantine,
990 record: None,
991 @@ -490,17 +490,17 @@ mod test {
992 for (expected_auth_results, arc, remote_ip) in [
993 (
994 "arc=pass smtp.remote-ip=192.127.9.2",
995- DKIMResult::Pass,
996+ DkimResult::Pass,
997 "192.127.9.2".parse().unwrap(),
998 ),
999 (
1000 "arc=neutral (body hash did not verify) smtp.remote-ip=1:2:3::a",
1001- DKIMResult::Neutral(Error::FailedBodyHashMatch),
1002+ DkimResult::Neutral(Error::FailedBodyHashMatch),
1003 "1:2:3::a".parse().unwrap(),
1004 ),
1005 ] {
1006 auth_results = auth_results.with_arc_result(
1007- &ARCOutput {
1008+ &ArcOutput {
1009 result: arc,
1010 set: vec![],
1011 },
1012 diff --git a/src/common/base32.rs b/src/common/base32.rs
1013index 0e8fbca..d2ecf63 100644
1014--- a/src/common/base32.rs
1015+++ b/src/common/base32.rs
1016 @@ -8,6 +8,8 @@
1017 * except according to those terms.
1018 */
1019
1020+ use std::io;
1021+
1022 pub(crate) static BASE32_ALPHABET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
1023
1024 pub(crate) struct Base32Writer {
1025 @@ -66,8 +68,8 @@ impl Base32Writer {
1026 }
1027 }
1028
1029- impl std::io::Write for Base32Writer {
1030- fn write(&mut self, bytes: &[u8]) -> std::io::Result<usize> {
1031+ impl io::Write for Base32Writer {
1032+ fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
1033 let start_pos = self.pos;
1034
1035 for &byte in bytes {
1036 @@ -77,13 +79,14 @@ impl std::io::Write for Base32Writer {
1037 Ok(self.pos - start_pos)
1038 }
1039
1040- fn flush(&mut self) -> std::io::Result<()> {
1041+ fn flush(&mut self) -> io::Result<()> {
1042 Ok(())
1043 }
1044 }
1045
1046 #[cfg(test)]
1047 mod tests {
1048+
1049 use std::io::Write;
1050
1051 use sha1::{Digest, Sha1};
1052 diff --git a/src/common/headers.rs b/src/common/headers.rs
1053index ff45eaa..d2673eb 100644
1054--- a/src/common/headers.rs
1055+++ b/src/common/headers.rs
1056 @@ -9,7 +9,7 @@
1057 */
1058
1059 use std::{
1060- io::Write,
1061+ io,
1062 iter::{Enumerate, Peekable},
1063 slice::Iter,
1064 };
1065 @@ -266,7 +266,7 @@ impl<'x> Iterator for HeaderParser<'x> {
1066 }
1067
1068 pub trait HeaderWriter: Sized {
1069- fn write_header(&self, writer: impl Write) -> std::io::Result<()>;
1070+ fn write_header(&self, writer: impl io::Write) -> io::Result<()>;
1071 fn to_header(&self) -> String {
1072 let mut buf = Vec::new();
1073 self.write_header(&mut buf).unwrap();
1074 diff --git a/src/common/resolver.rs b/src/common/resolver.rs
1075index f4e05e1..9a8b9ca 100644
1076--- a/src/common/resolver.rs
1077+++ b/src/common/resolver.rs
1078 @@ -24,8 +24,8 @@ use trust_dns_resolver::{
1079
1080 use crate::{
1081 dkim::{Atps, DomainKey, DomainKeyReport},
1082- dmarc::DMARC,
1083- spf::{Macro, SPF},
1084+ dmarc::Dmarc,
1085+ spf::{Macro, Spf},
1086 Error, Policy, Resolver, Txt, MX,
1087 };
1088
1089 @@ -356,21 +356,21 @@ impl From<Atps> for Txt {
1090 }
1091 }
1092
1093- impl From<SPF> for Txt {
1094- fn from(v: SPF) -> Self {
1095- Txt::SPF(v.into())
1096+ impl From<Spf> for Txt {
1097+ fn from(v: Spf) -> Self {
1098+ Txt::Spf(v.into())
1099 }
1100 }
1101
1102 impl From<Macro> for Txt {
1103 fn from(v: Macro) -> Self {
1104- Txt::SPFMacro(v.into())
1105+ Txt::SpfMacro(v.into())
1106 }
1107 }
1108
1109- impl From<DMARC> for Txt {
1110- fn from(v: DMARC) -> Self {
1111- Txt::DMARC(v.into())
1112+ impl From<Dmarc> for Txt {
1113+ fn from(v: Dmarc) -> Self {
1114+ Txt::Dmarc(v.into())
1115 }
1116 }
1117
1118 @@ -417,10 +417,10 @@ impl UnwrapTxtRecord for Atps {
1119 }
1120 }
1121
1122- impl UnwrapTxtRecord for SPF {
1123+ impl UnwrapTxtRecord for Spf {
1124 fn unwrap_txt(txt: Txt) -> crate::Result<Arc<Self>> {
1125 match txt {
1126- Txt::SPF(a) => Ok(a),
1127+ Txt::Spf(a) => Ok(a),
1128 Txt::Error(err) => Err(err),
1129 _ => Err(Error::Io("Invalid record type".to_string())),
1130 }
1131 @@ -430,17 +430,17 @@ impl UnwrapTxtRecord for SPF {
1132 impl UnwrapTxtRecord for Macro {
1133 fn unwrap_txt(txt: Txt) -> crate::Result<Arc<Self>> {
1134 match txt {
1135- Txt::SPFMacro(a) => Ok(a),
1136+ Txt::SpfMacro(a) => Ok(a),
1137 Txt::Error(err) => Err(err),
1138 _ => Err(Error::Io("Invalid record type".to_string())),
1139 }
1140 }
1141 }
1142
1143- impl UnwrapTxtRecord for DMARC {
1144+ impl UnwrapTxtRecord for Dmarc {
1145 fn unwrap_txt(txt: Txt) -> crate::Result<Arc<Self>> {
1146 match txt {
1147- Txt::DMARC(a) => Ok(a),
1148+ Txt::Dmarc(a) => Ok(a),
1149 Txt::Error(err) => Err(err),
1150 _ => Err(Error::Io("Invalid record type".to_string())),
1151 }
1152 diff --git a/src/dkim/canonicalize.rs b/src/dkim/canonicalize.rs
1153index ef34159..7ed0383 100644
1154--- a/src/dkim/canonicalize.rs
1155+++ b/src/dkim/canonicalize.rs
1156 @@ -8,7 +8,10 @@
1157 * except according to those terms.
1158 */
1159
1160- use std::{borrow::Cow, io::Write};
1161+ use std::{
1162+ borrow::Cow,
1163+ io::{self},
1164+ };
1165
1166 use sha1::Digest;
1167
1168 @@ -17,7 +20,7 @@ use crate::common::headers::HeaderIterator;
1169 use super::{Canonicalization, Signature};
1170
1171 impl Canonicalization {
1172- pub fn canonicalize_body(&self, body: &[u8], mut hasher: impl Write) -> std::io::Result<()> {
1173+ pub fn canonicalize_body(&self, body: &[u8], mut hasher: impl io::Write) -> io::Result<()> {
1174 let mut crlf_seq = 0;
1175
1176 match self {
1177 @@ -78,8 +81,8 @@ impl Canonicalization {
1178 pub fn canonicalize_headers<'x>(
1179 &self,
1180 headers: impl Iterator<Item = (&'x [u8], &'x [u8])>,
1181- mut hasher: impl Write,
1182- ) -> std::io::Result<()> {
1183+ mut hasher: impl io::Write,
1184+ ) -> io::Result<()> {
1185 match self {
1186 Canonicalization::Relaxed => {
1187 for (name, value) in headers {
1188 @@ -121,18 +124,18 @@ impl Canonicalization {
1189 pub fn hash_headers<'x, T>(
1190 &self,
1191 headers: impl Iterator<Item = (&'x [u8], &'x [u8])>,
1192- ) -> std::io::Result<Vec<u8>>
1193+ ) -> io::Result<Vec<u8>>
1194 where
1195- T: Digest + std::io::Write,
1196+ T: Digest + io::Write,
1197 {
1198 let mut hasher = T::new();
1199 self.canonicalize_headers(headers, &mut hasher)?;
1200 Ok(hasher.finalize().to_vec())
1201 }
1202
1203- pub fn hash_body<T>(&self, body: &[u8], l: u64) -> std::io::Result<Vec<u8>>
1204+ pub fn hash_body<T>(&self, body: &[u8], l: u64) -> io::Result<Vec<u8>>
1205 where
1206- T: Digest + std::io::Write,
1207+ T: Digest + io::Write,
1208 {
1209 let mut hasher = T::new();
1210 self.canonicalize_body(
1211 @@ -146,7 +149,7 @@ impl Canonicalization {
1212 Ok(hasher.finalize().to_vec())
1213 }
1214
1215- pub fn serialize_name(&self, mut writer: impl Write) -> std::io::Result<()> {
1216+ pub fn serialize_name(&self, mut writer: impl io::Write) -> io::Result<()> {
1217 writer.write_all(match self {
1218 Canonicalization::Relaxed => b"relaxed",
1219 Canonicalization::Simple => b"simple",
1220 @@ -159,8 +162,8 @@ impl<'x> Signature<'x> {
1221 pub(crate) fn canonicalize(
1222 &self,
1223 message: &'x [u8],
1224- header_hasher: impl Write,
1225- body_hasher: impl Write,
1226+ header_hasher: impl io::Write,
1227+ body_hasher: impl io::Write,
1228 ) -> crate::Result<(usize, Vec<Cow<'x, str>>)> {
1229 let mut headers_it = HeaderIterator::new(message);
1230 let mut headers = Vec::with_capacity(self.h.len());
1231 diff --git a/src/dkim/headers.rs b/src/dkim/headers.rs
1232index 08b1b0b..880d302 100644
1233--- a/src/dkim/headers.rs
1234+++ b/src/dkim/headers.rs
1235 @@ -10,7 +10,7 @@
1236
1237 use std::{
1238 fmt::{Display, Formatter},
1239- io::Write,
1240+ io,
1241 };
1242
1243 use crate::common::headers::HeaderWriter;
1244 @@ -18,7 +18,7 @@ use crate::common::headers::HeaderWriter;
1245 use super::{Algorithm, Canonicalization, HashAlgorithm, Signature};
1246
1247 impl<'x> Signature<'x> {
1248- pub(crate) fn write(&self, mut writer: impl Write, as_header: bool) -> std::io::Result<()> {
1249+ pub(crate) fn write(&self, mut writer: impl io::Write, as_header: bool) -> io::Result<()> {
1250 let (header, new_line) = match self.ch {
1251 Canonicalization::Relaxed if !as_header => (&b"dkim-signature:"[..], &b" "[..]),
1252 _ => (&b"DKIM-Signature: "[..], &b"\r\n\t"[..]),
1253 @@ -136,7 +136,7 @@ impl<'x> Signature<'x> {
1254 }
1255
1256 impl<'x> HeaderWriter for Signature<'x> {
1257- fn write_header(&self, writer: impl Write) -> std::io::Result<()> {
1258+ fn write_header(&self, writer: impl io::Write) -> io::Result<()> {
1259 self.write(writer, true)
1260 }
1261 }
1262 diff --git a/src/dkim/mod.rs b/src/dkim/mod.rs
1263index f68671e..11b5fe4 100644
1264--- a/src/dkim/mod.rs
1265+++ b/src/dkim/mod.rs
1266 @@ -13,7 +13,7 @@ use std::borrow::Cow;
1267 use rsa::RsaPublicKey;
1268
1269 use crate::{
1270- arc::Set, common::verify::VerifySignature, ARCOutput, DKIMOutput, DKIMResult, Error, Version,
1271+ arc::Set, common::verify::VerifySignature, ArcOutput, DkimOutput, DkimResult, Error, Version,
1272 };
1273
1274 pub mod canonicalize;
1275 @@ -177,10 +177,10 @@ impl<'x> VerifySignature for Signature<'x> {
1276 }
1277 }
1278
1279- impl<'x> DKIMOutput<'x> {
1280+ impl<'x> DkimOutput<'x> {
1281 pub(crate) fn pass() -> Self {
1282- DKIMOutput {
1283- result: DKIMResult::Pass,
1284+ DkimOutput {
1285+ result: DkimResult::Pass,
1286 signature: None,
1287 report: None,
1288 is_atps: false,
1289 @@ -188,8 +188,8 @@ impl<'x> DKIMOutput<'x> {
1290 }
1291
1292 pub(crate) fn perm_err(err: Error) -> Self {
1293- DKIMOutput {
1294- result: DKIMResult::PermError(err),
1295+ DkimOutput {
1296+ result: DkimResult::PermError(err),
1297 signature: None,
1298 report: None,
1299 is_atps: false,
1300 @@ -197,8 +197,8 @@ impl<'x> DKIMOutput<'x> {
1301 }
1302
1303 pub(crate) fn temp_err(err: Error) -> Self {
1304- DKIMOutput {
1305- result: DKIMResult::TempError(err),
1306+ DkimOutput {
1307+ result: DkimResult::TempError(err),
1308 signature: None,
1309 report: None,
1310 is_atps: false,
1311 @@ -206,8 +206,8 @@ impl<'x> DKIMOutput<'x> {
1312 }
1313
1314 pub(crate) fn fail(err: Error) -> Self {
1315- DKIMOutput {
1316- result: DKIMResult::Fail(err),
1317+ DkimOutput {
1318+ result: DkimResult::Fail(err),
1319 signature: None,
1320 report: None,
1321 is_atps: false,
1322 @@ -215,8 +215,8 @@ impl<'x> DKIMOutput<'x> {
1323 }
1324
1325 pub(crate) fn neutral(err: Error) -> Self {
1326- DKIMOutput {
1327- result: DKIMResult::Neutral(err),
1328+ DkimOutput {
1329+ result: DkimResult::Neutral(err),
1330 signature: None,
1331 report: None,
1332 is_atps: false,
1333 @@ -225,9 +225,9 @@ impl<'x> DKIMOutput<'x> {
1334
1335 pub(crate) fn dns_error(err: Error) -> Self {
1336 if matches!(&err, Error::DNSError) {
1337- DKIMOutput::temp_err(err)
1338+ DkimOutput::temp_err(err)
1339 } else {
1340- DKIMOutput::perm_err(err)
1341+ DkimOutput::perm_err(err)
1342 }
1343 }
1344
1345 @@ -241,7 +241,7 @@ impl<'x> DKIMOutput<'x> {
1346 self
1347 }
1348
1349- pub fn result(&self) -> &DKIMResult {
1350+ pub fn result(&self) -> &DkimResult {
1351 &self.result
1352 }
1353
1354 @@ -254,8 +254,8 @@ impl<'x> DKIMOutput<'x> {
1355 }
1356 }
1357
1358- impl<'x> ARCOutput<'x> {
1359- pub fn result(&self) -> &DKIMResult {
1360+ impl<'x> ArcOutput<'x> {
1361+ pub fn result(&self) -> &DkimResult {
1362 &self.result
1363 }
1364
1365 @@ -264,12 +264,12 @@ impl<'x> ARCOutput<'x> {
1366 }
1367 }
1368
1369- impl From<Error> for DKIMResult {
1370+ impl From<Error> for DkimResult {
1371 fn from(err: Error) -> Self {
1372 if matches!(&err, Error::DNSError) {
1373- DKIMResult::TempError(err)
1374+ DkimResult::TempError(err)
1375 } else {
1376- DKIMResult::PermError(err)
1377+ DkimResult::PermError(err)
1378 }
1379 }
1380 }
1381 diff --git a/src/dkim/sign.rs b/src/dkim/sign.rs
1382index 4bd6c99..0c33add 100644
1383--- a/src/dkim/sign.rs
1384+++ b/src/dkim/sign.rs
1385 @@ -8,7 +8,7 @@
1386 * except according to those terms.
1387 */
1388
1389- use std::{borrow::Cow, time::SystemTime};
1390+ use std::{borrow::Cow, io, time::SystemTime};
1391
1392 use ed25519_dalek::Signer;
1393 use mail_builder::encoders::base64::base64_encode;
1394 @@ -89,7 +89,7 @@ impl<'x> Signature<'x> {
1395
1396 fn sign_<T>(mut self, message: &'x [u8], with_key: &PrivateKey, now: u64) -> crate::Result<Self>
1397 where
1398- T: Digest + AssociatedOid + std::io::Write,
1399+ T: Digest + AssociatedOid + io::Write,
1400 {
1401 let mut body_hasher = T::new();
1402 let mut header_hasher = T::new();
1403 @@ -218,7 +218,7 @@ mod test {
1404 use crate::{
1405 common::parse::TxtRecordParser,
1406 dkim::{Atps, Canonicalization, DomainKey, DomainKeyReport, HashAlgorithm, Signature},
1407- AuthenticatedMessage, DKIMOutput, DKIMResult, PrivateKey, Resolver,
1408+ AuthenticatedMessage, DkimOutput, DkimResult, PrivateKey, Resolver,
1409 };
1410
1411 const RSA_PRIVATE_KEY: &str = r#"-----BEGIN RSA PRIVATE KEY-----
1412 @@ -497,7 +497,7 @@ GMot/L2x0IYyMLAz6oLWh2hm7zwtb0CgOrPo1ke44hFYnfc=
1413 signature: Signature<'x>,
1414 message_: &'x str,
1415 expect: Result<(), super::Error>,
1416- ) -> Vec<DKIMOutput<'x>> {
1417+ ) -> Vec<DkimOutput<'x>> {
1418 let mut message = Vec::with_capacity(message_.len() + 100);
1419 signature.write(&mut message, true).unwrap();
1420 message.extend_from_slice(message_.as_bytes());
1421 @@ -506,16 +506,16 @@ GMot/L2x0IYyMLAz6oLWh2hm7zwtb0CgOrPo1ke44hFYnfc=
1422 let dkim = resolver.verify_dkim(&message).await;
1423
1424 match (dkim.last().unwrap().result(), &expect) {
1425- (DKIMResult::Pass, Ok(_)) => (),
1426+ (DkimResult::Pass, Ok(_)) => (),
1427 (
1428- DKIMResult::Fail(hdr) | DKIMResult::PermError(hdr) | DKIMResult::Neutral(hdr),
1429+ DkimResult::Fail(hdr) | DkimResult::PermError(hdr) | DkimResult::Neutral(hdr),
1430 Err(err),
1431 ) if hdr == err => (),
1432 (result, expect) => panic!("Expected {:?} but got {:?}.", expect, result),
1433 }
1434
1435 dkim.into_iter()
1436- .map(|d| DKIMOutput {
1437+ .map(|d| DkimOutput {
1438 result: d.result,
1439 signature: None,
1440 report: d.report,
1441 diff --git a/src/dkim/verify.rs b/src/dkim/verify.rs
1442index 649a76c..3e91116 100644
1443--- a/src/dkim/verify.rs
1444+++ b/src/dkim/verify.rs
1445 @@ -15,7 +15,7 @@ use sha2::Sha256;
1446
1447 use crate::{
1448 common::{base32::Base32Writer, verify::VerifySignature},
1449- is_within_pct, AuthenticatedMessage, DKIMOutput, DKIMResult, Error, Resolver,
1450+ is_within_pct, AuthenticatedMessage, DkimOutput, DkimResult, Error, Resolver,
1451 };
1452
1453 use super::{
1454 @@ -29,7 +29,7 @@ impl Resolver {
1455 pub async fn verify_dkim<'x>(
1456 &self,
1457 message: &'x AuthenticatedMessage<'x>,
1458- ) -> Vec<DKIMOutput<'x>> {
1459+ ) -> Vec<DkimOutput<'x>> {
1460 self.verify_dkim_(
1461 message,
1462 SystemTime::now()
1463 @@ -44,7 +44,7 @@ impl Resolver {
1464 &self,
1465 message: &'x AuthenticatedMessage<'x>,
1466 now: u64,
1467- ) -> Vec<DKIMOutput<'x>> {
1468+ ) -> Vec<DkimOutput<'x>> {
1469 let mut output = Vec::with_capacity(message.dkim_headers.len());
1470 let mut report_requested = false;
1471
1472 @@ -61,13 +61,13 @@ impl Resolver {
1473 signature
1474 } else {
1475 output.push(
1476- DKIMOutput::neutral(Error::SignatureExpired).with_signature(signature),
1477+ DkimOutput::neutral(Error::SignatureExpired).with_signature(signature),
1478 );
1479 continue;
1480 }
1481 }
1482 Err(err) => {
1483- output.push(DKIMOutput::neutral(err.clone()));
1484+ output.push(DkimOutput::neutral(err.clone()));
1485 continue;
1486 }
1487 };
1488 @@ -83,7 +83,7 @@ impl Resolver {
1489
1490 if bh != &signature.bh {
1491 output.push(
1492- DKIMOutput::neutral(Error::FailedBodyHashMatch).with_signature(signature),
1493+ DkimOutput::neutral(Error::FailedBodyHashMatch).with_signature(signature),
1494 );
1495 continue;
1496 }
1497 @@ -92,14 +92,14 @@ impl Resolver {
1498 let record = match self.txt_lookup::<DomainKey>(signature.domain_key()).await {
1499 Ok(record) => record,
1500 Err(err) => {
1501- output.push(DKIMOutput::dns_error(err).with_signature(signature));
1502+ output.push(DkimOutput::dns_error(err).with_signature(signature));
1503 continue;
1504 }
1505 };
1506
1507 // Enforce t=s flag
1508 if !signature.validate_auid(&record) {
1509- output.push(DKIMOutput::fail(Error::FailedAUIDMatch).with_signature(signature));
1510+ output.push(DkimOutput::fail(Error::FailedAUIDMatch).with_signature(signature));
1511 continue;
1512 }
1513
1514 @@ -116,7 +116,7 @@ impl Resolver {
1515
1516 // Verify signature
1517 if let Err(err) = signature.verify(record.as_ref(), &hh) {
1518- output.push(DKIMOutput::fail(err).with_signature(signature));
1519+ output.push(DkimOutput::fail(err).with_signature(signature));
1520 continue;
1521 }
1522
1523 @@ -158,11 +158,11 @@ impl Resolver {
1524 match self.txt_lookup::<Atps>(query_domain).await {
1525 Ok(_) => {
1526 // ATPS Verification successful
1527- output.push(DKIMOutput::pass().with_atps().with_signature(signature));
1528+ output.push(DkimOutput::pass().with_atps().with_signature(signature));
1529 }
1530 Err(err) => {
1531 output.push(
1532- DKIMOutput::dns_error(err)
1533+ DkimOutput::dns_error(err)
1534 .with_atps()
1535 .with_signature(signature),
1536 );
1537 @@ -173,7 +173,7 @@ impl Resolver {
1538 }
1539
1540 // Verification successful
1541- output.push(DKIMOutput::pass().with_signature(signature));
1542+ output.push(DkimOutput::pass().with_signature(signature));
1543 }
1544
1545 // Handle reports
1546 @@ -181,7 +181,7 @@ impl Resolver {
1547 for dkim in &mut output {
1548 // Process signatures with errors that requested reports
1549 let signature = if let Some(signature) = &dkim.signature {
1550- if signature.r && dkim.result != DKIMResult::Pass {
1551+ if signature.r && dkim.result != DkimResult::Pass {
1552 signature
1553 } else {
1554 continue;
1555 @@ -206,10 +206,10 @@ impl Resolver {
1556
1557 // Set report address
1558 dkim.report = match &dkim.result() {
1559- DKIMResult::Neutral(err)
1560- | DKIMResult::Fail(err)
1561- | DKIMResult::PermError(err)
1562- | DKIMResult::TempError(err) => {
1563+ DkimResult::Neutral(err)
1564+ | DkimResult::Fail(err)
1565+ | DkimResult::PermError(err)
1566+ | DkimResult::TempError(err) => {
1567 let send_report = match err {
1568 Error::CryptoError(_)
1569 | Error::Io(_)
1570 @@ -244,7 +244,7 @@ impl Resolver {
1571 None
1572 }
1573 }
1574- DKIMResult::None | DKIMResult::Pass => None,
1575+ DkimResult::None | DkimResult::Pass => None,
1576 };
1577 }
1578 }
1579 @@ -377,7 +377,7 @@ mod test {
1580 use crate::{
1581 common::parse::TxtRecordParser,
1582 dkim::{verify::Verifier, DomainKey},
1583- AuthenticatedMessage, DKIMResult, Resolver,
1584+ AuthenticatedMessage, DkimResult, Resolver,
1585 };
1586
1587 #[tokio::test]
1588 @@ -401,7 +401,7 @@ mod test {
1589
1590 let dkim = resolver.verify_dkim_(&message, 1667843664).await;
1591
1592- assert_eq!(dkim.last().unwrap().result(), &DKIMResult::Pass);
1593+ assert_eq!(dkim.last().unwrap().result(), &DkimResult::Pass);
1594 }
1595 }
1596
1597 diff --git a/src/dmarc/mod.rs b/src/dmarc/mod.rs
1598index 291bc2f..733ccc7 100644
1599--- a/src/dmarc/mod.rs
1600+++ b/src/dmarc/mod.rs
1601 @@ -10,13 +10,13 @@
1602
1603 use std::{fmt::Display, sync::Arc};
1604
1605- use crate::{DMARCOutput, DMARCResult, Error, Version};
1606+ use crate::{DmarcOutput, DmarcResult, Error, Version};
1607
1608 pub mod parse;
1609 pub mod verify;
1610
1611 #[derive(Debug, Clone, PartialEq, Eq)]
1612- pub struct DMARC {
1613+ pub struct Dmarc {
1614 pub(crate) v: Version,
1615 pub(crate) adkim: Alignment,
1616 pub(crate) aspf: Alignment,
1617 @@ -92,45 +92,45 @@ impl URI {
1618 }
1619 }
1620
1621- impl From<Error> for DMARCResult {
1622+ impl From<Error> for DmarcResult {
1623 fn from(err: Error) -> Self {
1624 if matches!(&err, Error::DNSError) {
1625- DMARCResult::TempError(err)
1626+ DmarcResult::TempError(err)
1627 } else {
1628- DMARCResult::PermError(err)
1629+ DmarcResult::PermError(err)
1630 }
1631 }
1632 }
1633
1634- impl Default for DMARCOutput {
1635+ impl Default for DmarcOutput {
1636 fn default() -> Self {
1637 Self {
1638 domain: String::new(),
1639 policy: Policy::None,
1640 record: None,
1641- spf_result: DMARCResult::None,
1642- dkim_result: DMARCResult::None,
1643+ spf_result: DmarcResult::None,
1644+ dkim_result: DmarcResult::None,
1645 }
1646 }
1647 }
1648
1649- impl DMARCOutput {
1650+ impl DmarcOutput {
1651 pub(crate) fn with_domain(mut self, domain: &str) -> Self {
1652 self.domain = domain.to_string();
1653 self
1654 }
1655
1656- pub(crate) fn with_spf_result(mut self, result: DMARCResult) -> Self {
1657+ pub(crate) fn with_spf_result(mut self, result: DmarcResult) -> Self {
1658 self.spf_result = result;
1659 self
1660 }
1661
1662- pub(crate) fn with_dkim_result(mut self, result: DMARCResult) -> Self {
1663+ pub(crate) fn with_dkim_result(mut self, result: DmarcResult) -> Self {
1664 self.dkim_result = result;
1665 self
1666 }
1667
1668- pub(crate) fn with_record(mut self, record: Arc<DMARC>) -> Self {
1669+ pub(crate) fn with_record(mut self, record: Arc<Dmarc>) -> Self {
1670 self.record = record.into();
1671 self
1672 }
1673 @@ -143,15 +143,15 @@ impl DMARCOutput {
1674 self.policy
1675 }
1676
1677- pub fn dkim_result(&self) -> &DMARCResult {
1678+ pub fn dkim_result(&self) -> &DmarcResult {
1679 &self.dkim_result
1680 }
1681
1682- pub fn spf_result(&self) -> &DMARCResult {
1683+ pub fn spf_result(&self) -> &DmarcResult {
1684 &self.spf_result
1685 }
1686
1687- pub fn dmarc_record(&self) -> Option<&DMARC> {
1688+ pub fn dmarc_record(&self) -> Option<&Dmarc> {
1689 self.record.as_deref()
1690 }
1691
1692 @@ -161,12 +161,12 @@ impl DMARCOutput {
1693 match &self.record {
1694 Some(record)
1695 if !record.ruf.is_empty()
1696- && (self.dkim_result != DMARCResult::Pass
1697+ && (self.dkim_result != DmarcResult::Pass
1698 && matches!(record.fo, Report::Any | Report::Dkim | Report::DkimSpf))
1699- || (self.spf_result != DMARCResult::Pass
1700+ || (self.spf_result != DmarcResult::Pass
1701 && matches!(record.fo, Report::Any | Report::Spf | Report::DkimSpf))
1702- || (self.dkim_result != DMARCResult::Pass
1703- && self.spf_result != DMARCResult::Pass
1704+ || (self.dkim_result != DmarcResult::Pass
1705+ && self.spf_result != DmarcResult::Pass
1706 && record.fo == Report::All) =>
1707 {
1708 Some(record.fo.clone())
1709 @@ -176,7 +176,7 @@ impl DMARCOutput {
1710 }
1711 }
1712
1713- impl DMARC {
1714+ impl Dmarc {
1715 pub fn ruf(&self) -> &[URI] {
1716 &self.ruf
1717 }
1718 diff --git a/src/dmarc/parse.rs b/src/dmarc/parse.rs
1719index 7fd2e5f..45846f8 100644
1720--- a/src/dmarc/parse.rs
1721+++ b/src/dmarc/parse.rs
1722 @@ -17,9 +17,9 @@ use crate::{
1723 Error, Version,
1724 };
1725
1726- use super::{Alignment, Format, Policy, Psd, Report, DMARC, URI};
1727+ use super::{Alignment, Dmarc, Format, Policy, Psd, Report, URI};
1728
1729- impl TxtRecordParser for DMARC {
1730+ impl TxtRecordParser for Dmarc {
1731 fn parse(bytes: &[u8]) -> crate::Result<Self> {
1732 let mut record = bytes.iter();
1733 if record.key().unwrap_or(0) != V
1734 @@ -29,7 +29,7 @@ impl TxtRecordParser for DMARC {
1735 return Err(Error::InvalidRecordType);
1736 }
1737
1738- let mut dmarc = DMARC {
1739+ let mut dmarc = Dmarc {
1740 adkim: Alignment::Relaxed,
1741 aspf: Alignment::Relaxed,
1742 fo: Report::All,
1743 @@ -342,7 +342,7 @@ const PSD: u64 = (b'p' as u64) | (b's' as u64) << 8 | (b'd' as u64) << 16;
1744 mod test {
1745 use crate::{
1746 common::parse::TxtRecordParser,
1747- dmarc::{Alignment, Format, Policy, Psd, Report, DMARC, URI},
1748+ dmarc::{Alignment, Dmarc, Format, Policy, Psd, Report, URI},
1749 Version,
1750 };
1751
1752 @@ -351,7 +351,7 @@ mod test {
1753 for (record, expected_result) in [
1754 (
1755 "v=DMARC1; p=none; rua=mailto:dmarc-feedback@example.com",
1756- DMARC {
1757+ Dmarc {
1758 adkim: Alignment::Relaxed,
1759 aspf: Alignment::Relaxed,
1760 fo: Report::All,
1761 @@ -373,7 +373,7 @@ mod test {
1762 "v=DMARC1; p=none; rua=mailto:dmarc-feedback@example.com;",
1763 "ruf=mailto:auth-reports@example.com"
1764 ),
1765- DMARC {
1766+ Dmarc {
1767 adkim: Alignment::Relaxed,
1768 aspf: Alignment::Relaxed,
1769 fo: Report::All,
1770 @@ -395,7 +395,7 @@ mod test {
1771 "v=DMARC1; p=quarantine; rua=mailto:dmarc-feedback@example.com,",
1772 "mailto:tld-test@thirdparty.example.net!10m; pct=25; fo=d:s"
1773 ),
1774- DMARC {
1775+ Dmarc {
1776 adkim: Alignment::Relaxed,
1777 aspf: Alignment::Relaxed,
1778 fo: Report::DkimSpf,
1779 @@ -420,7 +420,7 @@ mod test {
1780 "v=DMARC1; p=reject; sp=quarantine; np=None; aspf=s; adkim=s; fo = 1;",
1781 "rua=mailto:dmarc-feedback@example.com"
1782 ),
1783- DMARC {
1784+ Dmarc {
1785 adkim: Alignment::Strict,
1786 aspf: Alignment::Strict,
1787 fo: Report::Any,
1788 @@ -443,7 +443,7 @@ mod test {
1789 "rua=mailto:dmarc-feedback@example.com!10 K , mailto:user%20@example.com ! 2G;",
1790 "ignore_me= true; fo=s; rf = AfrF; ",
1791 ),
1792- DMARC {
1793+ Dmarc {
1794 adkim: Alignment::Relaxed,
1795 aspf: Alignment::Relaxed,
1796 fo: Report::Spf,
1797 @@ -468,7 +468,7 @@ mod test {
1798 "v=DMARC1; p=quarantine; rua=mailto:dmarc-feedback@example.com,",
1799 "mailto:tld-test@thirdparty.example.net; fo=s:d; t=y; psd=y;;",
1800 ),
1801- DMARC {
1802+ Dmarc {
1803 adkim: Alignment::Relaxed,
1804 aspf: Alignment::Relaxed,
1805 fo: Report::DkimSpf,
1806 @@ -490,7 +490,7 @@ mod test {
1807 ),
1808 ] {
1809 assert_eq!(
1810- DMARC::parse(record.as_bytes())
1811+ Dmarc::parse(record.as_bytes())
1812 .unwrap_or_else(|err| panic!("{:?} : {:?}", record, err)),
1813 expected_result,
1814 "{}",
1815 diff --git a/src/dmarc/verify.rs b/src/dmarc/verify.rs
1816index 8c8d30c..c29d34c 100644
1817--- a/src/dmarc/verify.rs
1818+++ b/src/dmarc/verify.rs
1819 @@ -11,20 +11,21 @@
1820 use std::sync::Arc;
1821
1822 use crate::{
1823- AuthenticatedMessage, DKIMOutput, DKIMResult, DMARCOutput, DMARCResult, Error, Resolver,
1824- SPFOutput, SPFResult,
1825+ AuthenticatedMessage, DkimOutput, DkimResult, DmarcOutput, DmarcResult, Error, Resolver,
1826+ SpfOutput, SpfResult,
1827 };
1828
1829- use super::{Alignment, DMARC, URI};
1830+ use super::{Alignment, Dmarc, URI};
1831
1832 impl Resolver {
1833+ /// Verifies the DMARC policy of an RFC5322.From domain
1834 pub async fn verify_dmarc(
1835 &self,
1836 message: &AuthenticatedMessage<'_>,
1837- dkim_output: &[DKIMOutput<'_>],
1838+ dkim_output: &[DkimOutput<'_>],
1839 mail_from_domain: &str,
1840- spf_output: &SPFOutput,
1841- ) -> DMARCOutput {
1842+ spf_output: &SpfOutput,
1843+ ) -> DmarcOutput {
1844 // Extract RFC5322.From
1845 let mut from_domain = "";
1846 for from in &message.from {
1847 @@ -34,79 +35,79 @@ impl Resolver {
1848 } else if from_domain != domain {
1849 // Multi-valued RFC5322.From header fields with multiple
1850 // domains MUST be exempt from DMARC checking.
1851- return DMARCOutput::default();
1852+ return DmarcOutput::default();
1853 }
1854 }
1855 }
1856 if from_domain.is_empty() {
1857- return DMARCOutput::default();
1858+ return DmarcOutput::default();
1859 }
1860
1861 // Obtain DMARC policy
1862 let dmarc = match self.dmarc_tree_walk(from_domain).await {
1863 Ok(Some(dmarc)) => dmarc,
1864- Ok(None) => return DMARCOutput::default().with_domain(from_domain),
1865+ Ok(None) => return DmarcOutput::default().with_domain(from_domain),
1866 Err(err) => {
1867- let err = DMARCResult::from(err);
1868- return DMARCOutput::default()
1869+ let err = DmarcResult::from(err);
1870+ return DmarcOutput::default()
1871 .with_domain(from_domain)
1872 .with_dkim_result(err.clone())
1873 .with_spf_result(err);
1874 }
1875 };
1876
1877- let mut output = DMARCOutput {
1878- spf_result: DMARCResult::None,
1879- dkim_result: DMARCResult::None,
1880+ let mut output = DmarcOutput {
1881+ spf_result: DmarcResult::None,
1882+ dkim_result: DmarcResult::None,
1883 domain: from_domain.to_string(),
1884 policy: dmarc.p,
1885 record: None,
1886 };
1887
1888- let has_dkim_pass = dkim_output.iter().any(|o| o.result == DKIMResult::Pass);
1889- if spf_output.result == SPFResult::Pass || has_dkim_pass {
1890+ let has_dkim_pass = dkim_output.iter().any(|o| o.result == DkimResult::Pass);
1891+ if spf_output.result == SpfResult::Pass || has_dkim_pass {
1892 // Check SPF alignment
1893 let from_subdomain = format!(".{}", from_domain);
1894- if spf_output.result == SPFResult::Pass {
1895+ if spf_output.result == SpfResult::Pass {
1896 output.spf_result = if mail_from_domain == from_domain {
1897- DMARCResult::Pass
1898+ DmarcResult::Pass
1899 } else if dmarc.aspf == Alignment::Relaxed
1900 && mail_from_domain.ends_with(&from_subdomain)
1901 || from_domain.ends_with(&format!(".{}", mail_from_domain))
1902 {
1903 output.policy = dmarc.sp;
1904- DMARCResult::Pass
1905+ DmarcResult::Pass
1906 } else {
1907- DMARCResult::Fail(Error::DMARCNotAligned)
1908+ DmarcResult::Fail(Error::DMARCNotAligned)
1909 };
1910 }
1911
1912 // Check DKIM alignment
1913 if has_dkim_pass {
1914 output.dkim_result = if dkim_output.iter().any(|o| {
1915- o.result == DKIMResult::Pass && o.signature.as_ref().unwrap().d.eq(from_domain)
1916+ o.result == DkimResult::Pass && o.signature.as_ref().unwrap().d.eq(from_domain)
1917 }) {
1918- DMARCResult::Pass
1919+ DmarcResult::Pass
1920 } else if dmarc.adkim == Alignment::Relaxed
1921 && dkim_output.iter().any(|o| {
1922- o.result == DKIMResult::Pass
1923+ o.result == DkimResult::Pass
1924 && (o.signature.as_ref().unwrap().d.ends_with(&from_subdomain)
1925 || from_domain
1926 .ends_with(&format!(".{}", o.signature.as_ref().unwrap().d)))
1927 })
1928 {
1929 output.policy = dmarc.sp;
1930- DMARCResult::Pass
1931+ DmarcResult::Pass
1932 } else {
1933 if dkim_output.iter().any(|o| {
1934- o.result == DKIMResult::Pass
1935+ o.result == DkimResult::Pass
1936 && (o.signature.as_ref().unwrap().d.ends_with(&from_subdomain)
1937 || from_domain
1938 .ends_with(&format!(".{}", o.signature.as_ref().unwrap().d)))
1939 }) {
1940 output.policy = dmarc.sp;
1941 }
1942- DMARCResult::Fail(Error::DMARCNotAligned)
1943+ DmarcResult::Fail(Error::DMARCNotAligned)
1944 };
1945 }
1946 }
1947 @@ -114,6 +115,7 @@ impl Resolver {
1948 output.with_record(dmarc)
1949 }
1950
1951+ /// Validates the external report e-mail addresses of a DMARC record
1952 pub async fn verify_dmarc_report_address<'x>(
1953 &self,
1954 domain: &str,
1955 @@ -123,7 +125,7 @@ impl Resolver {
1956 for address in addresses {
1957 if address.uri.ends_with(domain)
1958 || match self
1959- .txt_lookup::<DMARC>(format!(
1960+ .txt_lookup::<Dmarc>(format!(
1961 "{}.report.dmarc.{}.",
1962 domain,
1963 address
1964 @@ -146,7 +148,7 @@ impl Resolver {
1965 result.into()
1966 }
1967
1968- async fn dmarc_tree_walk(&self, domain: &str) -> crate::Result<Option<Arc<DMARC>>> {
1969+ async fn dmarc_tree_walk(&self, domain: &str) -> crate::Result<Option<Arc<Dmarc>>> {
1970 let labels = domain.split('.').collect::<Vec<_>>();
1971 let mut x = labels.len();
1972 if x == 1 {
1973 @@ -163,7 +165,7 @@ impl Resolver {
1974 domain.push('.');
1975
1976 // Query DMARC
1977- match self.txt_lookup::<DMARC>(domain).await {
1978+ match self.txt_lookup::<Dmarc>(domain).await {
1979 Ok(dmarc) => {
1980 return Ok(Some(dmarc));
1981 }
1982 @@ -192,9 +194,9 @@ mod test {
1983 use crate::{
1984 common::parse::TxtRecordParser,
1985 dkim::Signature,
1986- dmarc::{Policy, DMARC, URI},
1987- AuthenticatedMessage, DKIMOutput, DKIMResult, DMARCResult, Error, Resolver, SPFOutput,
1988- SPFResult,
1989+ dmarc::{Dmarc, Policy, URI},
1990+ AuthenticatedMessage, DkimOutput, DkimResult, DmarcResult, Error, Resolver, SpfOutput,
1991+ SpfResult,
1992 };
1993
1994 #[tokio::test]
1995 @@ -223,10 +225,10 @@ mod test {
1996 "From: hello@example.org\r\n\r\n",
1997 "example.org",
1998 "example.org",
1999- DKIMResult::Pass,
2000- SPFResult::Pass,
2001- DMARCResult::Pass,
2002- DMARCResult::Pass,
2003+ DkimResult::Pass,
2004+ SpfResult::Pass,
2005+ DmarcResult::Pass,
2006+ DmarcResult::Pass,
2007 Policy::Reject,
2008 ),
2009 // Relaxed - Pass
2010 @@ -239,10 +241,10 @@ mod test {
2011 "From: hello@example.org\r\n\r\n",
2012 "subdomain.example.org",
2013 "subdomain.example.org",
2014- DKIMResult::Pass,
2015- SPFResult::Pass,
2016- DMARCResult::Pass,
2017- DMARCResult::Pass,
2018+ DkimResult::Pass,
2019+ SpfResult::Pass,
2020+ DmarcResult::Pass,
2021+ DmarcResult::Pass,
2022 Policy::Quarantine,
2023 ),
2024 // Strict - Fail
2025 @@ -255,10 +257,10 @@ mod test {
2026 "From: hello@example.org\r\n\r\n",
2027 "subdomain.example.org",
2028 "subdomain.example.org",
2029- DKIMResult::Pass,
2030- SPFResult::Pass,
2031- DMARCResult::Fail(Error::DMARCNotAligned),
2032- DMARCResult::Fail(Error::DMARCNotAligned),
2033+ DkimResult::Pass,
2034+ SpfResult::Pass,
2035+ DmarcResult::Fail(Error::DMARCNotAligned),
2036+ DmarcResult::Fail(Error::DMARCNotAligned),
2037 Policy::Quarantine,
2038 ),
2039 // Strict - Pass with tree walk
2040 @@ -271,10 +273,10 @@ mod test {
2041 "From: hello@a.b.c.example.org\r\n\r\n",
2042 "a.b.c.example.org",
2043 "a.b.c.example.org",
2044- DKIMResult::Pass,
2045- SPFResult::Pass,
2046- DMARCResult::Pass,
2047- DMARCResult::Pass,
2048+ DkimResult::Pass,
2049+ SpfResult::Pass,
2050+ DmarcResult::Pass,
2051+ DmarcResult::Pass,
2052 Policy::Reject,
2053 ),
2054 // Relaxed - Pass with tree walk
2055 @@ -287,10 +289,10 @@ mod test {
2056 "From: hello@a.b.c.example.org\r\n\r\n",
2057 "example.org",
2058 "example.org",
2059- DKIMResult::Pass,
2060- SPFResult::Pass,
2061- DMARCResult::Pass,
2062- DMARCResult::Pass,
2063+ DkimResult::Pass,
2064+ SpfResult::Pass,
2065+ DmarcResult::Pass,
2066+ DmarcResult::Pass,
2067 Policy::Quarantine,
2068 ),
2069 // Failed mechanisms
2070 @@ -303,16 +305,16 @@ mod test {
2071 "From: hello@example.org\r\n\r\n",
2072 "example.org",
2073 "example.org",
2074- DKIMResult::Fail(Error::SignatureExpired),
2075- SPFResult::Fail,
2076- DMARCResult::None,
2077- DMARCResult::None,
2078+ DkimResult::Fail(Error::SignatureExpired),
2079+ SpfResult::Fail,
2080+ DmarcResult::None,
2081+ DmarcResult::None,
2082 Policy::Reject,
2083 ),
2084 ] {
2085 resolver.txt_add(
2086 dmarc_dns,
2087- DMARC::parse(dmarc.as_bytes()).unwrap(),
2088+ Dmarc::parse(dmarc.as_bytes()).unwrap(),
2089 Instant::now() + Duration::new(3200, 0),
2090 );
2091
2092 @@ -321,13 +323,13 @@ mod test {
2093 d: signature_domain.into(),
2094 ..Default::default()
2095 };
2096- let dkim = DKIMOutput {
2097+ let dkim = DkimOutput {
2098 result: dkim,
2099 signature: (&signature).into(),
2100 report: None,
2101 is_atps: false,
2102 };
2103- let spf = SPFOutput {
2104+ let spf = SpfOutput {
2105 result: spf,
2106 domain: mail_from_domain.to_string(),
2107 report: None,
2108 @@ -347,7 +349,7 @@ mod test {
2109 let resolver = Resolver::new_system_conf().unwrap();
2110 resolver.txt_add(
2111 "example.org.report.dmarc.external.org.",
2112- DMARC::parse(b"v=DMARC1").unwrap(),
2113+ Dmarc::parse(b"v=DMARC1").unwrap(),
2114 Instant::now() + Duration::new(3200, 0),
2115 );
2116 let uris = vec![
2117 diff --git a/src/lib.rs b/src/lib.rs
2118index 39825b1..b655369 100644
2119--- a/src/lib.rs
2120+++ b/src/lib.rs
2121 @@ -53,7 +53,7 @@
2122 //! let result = resolver.verify_dkim(&authenticated_message).await;
2123 //!
2124 //! // Make sure all signatures passed verification
2125- //! assert!(result.iter().all(|s| s.result() == &DKIMResult::Pass));
2126+ //! assert!(result.iter().all(|s| s.result() == &DkimResult::Pass));
2127 //! ```
2128 //!
2129 //! ### DKIM Signing
2130 @@ -103,7 +103,7 @@
2131 //! let result = resolver.verify_arc(&authenticated_message).await;
2132 //!
2133 //! // Make sure ARC passed verification
2134- //! assert_eq!(result.result(), &DKIMResult::Pass);
2135+ //! assert_eq!(result.result(), &DkimResult::Pass);
2136 //! ```
2137 //!
2138 //! ### ARC Chain Sealing
2139 @@ -128,7 +128,7 @@
2140 //! if arc_result.can_be_sealed() {
2141 //! // Seal the e-mail message using RSA-SHA256
2142 //! let pk_rsa = PrivateKey::from_rsa_pkcs1_pem(RSA_PRIVATE_KEY).unwrap();
2143- //! let arc_set = ARC::new(&auth_results)
2144+ //! let arc_set = ArcSet::new(&auth_results)
2145 //! .domain("example.org")
2146 //! .selector("default")
2147 //! .headers(["From", "To", "Subject", "DKIM-Signature"])
2148 @@ -152,13 +152,13 @@
2149 //! let result = resolver
2150 //! .verify_spf_helo("127.0.0.1".parse().unwrap(), "gmail.com")
2151 //! .await;
2152- //! assert_eq!(result.result(), SPFResult::Fail);
2153+ //! assert_eq!(result.result(), SpfResult::Fail);
2154 //!
2155 //! // Verify MAIL-FROM identity
2156 //! let result = resolver
2157 //! .verify_spf_sender("::1".parse().unwrap(), "gmail.com", "sender@gmail.com")
2158 //! .await;
2159- //! assert_eq!(result.result(), SPFResult::Fail);
2160+ //! assert_eq!(result.result(), SpfResult::Fail);
2161 //! ```
2162 //!
2163 //! ### DMARC Policy Evaluation
2164 @@ -185,8 +185,8 @@
2165 //! &spf_result,
2166 //! )
2167 //! .await;
2168- //! assert_eq!(dmarc_result.dkim_result(), &DMARCResult::Pass);
2169- //! assert_eq!(dmarc_result.spf_result(), &DMARCResult::Pass);
2170+ //! assert_eq!(dmarc_result.dkim_result(), &DmarcResult::Pass);
2171+ //! assert_eq!(dmarc_result.spf_result(), &DmarcResult::Pass);
2172 //! ```
2173 //!
2174 //! More examples available under the [examples](examples) directory.
2175 @@ -202,7 +202,7 @@
2176 //! To fuzz the library with `cargo-fuzz`:
2177 //!
2178 //! ```bash
2179- //! $ cargo +nightly fuzz run mail_parser
2180+ //! $ cargo +nightly fuzz run mail_auth
2181 //! ```
2182 //!
2183 //! ## Conformed RFCs
2184 @@ -255,6 +255,7 @@
2185 use std::{
2186 cell::Cell,
2187 fmt::Display,
2188+ io,
2189 net::{IpAddr, Ipv4Addr, Ipv6Addr},
2190 sync::Arc,
2191 time::SystemTime,
2192 @@ -263,9 +264,9 @@ use std::{
2193 use arc::Set;
2194 use common::{headers::Header, lru::LruCache};
2195 use dkim::{Atps, Canonicalization, DomainKey, DomainKeyReport, HashAlgorithm};
2196- use dmarc::DMARC;
2197+ use dmarc::Dmarc;
2198 use rsa::RsaPrivateKey;
2199- use spf::{Macro, SPF};
2200+ use spf::{Macro, Spf};
2201 use trust_dns_resolver::{proto::op::ResponseCode, TokioAsyncResolver};
2202
2203 pub mod arc;
2204 @@ -294,13 +295,12 @@ pub struct Resolver {
2205 }
2206
2207 #[derive(Debug, Clone, PartialEq, Eq)]
2208- #[allow(clippy::upper_case_acronyms)]
2209 pub(crate) enum Txt {
2210- SPF(Arc<SPF>),
2211- SPFMacro(Arc<Macro>),
2212+ Spf(Arc<Spf>),
2213+ SpfMacro(Arc<Macro>),
2214 DomainKey(Arc<DomainKey>),
2215 DomainKeyReport(Arc<DomainKeyReport>),
2216- DMARC(Arc<DMARC>),
2217+ Dmarc(Arc<Dmarc>),
2218 Atps(Arc<Atps>),
2219 Error(Error),
2220 }
2221 @@ -339,12 +339,12 @@ pub struct AuthenticationResults<'x> {
2222
2223 #[derive(Debug, Clone, PartialEq, Eq)]
2224 // Received-SPF header
2225- pub struct ReceivedSPF {
2226+ pub struct ReceivedSpf {
2227 pub(crate) received_spf: String,
2228 }
2229
2230 #[derive(Debug, PartialEq, Eq, Clone)]
2231- pub enum DKIMResult {
2232+ pub enum DkimResult {
2233 Pass,
2234 Neutral(crate::Error),
2235 Fail(crate::Error),
2236 @@ -354,21 +354,21 @@ pub enum DKIMResult {
2237 }
2238
2239 #[derive(Debug, PartialEq, Eq, Clone)]
2240- pub struct DKIMOutput<'x> {
2241- result: DKIMResult,
2242+ pub struct DkimOutput<'x> {
2243+ result: DkimResult,
2244 signature: Option<&'x dkim::Signature<'x>>,
2245 report: Option<String>,
2246 is_atps: bool,
2247 }
2248
2249 #[derive(Debug, PartialEq, Eq, Clone)]
2250- pub struct ARCOutput<'x> {
2251- result: DKIMResult,
2252+ pub struct ArcOutput<'x> {
2253+ result: DkimResult,
2254 set: Vec<Set<'x>>,
2255 }
2256
2257 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
2258- pub enum SPFResult {
2259+ pub enum SpfResult {
2260 Pass,
2261 Fail,
2262 SoftFail,
2263 @@ -379,24 +379,24 @@ pub enum SPFResult {
2264 }
2265
2266 #[derive(Debug, PartialEq, Eq, Clone)]
2267- pub struct SPFOutput {
2268- result: SPFResult,
2269+ pub struct SpfOutput {
2270+ result: SpfResult,
2271 domain: String,
2272 report: Option<String>,
2273 explanation: Option<String>,
2274 }
2275
2276 #[derive(Debug, PartialEq, Eq, Clone)]
2277- pub struct DMARCOutput {
2278- spf_result: DMARCResult,
2279- dkim_result: DMARCResult,
2280+ pub struct DmarcOutput {
2281+ spf_result: DmarcResult,
2282+ dkim_result: DmarcResult,
2283 domain: String,
2284 policy: dmarc::Policy,
2285- record: Option<Arc<DMARC>>,
2286+ record: Option<Arc<Dmarc>>,
2287 }
2288
2289 #[derive(Debug, PartialEq, Eq, Clone)]
2290- pub enum DMARCResult {
2291+ pub enum DmarcResult {
2292 Pass,
2293 Fail(crate::Error),
2294 TempError(crate::Error),
2295 @@ -453,42 +453,42 @@ impl Display for Error {
2296 Error::CryptoError(err) => write!(f, "Cryptography layer error: {}", err),
2297 Error::Io(e) => write!(f, "I/O error: {}", e),
2298 Error::Base64 => write!(f, "Base64 encode or decode error."),
2299- Error::UnsupportedVersion => write!(f, "Unsupported version in DKIM Signature."),
2300- Error::UnsupportedAlgorithm => write!(f, "Unsupported algorithm in DKIM Signature."),
2301+ Error::UnsupportedVersion => write!(f, "Unsupported version in DKIM Signature"),
2302+ Error::UnsupportedAlgorithm => write!(f, "Unsupported algorithm in DKIM Signature"),
2303 Error::UnsupportedCanonicalization => {
2304- write!(f, "Unsupported canonicalization method in DKIM Signature.")
2305+ write!(f, "Unsupported canonicalization method in DKIM Signature")
2306 }
2307 Error::UnsupportedKeyType => {
2308- write!(f, "Unsupported key type in DKIM DNS record.")
2309+ write!(f, "Unsupported key type in DKIM DNS record")
2310 }
2311 Error::FailedBodyHashMatch => {
2312- write!(f, "Calculated body hash does not match signature hash.")
2313+ write!(f, "Calculated body hash does not match signature hash")
2314 }
2315- Error::RevokedPublicKey => write!(f, "Public key for this signature has been revoked."),
2316+ Error::RevokedPublicKey => write!(f, "Public key for this signature has been revoked"),
2317 Error::IncompatibleAlgorithms => write!(
2318 f,
2319- "Incompatible algorithms used in signature and DKIM DNS record."
2320+ "Incompatible algorithms used in signature and DKIM DNS record"
2321 ),
2322- Error::FailedVerification => write!(f, "Signature verification failed."),
2323- Error::SignatureExpired => write!(f, "Signature expired."),
2324- Error::FailedAUIDMatch => write!(f, "AUID does not match domain name."),
2325+ Error::FailedVerification => write!(f, "Signature verification failed"),
2326+ Error::SignatureExpired => write!(f, "Signature expired"),
2327+ Error::FailedAUIDMatch => write!(f, "AUID does not match domain name"),
2328 Error::ARCInvalidInstance(i) => {
2329- write!(f, "Invalid 'i={}' value found in ARC header.", i)
2330+ write!(f, "Invalid 'i={}' value found in ARC header", i)
2331 }
2332- Error::ARCInvalidCV => write!(f, "Invalid 'cv=' value found in ARC header."),
2333- Error::ARCHasHeaderTag => write!(f, "Invalid 'h=' tag present in ARC-Seal."),
2334- Error::ARCBrokenChain => write!(f, "Broken or missing ARC chain."),
2335- Error::ARCChainTooLong => write!(f, "Too many ARC headers."),
2336- Error::InvalidRecordType => write!(f, "Invalid record."),
2337- Error::DNSError => write!(f, "DNS resolution error."),
2338- Error::DNSRecordNotFound(code) => write!(f, "DNS record not found: {}.", code),
2339- Error::DMARCNotAligned => write!(f, "DMARC policy not aligned."),
2340+ Error::ARCInvalidCV => write!(f, "Invalid 'cv=' value found in ARC header"),
2341+ Error::ARCHasHeaderTag => write!(f, "Invalid 'h=' tag present in ARC-Seal"),
2342+ Error::ARCBrokenChain => write!(f, "Broken or missing ARC chain"),
2343+ Error::ARCChainTooLong => write!(f, "Too many ARC headers"),
2344+ Error::InvalidRecordType => write!(f, "Invalid record"),
2345+ Error::DNSError => write!(f, "DNS resolution error"),
2346+ Error::DNSRecordNotFound(code) => write!(f, "DNS record not found: {}", code),
2347+ Error::DMARCNotAligned => write!(f, "DMARC policy not aligned"),
2348 }
2349 }
2350 }
2351
2352- impl From<std::io::Error> for Error {
2353- fn from(err: std::io::Error) -> Self {
2354+ impl From<io::Error> for Error {
2355+ fn from(err: io::Error) -> Self {
2356 Error::Io(err.to_string())
2357 }
2358 }
2359 @@ -507,6 +507,9 @@ impl From<ed25519_dalek::ed25519::Error> for Error {
2360
2361 thread_local!(static COUNTER: Cell<u64> = Cell::new(0));
2362
2363+ /// Generates a random value between 0 and 100.
2364+ /// Returns true if the generated value is within the requested
2365+ /// sampling percentage specified in a SPF, DKIM or DMARC policy.
2366 pub(crate) fn is_within_pct(pct: u8) -> bool {
2367 pct == 100
2368 || COUNTER.with(|c| {
2369 diff --git a/src/report/arf/generate.rs b/src/report/arf/generate.rs
2370index df00b3d..75a2309 100644
2371--- a/src/report/arf/generate.rs
2372+++ b/src/report/arf/generate.rs
2373 @@ -8,7 +8,7 @@
2374 * except according to those terms.
2375 */
2376
2377- use std::{fmt::Write, time::SystemTime};
2378+ use std::{fmt::Write, io, time::SystemTime};
2379
2380 use mail_builder::{
2381 headers::{content_type::ContentType, HeaderType},
2382 @@ -25,8 +25,8 @@ impl<'x> Feedback<'x> {
2383 from: &'x str,
2384 to: &'x str,
2385 subject: &'x str,
2386- writer: impl std::io::Write,
2387- ) -> std::io::Result<()> {
2388+ writer: impl io::Write,
2389+ ) -> io::Result<()> {
2390 // Generate ARF
2391
2392 let arf = self.as_arf();
2393 @@ -101,10 +101,10 @@ impl<'x> Feedback<'x> {
2394 .write_to(writer)
2395 }
2396
2397- pub fn as_rfc5322(&self, from: &str, to: &str, subject: &str) -> std::io::Result<String> {
2398+ pub fn as_rfc5322(&self, from: &str, to: &str, subject: &str) -> io::Result<String> {
2399 let mut buf = Vec::new();
2400 self.write_rfc5322(from, to, subject, &mut buf)?;
2401- String::from_utf8(buf).map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))
2402+ String::from_utf8(buf).map_err(|err| io::Error::new(io::ErrorKind::Other, err))
2403 }
2404
2405 pub fn as_arf(&self) -> String {
2406 diff --git a/src/report/dmarc/generate.rs b/src/report/dmarc/generate.rs
2407index d44b026..e35f00e 100644
2408--- a/src/report/dmarc/generate.rs
2409+++ b/src/report/dmarc/generate.rs
2410 @@ -12,14 +12,15 @@ use flate2::{write::GzEncoder, Compression};
2411 use mail_builder::{headers::HeaderType, MessageBuilder};
2412
2413 use crate::report::{
2414- ActionDisposition, Alignment, AuthResult, DKIMAuthResult, DKIMResult, DMARCResult, DateRange,
2415- Disposition, Identifier, PolicyEvaluated, PolicyOverride, PolicyOverrideReason,
2416- PolicyPublished, Record, Report, ReportMetadata, Row, SPFAuthResult, SPFDomainScope, SPFResult,
2417+ ActionDisposition, Alignment, AuthResult, DKIMAuthResult, DateRange, Disposition, DkimResult,
2418+ DmarcResult, Identifier, PolicyEvaluated, PolicyOverride, PolicyOverrideReason,
2419+ PolicyPublished, Record, Report, ReportMetadata, Row, SPFAuthResult, SPFDomainScope, SpfResult,
2420 };
2421
2422 use std::{
2423 borrow::Cow,
2424 fmt::{Display, Formatter, Write},
2425+ io,
2426 };
2427
2428 impl Report {
2429 @@ -29,12 +30,12 @@ impl Report {
2430 submitter: &'x str,
2431 from: &'x str,
2432 to: &'x str,
2433- writer: impl std::io::Write,
2434- ) -> std::io::Result<()> {
2435+ writer: impl io::Write,
2436+ ) -> io::Result<()> {
2437 // Compress XML report
2438 let xml = self.as_xnl();
2439 let mut e = GzEncoder::new(Vec::with_capacity(xml.len()), Compression::default());
2440- std::io::Write::write_all(&mut e, xml.as_bytes())?;
2441+ io::Write::write_all(&mut e, xml.as_bytes())?;
2442 let compressed_bytes = e.finish()?;
2443
2444 MessageBuilder::new()
2445 @@ -79,10 +80,10 @@ impl Report {
2446 submitter: &'x str,
2447 from: &'x str,
2448 to: &'x str,
2449- ) -> std::io::Result<String> {
2450+ ) -> io::Result<String> {
2451 let mut buf = Vec::new();
2452 self.write_rfc5322(receiver_domain, submitter, from, to, &mut buf)?;
2453- String::from_utf8(buf).map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))
2454+ String::from_utf8(buf).map_err(|err| io::Error::new(io::ErrorKind::Other, err))
2455 }
2456
2457 pub fn as_xnl(&self) -> String {
2458 @@ -324,12 +325,12 @@ impl Display for ActionDisposition {
2459 }
2460 }
2461
2462- impl Display for DMARCResult {
2463+ impl Display for DmarcResult {
2464 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2465 f.write_str(match self {
2466- DMARCResult::Pass => "pass",
2467- DMARCResult::Fail => "fail",
2468- DMARCResult::Unspecified => "",
2469+ DmarcResult::Pass => "pass",
2470+ DmarcResult::Fail => "fail",
2471+ DmarcResult::Unspecified => "",
2472 })
2473 }
2474 }
2475 @@ -347,16 +348,16 @@ impl Display for PolicyOverride {
2476 }
2477 }
2478
2479- impl Display for DKIMResult {
2480+ impl Display for DkimResult {
2481 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2482 f.write_str(match self {
2483- DKIMResult::None => "none",
2484- DKIMResult::Pass => "pass",
2485- DKIMResult::Fail => "fail",
2486- DKIMResult::Policy => "policy",
2487- DKIMResult::Neutral => "neutral",
2488- DKIMResult::TempError => "temperror",
2489- DKIMResult::PermError => "permerror",
2490+ DkimResult::None => "none",
2491+ DkimResult::Pass => "pass",
2492+ DkimResult::Fail => "fail",
2493+ DkimResult::Policy => "policy",
2494+ DkimResult::Neutral => "neutral",
2495+ DkimResult::TempError => "temperror",
2496+ DkimResult::PermError => "permerror",
2497 })
2498 }
2499 }
2500 @@ -370,16 +371,16 @@ impl Display for SPFDomainScope {
2501 }
2502 }
2503
2504- impl Display for SPFResult {
2505+ impl Display for SpfResult {
2506 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2507 f.write_str(match self {
2508- SPFResult::None => "none",
2509- SPFResult::Neutral => "neutral",
2510- SPFResult::Pass => "pass",
2511- SPFResult::Fail => "fail",
2512- SPFResult::SoftFail => "softfail",
2513- SPFResult::TempError => "temperror",
2514- SPFResult::PermError => "permerror",
2515+ SpfResult::None => "none",
2516+ SpfResult::Neutral => "neutral",
2517+ SpfResult::Pass => "pass",
2518+ SpfResult::Fail => "fail",
2519+ SpfResult::SoftFail => "softfail",
2520+ SpfResult::TempError => "temperror",
2521+ SpfResult::PermError => "permerror",
2522 })
2523 }
2524 }
2525 @@ -420,9 +421,9 @@ fn escape_xml(text: &str) -> Cow<'_, str> {
2526 #[cfg(test)]
2527 mod test {
2528 use crate::report::{
2529- ActionDisposition, Alignment, DKIMAuthResult, DKIMResult, DMARCResult, Disposition,
2530+ ActionDisposition, Alignment, DKIMAuthResult, Disposition, DkimResult, DmarcResult,
2531 PolicyOverride, PolicyOverrideReason, Record, Report, SPFAuthResult, SPFDomainScope,
2532- SPFResult,
2533+ SpfResult,
2534 };
2535
2536 #[test]
2537 @@ -448,8 +449,8 @@ mod test {
2538 .with_source_ip("192.168.1.2".parse().unwrap())
2539 .with_count(3)
2540 .with_action_disposition(ActionDisposition::Pass)
2541- .with_dmarc_dkim_result(DMARCResult::Pass)
2542- .with_dmarc_spf_result(DMARCResult::Fail)
2543+ .with_dmarc_dkim_result(DmarcResult::Pass)
2544+ .with_dmarc_spf_result(DmarcResult::Fail)
2545 .with_policy_override_reason(
2546 PolicyOverrideReason::new(PolicyOverride::Forwarded)
2547 .with_comment("it was forwarded"),
2548 @@ -465,14 +466,14 @@ mod test {
2549 DKIMAuthResult::new()
2550 .with_domain("test.org")
2551 .with_selector("my-selector")
2552- .with_result(DKIMResult::PermError)
2553+ .with_result(DkimResult::PermError)
2554 .with_human_result("failed to parse record"),
2555 )
2556 .with_spf_auth_result(
2557 SPFAuthResult::new()
2558 .with_domain("test.org")
2559 .with_scope(SPFDomainScope::Helo)
2560- .with_result(SPFResult::SoftFail)
2561+ .with_result(SpfResult::SoftFail)
2562 .with_human_result("dns timed out"),
2563 ),
2564 )
2565 @@ -481,8 +482,8 @@ mod test {
2566 .with_source_ip("a:b:c::e:f".parse().unwrap())
2567 .with_count(99)
2568 .with_action_disposition(ActionDisposition::Reject)
2569- .with_dmarc_dkim_result(DMARCResult::Fail)
2570- .with_dmarc_spf_result(DMARCResult::Pass)
2571+ .with_dmarc_dkim_result(DmarcResult::Fail)
2572+ .with_dmarc_spf_result(DmarcResult::Pass)
2573 .with_policy_override_reason(
2574 PolicyOverrideReason::new(PolicyOverride::LocalPolicy)
2575 .with_comment("on the white list"),
2576 @@ -498,14 +499,14 @@ mod test {
2577 DKIMAuthResult::new()
2578 .with_domain("test2.org")
2579 .with_selector("my-other-selector")
2580- .with_result(DKIMResult::Neutral)
2581+ .with_result(DkimResult::Neutral)
2582 .with_human_result("something went wrong"),
2583 )
2584 .with_spf_auth_result(
2585 SPFAuthResult::new()
2586 .with_domain("test.org")
2587 .with_scope(SPFDomainScope::MailFrom)
2588- .with_result(SPFResult::None)
2589+ .with_result(SpfResult::None)
2590 .with_human_result("no policy found"),
2591 ),
2592 );
2593 diff --git a/src/report/dmarc/mod.rs b/src/report/dmarc/mod.rs
2594index 658a373..5ba302a 100644
2595--- a/src/report/dmarc/mod.rs
2596+++ b/src/report/dmarc/mod.rs
2597 @@ -15,13 +15,13 @@ use std::fmt::Write;
2598 use std::net::IpAddr;
2599
2600 use crate::{
2601- dmarc::DMARC,
2602+ dmarc::Dmarc,
2603 report::{
2604- ActionDisposition, Alignment, DKIMAuthResult, DKIMResult, DMARCResult, Disposition,
2605+ ActionDisposition, Alignment, DKIMAuthResult, Disposition, DkimResult, DmarcResult,
2606 PolicyOverride, PolicyOverrideReason, Record, Report, SPFAuthResult, SPFDomainScope,
2607- SPFResult,
2608+ SpfResult,
2609 },
2610- ARCOutput, DKIMOutput, DMARCOutput, SPFOutput,
2611+ ArcOutput, DkimOutput, DmarcOutput, SpfOutput,
2612 };
2613
2614 impl Report {
2615 @@ -182,7 +182,7 @@ impl Report {
2616 self
2617 }
2618
2619- pub fn with_policy_published(mut self, dmarc: &DMARC) -> Self {
2620+ pub fn with_policy_published(mut self, dmarc: &Dmarc) -> Self {
2621 self.policy_published.adkim = (&dmarc.adkim).into();
2622 self.policy_published.aspf = (&dmarc.aspf).into();
2623 self.policy_published.p = (&dmarc.p).into();
2624 @@ -206,22 +206,22 @@ impl Record {
2625 Record::default()
2626 }
2627
2628- pub fn with_dkim_output(mut self, dkim_output: &[DKIMOutput]) {
2629+ pub fn with_dkim_output(mut self, dkim_output: &[DkimOutput]) {
2630 for dkim in dkim_output {
2631 if let Some(signature) = &dkim.signature {
2632 let (result, human_result) = match &dkim.result {
2633- crate::DKIMResult::Pass => (DKIMResult::Pass, None),
2634- crate::DKIMResult::Neutral(err) => {
2635- (DKIMResult::Neutral, err.to_string().into())
2636+ crate::DkimResult::Pass => (DkimResult::Pass, None),
2637+ crate::DkimResult::Neutral(err) => {
2638+ (DkimResult::Neutral, err.to_string().into())
2639 }
2640- crate::DKIMResult::Fail(err) => (DKIMResult::Fail, err.to_string().into()),
2641- crate::DKIMResult::PermError(err) => {
2642- (DKIMResult::PermError, err.to_string().into())
2643+ crate::DkimResult::Fail(err) => (DkimResult::Fail, err.to_string().into()),
2644+ crate::DkimResult::PermError(err) => {
2645+ (DkimResult::PermError, err.to_string().into())
2646 }
2647- crate::DKIMResult::TempError(err) => {
2648- (DKIMResult::TempError, err.to_string().into())
2649+ crate::DkimResult::TempError(err) => {
2650+ (DkimResult::TempError, err.to_string().into())
2651 }
2652- crate::DKIMResult::None => (DKIMResult::None, None),
2653+ crate::DkimResult::None => (DkimResult::None, None),
2654 };
2655
2656 self.auth_results.dkim.push(DKIMAuthResult {
2657 @@ -234,24 +234,24 @@ impl Record {
2658 }
2659 }
2660
2661- pub fn with_spf_output(mut self, spf_output: &SPFOutput, scope: SPFDomainScope) {
2662+ pub fn with_spf_output(mut self, spf_output: &SpfOutput, scope: SPFDomainScope) {
2663 self.auth_results.spf.push(SPFAuthResult {
2664 domain: spf_output.domain.to_string(),
2665 scope,
2666 result: match spf_output.result {
2667- crate::SPFResult::Pass => SPFResult::Pass,
2668- crate::SPFResult::Fail => SPFResult::Fail,
2669- crate::SPFResult::SoftFail => SPFResult::SoftFail,
2670- crate::SPFResult::Neutral => SPFResult::Neutral,
2671- crate::SPFResult::TempError => SPFResult::TempError,
2672- crate::SPFResult::PermError => SPFResult::PermError,
2673- crate::SPFResult::None => SPFResult::None,
2674+ crate::SpfResult::Pass => SpfResult::Pass,
2675+ crate::SpfResult::Fail => SpfResult::Fail,
2676+ crate::SpfResult::SoftFail => SpfResult::SoftFail,
2677+ crate::SpfResult::Neutral => SpfResult::Neutral,
2678+ crate::SpfResult::TempError => SpfResult::TempError,
2679+ crate::SpfResult::PermError => SpfResult::PermError,
2680+ crate::SpfResult::None => SpfResult::None,
2681 },
2682 human_result: None,
2683 });
2684 }
2685
2686- pub fn with_dmarc_output(mut self, dmarc_output: &DMARCOutput) {
2687+ pub fn with_dmarc_output(mut self, dmarc_output: &DmarcOutput) {
2688 self.row.policy_evaluated.disposition = match dmarc_output.policy {
2689 crate::dmarc::Policy::None => ActionDisposition::None,
2690 crate::dmarc::Policy::Quarantine => ActionDisposition::Quarantine,
2691 @@ -262,8 +262,8 @@ impl Record {
2692 self.row.policy_evaluated.spf = (&dmarc_output.spf_result).into();
2693 }
2694
2695- pub fn with_arc_output(mut self, arc_output: &ARCOutput) {
2696- if arc_output.result == crate::DKIMResult::Pass {
2697+ pub fn with_arc_output(mut self, arc_output: &ArcOutput) {
2698+ if arc_output.result == crate::DkimResult::Pass {
2699 let mut comment = "arc=pass".to_string();
2700 for set in arc_output.set.iter().rev() {
2701 let seal = &set.seal.header;
2702 @@ -308,20 +308,20 @@ impl Record {
2703 self
2704 }
2705
2706- pub fn dmarc_dkim_result(&self) -> DMARCResult {
2707+ pub fn dmarc_dkim_result(&self) -> DmarcResult {
2708 self.row.policy_evaluated.dkim
2709 }
2710
2711- pub fn with_dmarc_dkim_result(mut self, dkim: DMARCResult) -> Self {
2712+ pub fn with_dmarc_dkim_result(mut self, dkim: DmarcResult) -> Self {
2713 self.row.policy_evaluated.dkim = dkim;
2714 self
2715 }
2716
2717- pub fn dmarc_spf_result(&self) -> DMARCResult {
2718+ pub fn dmarc_spf_result(&self) -> DmarcResult {
2719 self.row.policy_evaluated.spf
2720 }
2721
2722- pub fn with_dmarc_spf_result(mut self, spf: DMARCResult) -> Self {
2723+ pub fn with_dmarc_spf_result(mut self, spf: DmarcResult) -> Self {
2724 self.row.policy_evaluated.spf = spf;
2725 self
2726 }
2727 @@ -404,11 +404,11 @@ impl DKIMAuthResult {
2728 self
2729 }
2730
2731- pub fn result(&self) -> DKIMResult {
2732+ pub fn result(&self) -> DkimResult {
2733 self.result
2734 }
2735
2736- pub fn with_result(mut self, result: DKIMResult) -> Self {
2737+ pub fn with_result(mut self, result: DkimResult) -> Self {
2738 self.result = result;
2739 self
2740 }
2741 @@ -446,11 +446,11 @@ impl SPFAuthResult {
2742 self
2743 }
2744
2745- pub fn result(&self) -> SPFResult {
2746+ pub fn result(&self) -> SpfResult {
2747 self.result
2748 }
2749
2750- pub fn with_result(mut self, result: SPFResult) -> Self {
2751+ pub fn with_result(mut self, result: SpfResult) -> Self {
2752 self.result = result;
2753 self
2754 }
2755 @@ -487,11 +487,11 @@ impl PolicyOverrideReason {
2756 }
2757 }
2758
2759- impl From<&crate::DMARCResult> for DMARCResult {
2760- fn from(result: &crate::DMARCResult) -> Self {
2761+ impl From<&crate::DmarcResult> for DmarcResult {
2762+ fn from(result: &crate::DmarcResult) -> Self {
2763 match result {
2764- crate::DMARCResult::Pass => DMARCResult::Pass,
2765- _ => DMARCResult::Fail,
2766+ crate::DmarcResult::Pass => DmarcResult::Pass,
2767+ _ => DmarcResult::Fail,
2768 }
2769 }
2770 }
2771 diff --git a/src/report/dmarc/parse.rs b/src/report/dmarc/parse.rs
2772index 64ab859..a30eefb 100644
2773--- a/src/report/dmarc/parse.rs
2774+++ b/src/report/dmarc/parse.rs
2775 @@ -17,10 +17,10 @@ use quick_xml::events::{BytesStart, Event};
2776 use quick_xml::reader::Reader;
2777
2778 use crate::report::{
2779- ActionDisposition, Alignment, AuthResult, DKIMAuthResult, DKIMResult, DMARCResult, DateRange,
2780- Disposition, Error, Extension, Identifier, PolicyEvaluated, PolicyOverride,
2781+ ActionDisposition, Alignment, AuthResult, DKIMAuthResult, DateRange, Disposition, DkimResult,
2782+ DmarcResult, Error, Extension, Identifier, PolicyEvaluated, PolicyOverride,
2783 PolicyOverrideReason, PolicyPublished, Record, Report, ReportMetadata, Row, SPFAuthResult,
2784- SPFDomainScope, SPFResult,
2785+ SPFDomainScope, SpfResult,
2786 };
2787
2788 impl Report {
2789 @@ -589,48 +589,48 @@ impl FromStr for PolicyOverride {
2790 }
2791 }
2792
2793- impl FromStr for DMARCResult {
2794+ impl FromStr for DmarcResult {
2795 type Err = ();
2796
2797 fn from_str(s: &str) -> Result<Self, Self::Err> {
2798 Ok(match s.as_bytes() {
2799- b"pass" => DMARCResult::Pass,
2800- b"fail" => DMARCResult::Fail,
2801- _ => DMARCResult::Unspecified,
2802+ b"pass" => DmarcResult::Pass,
2803+ b"fail" => DmarcResult::Fail,
2804+ _ => DmarcResult::Unspecified,
2805 })
2806 }
2807 }
2808
2809- impl FromStr for DKIMResult {
2810+ impl FromStr for DkimResult {
2811 type Err = ();
2812
2813 fn from_str(s: &str) -> Result<Self, Self::Err> {
2814 Ok(match s.as_bytes() {
2815- b"none" => DKIMResult::None,
2816- b"pass" => DKIMResult::Pass,
2817- b"fail" => DKIMResult::Fail,
2818- b"policy" => DKIMResult::Policy,
2819- b"neutral" => DKIMResult::Neutral,
2820- b"temperror" => DKIMResult::TempError,
2821- b"permerror" => DKIMResult::PermError,
2822- _ => DKIMResult::None,
2823+ b"none" => DkimResult::None,
2824+ b"pass" => DkimResult::Pass,
2825+ b"fail" => DkimResult::Fail,
2826+ b"policy" => DkimResult::Policy,
2827+ b"neutral" => DkimResult::Neutral,
2828+ b"temperror" => DkimResult::TempError,
2829+ b"permerror" => DkimResult::PermError,
2830+ _ => DkimResult::None,
2831 })
2832 }
2833 }
2834
2835- impl FromStr for SPFResult {
2836+ impl FromStr for SpfResult {
2837 type Err = ();
2838
2839 fn from_str(s: &str) -> Result<Self, Self::Err> {
2840 Ok(match s.as_bytes() {
2841- b"none" => SPFResult::None,
2842- b"pass" => SPFResult::Pass,
2843- b"fail" => SPFResult::Fail,
2844- b"softfail" => SPFResult::SoftFail,
2845- b"neutral" => SPFResult::Neutral,
2846- b"temperror" => SPFResult::TempError,
2847- b"permerror" => SPFResult::PermError,
2848- _ => SPFResult::None,
2849+ b"none" => SpfResult::None,
2850+ b"pass" => SpfResult::Pass,
2851+ b"fail" => SpfResult::Fail,
2852+ b"softfail" => SpfResult::SoftFail,
2853+ b"neutral" => SpfResult::Neutral,
2854+ b"temperror" => SpfResult::TempError,
2855+ b"permerror" => SpfResult::PermError,
2856+ _ => SpfResult::None,
2857 })
2858 }
2859 }
2860 diff --git a/src/report/mod.rs b/src/report/mod.rs
2861index e3bd93a..242e8cf 100644
2862--- a/src/report/mod.rs
2863+++ b/src/report/mod.rs
2864 @@ -73,7 +73,7 @@ pub struct PolicyPublished {
2865 impl Eq for PolicyPublished {}
2866
2867 #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
2868- pub enum DMARCResult {
2869+ pub enum DmarcResult {
2870 Pass,
2871 Fail,
2872 Unspecified,
2873 @@ -98,8 +98,8 @@ pub struct PolicyOverrideReason {
2874 #[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
2875 pub struct PolicyEvaluated {
2876 disposition: ActionDisposition,
2877- dkim: DMARCResult,
2878- spf: DMARCResult,
2879+ dkim: DmarcResult,
2880+ spf: DmarcResult,
2881 reason: Vec<PolicyOverrideReason>,
2882 }
2883
2884 @@ -124,7 +124,7 @@ pub struct Identifier {
2885 }
2886
2887 #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
2888- pub enum DKIMResult {
2889+ pub enum DkimResult {
2890 None,
2891 Pass,
2892 Fail,
2893 @@ -138,7 +138,7 @@ pub enum DKIMResult {
2894 pub struct DKIMAuthResult {
2895 domain: String,
2896 selector: String,
2897- result: DKIMResult,
2898+ result: DkimResult,
2899 human_result: Option<String>,
2900 }
2901
2902 @@ -150,7 +150,7 @@ pub enum SPFDomainScope {
2903 }
2904
2905 #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
2906- pub enum SPFResult {
2907+ pub enum SpfResult {
2908 None,
2909 Neutral,
2910 Pass,
2911 @@ -164,7 +164,7 @@ pub enum SPFResult {
2912 pub struct SPFAuthResult {
2913 domain: String,
2914 scope: SPFDomainScope,
2915- result: SPFResult,
2916+ result: SpfResult,
2917 human_result: Option<String>,
2918 }
2919
2920 @@ -221,9 +221,9 @@ impl Default for ActionDisposition {
2921 }
2922 }
2923
2924- impl Default for DMARCResult {
2925+ impl Default for DmarcResult {
2926 fn default() -> Self {
2927- DMARCResult::Unspecified
2928+ DmarcResult::Unspecified
2929 }
2930 }
2931
2932 @@ -233,15 +233,15 @@ impl Default for PolicyOverride {
2933 }
2934 }
2935
2936- impl Default for DKIMResult {
2937+ impl Default for DkimResult {
2938 fn default() -> Self {
2939- DKIMResult::None
2940+ DkimResult::None
2941 }
2942 }
2943
2944- impl Default for SPFResult {
2945+ impl Default for SpfResult {
2946 fn default() -> Self {
2947- SPFResult::None
2948+ SpfResult::None
2949 }
2950 }
2951
2952 diff --git a/src/spf/mod.rs b/src/spf/mod.rs
2953index 799b975..ea912b1 100644
2954--- a/src/spf/mod.rs
2955+++ b/src/spf/mod.rs
2956 @@ -17,7 +17,7 @@ use std::{
2957 net::{Ipv4Addr, Ipv6Addr},
2958 };
2959
2960- use crate::{is_within_pct, SPFOutput, SPFResult, Version};
2961+ use crate::{is_within_pct, SpfOutput, SpfResult, Version};
2962
2963 /*
2964 "+" pass
2965 @@ -131,7 +131,7 @@ pub enum Macro {
2966 }
2967
2968 #[derive(Debug, PartialEq, Eq, Clone)]
2969- pub struct SPF {
2970+ pub struct Spf {
2971 version: Version,
2972 directives: Vec<Directive>,
2973 exp: Option<Macro>,
2974 @@ -170,31 +170,31 @@ impl Mechanism {
2975 }
2976 }
2977
2978- impl TryFrom<&str> for SPFResult {
2979+ impl TryFrom<&str> for SpfResult {
2980 type Error = ();
2981
2982 fn try_from(value: &str) -> Result<Self, Self::Error> {
2983 if value.eq_ignore_ascii_case("pass") {
2984- Ok(SPFResult::Pass)
2985+ Ok(SpfResult::Pass)
2986 } else if value.eq_ignore_ascii_case("fail") {
2987- Ok(SPFResult::Fail)
2988+ Ok(SpfResult::Fail)
2989 } else if value.eq_ignore_ascii_case("softfail") {
2990- Ok(SPFResult::SoftFail)
2991+ Ok(SpfResult::SoftFail)
2992 } else if value.eq_ignore_ascii_case("neutral") {
2993- Ok(SPFResult::Neutral)
2994+ Ok(SpfResult::Neutral)
2995 } else if value.eq_ignore_ascii_case("temperror") {
2996- Ok(SPFResult::TempError)
2997+ Ok(SpfResult::TempError)
2998 } else if value.eq_ignore_ascii_case("permerror") {
2999- Ok(SPFResult::PermError)
3000+ Ok(SpfResult::PermError)
3001 } else if value.eq_ignore_ascii_case("none") {
3002- Ok(SPFResult::None)
3003+ Ok(SpfResult::None)
3004 } else {
3005 Err(())
3006 }
3007 }
3008 }
3009
3010- impl TryFrom<String> for SPFResult {
3011+ impl TryFrom<String> for SpfResult {
3012 type Error = ();
3013
3014 fn try_from(value: String) -> Result<Self, Self::Error> {
3015 @@ -202,32 +202,32 @@ impl TryFrom<String> for SPFResult {
3016 }
3017 }
3018
3019- impl SPFOutput {
3020+ impl SpfOutput {
3021 pub(crate) fn new(domain: String) -> Self {
3022- SPFOutput {
3023- result: SPFResult::None,
3024+ SpfOutput {
3025+ result: SpfResult::None,
3026 report: None,
3027 explanation: None,
3028 domain,
3029 }
3030 }
3031
3032- pub(crate) fn with_result(mut self, result: SPFResult) -> Self {
3033+ pub(crate) fn with_result(mut self, result: SpfResult) -> Self {
3034 self.result = result;
3035 self
3036 }
3037
3038- pub(crate) fn with_report(mut self, spf: &SPF) -> Self {
3039+ pub(crate) fn with_report(mut self, spf: &Spf) -> Self {
3040 match &spf.ra {
3041 Some(ra) if is_within_pct(spf.rp) => {
3042 if match self.result {
3043- SPFResult::Fail => (spf.rr & RR_FAIL) != 0,
3044- SPFResult::SoftFail => (spf.rr & RR_SOFTFAIL) != 0,
3045- SPFResult::Neutral | SPFResult::None => (spf.rr & RR_NEUTRAL_NONE) != 0,
3046- SPFResult::TempError | SPFResult::PermError => {
3047+ SpfResult::Fail => (spf.rr & RR_FAIL) != 0,
3048+ SpfResult::SoftFail => (spf.rr & RR_SOFTFAIL) != 0,
3049+ SpfResult::Neutral | SpfResult::None => (spf.rr & RR_NEUTRAL_NONE) != 0,
3050+ SpfResult::TempError | SpfResult::PermError => {
3051 (spf.rr & RR_TEMP_PERM_ERROR) != 0
3052 }
3053- SPFResult::Pass => false,
3054+ SpfResult::Pass => false,
3055 } {
3056 self.report = format!("{}@{}", String::from_utf8_lossy(ra), self.domain).into();
3057 }
3058 @@ -242,7 +242,7 @@ impl SPFOutput {
3059 self
3060 }
3061
3062- pub fn result(&self) -> SPFResult {
3063+ pub fn result(&self) -> SpfResult {
3064 self.result
3065 }
3066
3067 diff --git a/src/spf/parse.rs b/src/spf/parse.rs
3068index f864351..46912d3 100644
3069--- a/src/spf/parse.rs
3070+++ b/src/spf/parse.rs
3071 @@ -19,12 +19,12 @@ use crate::{
3072 };
3073
3074 use super::{
3075- Directive, Macro, Mechanism, Qualifier, Variable, RR_FAIL, RR_NEUTRAL_NONE, RR_SOFTFAIL,
3076- RR_TEMP_PERM_ERROR, SPF,
3077+ Directive, Macro, Mechanism, Qualifier, Spf, Variable, RR_FAIL, RR_NEUTRAL_NONE, RR_SOFTFAIL,
3078+ RR_TEMP_PERM_ERROR,
3079 };
3080
3081- impl TxtRecordParser for SPF {
3082- fn parse(bytes: &[u8]) -> crate::Result<SPF> {
3083+ impl TxtRecordParser for Spf {
3084+ fn parse(bytes: &[u8]) -> crate::Result<Spf> {
3085 let mut record = bytes.iter();
3086 if !matches!(record.key(), Some(k) if k == V)
3087 || !record.match_bytes(b"spf1")
3088 @@ -33,7 +33,7 @@ impl TxtRecordParser for SPF {
3089 return Err(Error::InvalidRecordType);
3090 }
3091
3092- let mut spf = SPF {
3093+ let mut spf = Spf {
3094 version: Version::V1,
3095 directives: Vec::new(),
3096 redirect: None,
3097 @@ -738,8 +738,8 @@ mod test {
3098 use crate::{
3099 common::parse::TxtRecordParser,
3100 spf::{
3101- Directive, Macro, Mechanism, Qualifier, Variable, Version, RR_FAIL, RR_NEUTRAL_NONE,
3102- RR_SOFTFAIL, RR_TEMP_PERM_ERROR, SPF,
3103+ Directive, Macro, Mechanism, Qualifier, Spf, Variable, Version, RR_FAIL,
3104+ RR_NEUTRAL_NONE, RR_SOFTFAIL, RR_TEMP_PERM_ERROR,
3105 },
3106 };
3107
3108 @@ -750,7 +750,7 @@ mod test {
3109 for (record, expected_result) in [
3110 (
3111 "v=spf1 +mx a:colo.example.com/28 -all",
3112- SPF {
3113+ Spf {
3114 version: Version::V1,
3115 ra: None,
3116 rp: 100,
3117 @@ -780,7 +780,7 @@ mod test {
3118 ),
3119 (
3120 "v=spf1 a:A.EXAMPLE.COM -all",
3121- SPF {
3122+ Spf {
3123 version: Version::V1,
3124 ra: None,
3125 rp: 100,
3126 @@ -802,7 +802,7 @@ mod test {
3127 ),
3128 (
3129 "v=spf1 +mx -all",
3130- SPF {
3131+ Spf {
3132 version: Version::V1,
3133 ra: None,
3134 rp: 100,
3135 @@ -824,7 +824,7 @@ mod test {
3136 ),
3137 (
3138 "v=spf1 +mx redirect=_spf.example.com",
3139- SPF {
3140+ Spf {
3141 version: Version::V1,
3142 ra: None,
3143 rp: 100,
3144 @@ -843,7 +843,7 @@ mod test {
3145 ),
3146 (
3147 "v=spf1 a mx -all",
3148- SPF {
3149+ Spf {
3150 version: Version::V1,
3151 ra: None,
3152 rp: 100,
3153 @@ -873,7 +873,7 @@ mod test {
3154 ),
3155 (
3156 "v=spf1 include:example.com include:example.org -all",
3157- SPF {
3158+ Spf {
3159 version: Version::V1,
3160 ra: None,
3161 rp: 100,
3162 @@ -899,7 +899,7 @@ mod test {
3163 ),
3164 (
3165 "v=spf1 exists:%{ir}.%{l1r+-}._spf.%{d} -all",
3166- SPF {
3167+ Spf {
3168 version: Version::V1,
3169 ra: None,
3170 rp: 100,
3171 @@ -943,7 +943,7 @@ mod test {
3172 ),
3173 (
3174 "v=spf1 mx -all exp=explain._spf.%{d}",
3175- SPF {
3176+ Spf {
3177 version: Version::V1,
3178 ra: None,
3179 rp: 100,
3180 @@ -975,7 +975,7 @@ mod test {
3181 ),
3182 (
3183 "v=spf1 ip4:192.0.2.1 ip4:192.0.2.129 -all",
3184- SPF {
3185+ Spf {
3186 version: Version::V1,
3187 ra: None,
3188 rp: 100,
3189 @@ -1003,7 +1003,7 @@ mod test {
3190 ),
3191 (
3192 "v=spf1 ip4:192.0.2.0/24 mx -all",
3193- SPF {
3194+ Spf {
3195 version: Version::V1,
3196 ra: None,
3197 rp: 100,
3198 @@ -1032,7 +1032,7 @@ mod test {
3199 ),
3200 (
3201 "v=spf1 mx/30 mx:example.org/30 -all",
3202- SPF {
3203+ Spf {
3204 version: Version::V1,
3205 ra: None,
3206 rp: 100,
3207 @@ -1062,7 +1062,7 @@ mod test {
3208 ),
3209 (
3210 "v=spf1 ptr -all",
3211- SPF {
3212+ Spf {
3213 version: Version::V1,
3214 ra: None,
3215 rp: 100,
3216 @@ -1082,7 +1082,7 @@ mod test {
3217 ),
3218 (
3219 "v=spf1 exists:%{l1r+}.%{d}",
3220- SPF {
3221+ Spf {
3222 version: Version::V1,
3223 ra: None,
3224 rp: 100,
3225 @@ -1115,7 +1115,7 @@ mod test {
3226 ),
3227 (
3228 "v=spf1 exists:%{ir}.%{l1r+}.%{d}",
3229- SPF {
3230+ Spf {
3231 version: Version::V1,
3232 ra: None,
3233 rp: 100,
3234 @@ -1156,7 +1156,7 @@ mod test {
3235 ),
3236 (
3237 "v=spf1 exists:_h.%{h}._l.%{l}._o.%{o}._i.%{i}._spf.%{d} ?all",
3238- SPF {
3239+ Spf {
3240 version: Version::V1,
3241 ra: None,
3242 rp: 100,
3243 @@ -1217,7 +1217,7 @@ mod test {
3244 ),
3245 (
3246 "v=spf1 mx ?exists:%{ir}.whitelist.example.org -all",
3247- SPF {
3248+ Spf {
3249 version: Version::V1,
3250 ra: None,
3251 rp: 100,
3252 @@ -1254,7 +1254,7 @@ mod test {
3253 ),
3254 (
3255 "v=spf1 mx exists:%{l}._%-spf_%_verify%%.%{d} -all",
3256- SPF {
3257+ Spf {
3258 version: Version::V1,
3259 ra: None,
3260 rp: 100,
3261 @@ -1298,7 +1298,7 @@ mod test {
3262 ),
3263 (
3264 "v=spf1 mx redirect=%{l1r+}._at_.%{o,=_/}._spf.%{d}",
3265- SPF {
3266+ Spf {
3267 version: Version::V1,
3268 ra: None,
3269 rp: 100,
3270 @@ -1345,7 +1345,7 @@ mod test {
3271 ),
3272 (
3273 "v=spf1 -ip4:192.0.2.0/24 a//96 +all",
3274- SPF {
3275+ Spf {
3276 version: Version::V1,
3277 ra: None,
3278 rp: 100,
3279 @@ -1377,7 +1377,7 @@ mod test {
3280 "v=spf1 +mx/11//100 ~a:domain.com/12/123 ?ip6:::1 ",
3281 "-ip6:a::b/111 ip6:1080::8:800:68.0.3.1/96 "
3282 ),
3283- SPF {
3284+ Spf {
3285 version: Version::V1,
3286 ra: None,
3287 rp: 100,
3288 @@ -1427,7 +1427,7 @@ mod test {
3289 ),
3290 (
3291 concat!("v=spf1 mx:example.org -all ra=postmaster rp=15 rr=e:f:s:n"),
3292- SPF {
3293+ Spf {
3294 version: Version::V1,
3295 ra: b"postmaster".to_vec().into(),
3296 rp: 15,
3297 @@ -1449,7 +1449,7 @@ mod test {
3298 ),
3299 ] {
3300 assert_eq!(
3301- SPF::parse(record.as_bytes())
3302+ Spf::parse(record.as_bytes())
3303 .unwrap_or_else(|err| panic!("{:?} : {:?}", record, err)),
3304 expected_result,
3305 "{}",
3306 diff --git a/src/spf/verify.rs b/src/spf/verify.rs
3307index cd1b697..e85dd4c 100644
3308--- a/src/spf/verify.rs
3309+++ b/src/spf/verify.rs
3310 @@ -13,12 +13,13 @@ use std::{
3311 time::Instant,
3312 };
3313
3314- use crate::{Error, Policy, Resolver, SPFOutput, SPFResult};
3315+ use crate::{Error, Policy, Resolver, SpfOutput, SpfResult};
3316
3317- use super::{Macro, Mechanism, Qualifier, Variables, SPF};
3318+ use super::{Macro, Mechanism, Qualifier, Spf, Variables};
3319
3320 impl Resolver {
3321- pub async fn verify_spf_helo(&self, ip: IpAddr, helo_domain: &str) -> SPFOutput {
3322+ /// Verifies the SPF EHLO identity
3323+ pub async fn verify_spf_helo(&self, ip: IpAddr, helo_domain: &str) -> SpfOutput {
3324 if helo_domain.has_labels() {
3325 self.check_host(
3326 ip,
3327 @@ -28,16 +29,17 @@ impl Resolver {
3328 )
3329 .await
3330 } else {
3331- SPFOutput::new(helo_domain.to_string()).with_result(SPFResult::None)
3332+ SpfOutput::new(helo_domain.to_string()).with_result(SpfResult::None)
3333 }
3334 }
3335
3336+ /// Verifies the SPF MAIL FROM identity
3337 pub async fn verify_spf_sender(
3338 &self,
3339 ip: IpAddr,
3340 helo_domain: &str,
3341 sender: &str,
3342- ) -> SPFOutput {
3343+ ) -> SpfOutput {
3344 self.check_host(
3345 ip,
3346 sender.rsplit_once('@').map_or(helo_domain, |(_, d)| d),
3347 @@ -47,12 +49,13 @@ impl Resolver {
3348 .await
3349 }
3350
3351- pub async fn verify_spf(&self, ip: IpAddr, helo_domain: &str, mail_from: &str) -> SPFOutput {
3352+ /// Verifies both the SPF EHLO and MAIL FROM identities
3353+ pub async fn verify_spf(&self, ip: IpAddr, helo_domain: &str, mail_from: &str) -> SpfOutput {
3354 // Verify HELO identity
3355 let output = self.verify_spf_helo(ip, helo_domain).await;
3356 match output.result() {
3357- SPFResult::TempError | SPFResult::Pass => (),
3358- SPFResult::None | SPFResult::PermError if self.verify_policy != Policy::VeryStrict => {}
3359+ SpfResult::TempError | SpfResult::Pass => (),
3360+ SpfResult::None | SpfResult::PermError if self.verify_policy != Policy::VeryStrict => {}
3361 _ => return output,
3362 }
3363
3364 @@ -67,10 +70,10 @@ impl Resolver {
3365 domain: &str,
3366 helo_domain: &str,
3367 sender: &str,
3368- ) -> SPFOutput {
3369- let output = SPFOutput::new(domain.to_string());
3370+ ) -> SpfOutput {
3371+ let output = SpfOutput::new(domain.to_string());
3372 if domain.is_empty() || domain.len() > 63 || !domain.has_labels() {
3373- return output.with_result(SPFResult::None);
3374+ return output.with_result(SpfResult::None);
3375 }
3376 let mut vars = Variables::new();
3377 let mut has_p_var = false;
3378 @@ -85,7 +88,7 @@ impl Resolver {
3379 vars.set_helo_domain(helo_domain.as_bytes());
3380
3381 let mut lookup_limit = LookupLimit::new();
3382- let mut spf_record = match self.txt_lookup::<SPF>(domain).await {
3383+ let mut spf_record = match self.txt_lookup::<Spf>(domain).await {
3384 Ok(spf_record) => spf_record,
3385 Err(err) => return output.with_result(err.into()),
3386 };
3387 @@ -101,7 +104,7 @@ impl Resolver {
3388 if !has_p_var && directive.mechanism.needs_ptr() {
3389 if !lookup_limit.can_lookup() {
3390 return output
3391- .with_result(SPFResult::PermError)
3392+ .with_result(SpfResult::PermError)
3393 .with_report(&spf_record);
3394 }
3395 if let Some(ptr) = self
3396 @@ -126,7 +129,7 @@ impl Resolver {
3397 } => {
3398 if !lookup_limit.can_lookup() {
3399 return output
3400- .with_result(SPFResult::PermError)
3401+ .with_result(SpfResult::PermError)
3402 .with_report(&spf_record);
3403 }
3404 match self
3405 @@ -142,7 +145,7 @@ impl Resolver {
3406 Ok(false) | Err(Error::DNSRecordNotFound(_)) => false,
3407 Err(_) => {
3408 return output
3409- .with_result(SPFResult::TempError)
3410+ .with_result(SpfResult::TempError)
3411 .with_report(&spf_record);
3412 }
3413 }
3414 @@ -154,7 +157,7 @@ impl Resolver {
3415 } => {
3416 if !lookup_limit.can_lookup() {
3417 return output
3418- .with_result(SPFResult::PermError)
3419+ .with_result(SpfResult::PermError)
3420 .with_report(&spf_record);
3421 }
3422
3423 @@ -167,7 +170,7 @@ impl Resolver {
3424 for record in records.iter() {
3425 if !lookup_limit.can_lookup() {
3426 return output
3427- .with_result(SPFResult::PermError)
3428+ .with_result(SpfResult::PermError)
3429 .with_report(&spf_record);
3430 }
3431
3432 @@ -182,7 +185,7 @@ impl Resolver {
3433 Ok(false) | Err(Error::DNSRecordNotFound(_)) => (),
3434 Err(_) => {
3435 return output
3436- .with_result(SPFResult::TempError)
3437+ .with_result(SpfResult::TempError)
3438 .with_report(&spf_record);
3439 }
3440 }
3441 @@ -191,7 +194,7 @@ impl Resolver {
3442 Err(Error::DNSRecordNotFound(_)) => (),
3443 Err(_) => {
3444 return output
3445- .with_result(SPFResult::TempError)
3446+ .with_result(SpfResult::TempError)
3447 .with_report(&spf_record);
3448 }
3449 }
3450 @@ -200,12 +203,12 @@ impl Resolver {
3451 Mechanism::Include { macro_string } => {
3452 if !lookup_limit.can_lookup() {
3453 return output
3454- .with_result(SPFResult::PermError)
3455+ .with_result(SpfResult::PermError)
3456 .with_report(&spf_record);
3457 }
3458
3459 let target_name = macro_string.eval(&vars, &domain, true);
3460- match self.txt_lookup::<SPF>(target_name.as_ref()).await {
3461+ match self.txt_lookup::<Spf>(target_name.as_ref()).await {
3462 Ok(included_spf) => {
3463 let new_domain = target_name.to_string();
3464 include_stack.push((
3465 @@ -224,12 +227,12 @@ impl Resolver {
3466 | Error::ParseError,
3467 ) => {
3468 return output
3469- .with_result(SPFResult::PermError)
3470+ .with_result(SpfResult::PermError)
3471 .with_report(&spf_record)
3472 }
3473 Err(_) => {
3474 return output
3475- .with_result(SPFResult::TempError)
3476+ .with_result(SpfResult::TempError)
3477 .with_report(&spf_record)
3478 }
3479 }
3480 @@ -237,7 +240,7 @@ impl Resolver {
3481 Mechanism::Ptr { macro_string } => {
3482 if !lookup_limit.can_lookup() {
3483 return output
3484- .with_result(SPFResult::PermError)
3485+ .with_result(SpfResult::PermError)
3486 .with_report(&spf_record);
3487 }
3488
3489 @@ -265,7 +268,7 @@ impl Resolver {
3490 Mechanism::Exists { macro_string } => {
3491 if !lookup_limit.can_lookup() {
3492 return output
3493- .with_result(SPFResult::PermError)
3494+ .with_result(SpfResult::PermError)
3495 .with_report(&spf_record);
3496 }
3497
3498 @@ -276,7 +279,7 @@ impl Resolver {
3499 result
3500 } else {
3501 return output
3502- .with_result(SPFResult::TempError)
3503+ .with_result(SpfResult::TempError)
3504 .with_report(&spf_record);
3505 }
3506 }
3507 @@ -293,7 +296,7 @@ impl Resolver {
3508 directives = spf_record.directives.iter().enumerate().skip(prev_pos);
3509 let (_, directive) = directives.next().unwrap();
3510
3511- if matches!(result, Some(SPFResult::Pass)) {
3512+ if matches!(result, Some(SpfResult::Pass)) {
3513 result = Some((&directive.qualifier).into());
3514 break;
3515 } else {
3516 @@ -306,12 +309,12 @@ impl Resolver {
3517 if let (Some(macro_string), None) = (&spf_record.redirect, &result) {
3518 if !lookup_limit.can_lookup() {
3519 return output
3520- .with_result(SPFResult::PermError)
3521+ .with_result(SpfResult::PermError)
3522 .with_report(&spf_record);
3523 }
3524
3525 let target_name = macro_string.eval(&vars, &domain, true);
3526- match self.txt_lookup::<SPF>(target_name.as_ref()).await {
3527+ match self.txt_lookup::<Spf>(target_name.as_ref()).await {
3528 Ok(redirect_spf) => {
3529 let new_domain = target_name.to_string();
3530 spf_record = redirect_spf;
3531 @@ -326,12 +329,12 @@ impl Resolver {
3532 | Error::ParseError,
3533 ) => {
3534 return output
3535- .with_result(SPFResult::PermError)
3536+ .with_result(SpfResult::PermError)
3537 .with_report(&spf_record)
3538 }
3539 Err(_) => {
3540 return output
3541- .with_result(SPFResult::TempError)
3542+ .with_result(SpfResult::TempError)
3543 .with_report(&spf_record)
3544 }
3545 }
3546 @@ -342,20 +345,20 @@ impl Resolver {
3547 }
3548
3549 // Evaluate explain
3550- if let (Some(macro_string), Some(SPFResult::Fail { .. })) = (&spf_record.exp, &result) {
3551+ if let (Some(macro_string), Some(SpfResult::Fail { .. })) = (&spf_record.exp, &result) {
3552 if let Ok(macro_string) = self
3553 .txt_lookup::<Macro>(macro_string.eval(&vars, &domain, true).to_string())
3554 .await
3555 {
3556 return output
3557- .with_result(SPFResult::Fail)
3558+ .with_result(SpfResult::Fail)
3559 .with_explanation(macro_string.eval(&vars, &domain, false).to_string())
3560 .with_report(&spf_record);
3561 }
3562 }
3563
3564 output
3565- .with_result(result.unwrap_or(SPFResult::Neutral))
3566+ .with_result(result.unwrap_or(SpfResult::Neutral))
3567 .with_report(&spf_record)
3568 }
3569
3570 @@ -430,23 +433,23 @@ impl IpMask for Ipv4Addr {
3571 }
3572 }
3573
3574- impl From<&Qualifier> for SPFResult {
3575+ impl From<&Qualifier> for SpfResult {
3576 fn from(q: &Qualifier) -> Self {
3577 match q {
3578- Qualifier::Pass => SPFResult::Pass,
3579- Qualifier::Fail => SPFResult::Fail,
3580- Qualifier::SoftFail => SPFResult::SoftFail,
3581- Qualifier::Neutral => SPFResult::Neutral,
3582+ Qualifier::Pass => SpfResult::Pass,
3583+ Qualifier::Fail => SpfResult::Fail,
3584+ Qualifier::SoftFail => SpfResult::SoftFail,
3585+ Qualifier::Neutral => SpfResult::Neutral,
3586 }
3587 }
3588 }
3589
3590- impl From<Error> for SPFResult {
3591+ impl From<Error> for SpfResult {
3592 fn from(err: Error) -> Self {
3593 match err {
3594- Error::DNSRecordNotFound(_) | Error::InvalidRecordType => SPFResult::None,
3595- Error::ParseError => SPFResult::PermError,
3596- _ => SPFResult::TempError,
3597+ Error::DNSRecordNotFound(_) | Error::InvalidRecordType => SpfResult::None,
3598+ Error::ParseError => SpfResult::PermError,
3599+ _ => SpfResult::TempError,
3600 }
3601 }
3602 }
3603 @@ -509,8 +512,8 @@ mod test {
3604
3605 use crate::{
3606 common::parse::TxtRecordParser,
3607- spf::{Macro, SPF},
3608- Resolver, SPFResult, MX,
3609+ spf::{Macro, Spf},
3610+ Resolver, SpfResult, MX,
3611 };
3612
3613 #[tokio::test]
3614 @@ -548,7 +551,7 @@ mod test {
3615 let (name, record) = record.trim().split_once(' ').unwrap();
3616 resolver.txt_add(
3617 name.trim().to_string(),
3618- SPF::parse(record.as_bytes()),
3619+ Spf::parse(record.as_bytes()),
3620 valid_until,
3621 );
3622 } else if let Some(record) = line.strip_prefix("exp:") {
3623 @@ -616,7 +619,7 @@ mod test {
3624 client_ip = value.trim().parse().unwrap();
3625 } else if let Some(value) = line.strip_prefix("expect:") {
3626 let value = value.trim();
3627- let (result, exp): (SPFResult, &str) =
3628+ let (result, exp): (SpfResult, &str) =
3629 if let Some((result, exp)) = value.split_once(' ') {
3630 (result.trim().try_into().unwrap(), exp.trim())
3631 } else {