From 49864074cc2cd85caede425cafd1ef8f7f3be0e2 Mon Sep 17 00:00:00 2001 From: shukuru Date: Sun, 18 Feb 2024 23:42:11 +0100 Subject: Add raw DMARC report parsing - Parse deserialized data structure to a more versatile interface. --- src/data_structures.rs | 18 +++++----- src/parser/deserializer.rs | 89 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 9 deletions(-) diff --git a/src/data_structures.rs b/src/data_structures.rs index ef1bcc2..3e4e4ac 100644 --- a/src/data_structures.rs +++ b/src/data_structures.rs @@ -86,21 +86,21 @@ pub struct SPF { // Rust interface DS for HTML generation pub enum DMARCPolicy { - None, - Quarantine, - Reject, + None(String), + Quarantine(String), + Reject(String), } pub enum DMARCAlignment { - Relaxed, - Strict, + Relaxed(String), + Strict(String), } pub enum DMARCFailureOption { - AlignedPassFailure, - OtherAlignedPassFailure, - SignatureAlignmentFailure, - SPFFailure, + AlignedPassFailure(String), + OtherAlignedPassFailure(String), + SignatureAlignmentFailure(String), + SPFFailure(String), } pub struct DMARCRecord { diff --git a/src/parser/deserializer.rs b/src/parser/deserializer.rs index e2bd108..fc81b66 100644 --- a/src/parser/deserializer.rs +++ b/src/parser/deserializer.rs @@ -1,4 +1,5 @@ use std::io::Read; +use std::str::FromStr; use crate::data_structures; @@ -8,6 +9,79 @@ fn deserialize_dmarc_report( serde_xml_rs::from_reader(reader) } +impl From for data_structures::DMARCAlignment { + fn from(item: String) -> Self { + match item.as_str() { + "r" => data_structures::DMARCAlignment::Relaxed(item), + "s" => data_structures::DMARCAlignment::Strict(item), + _ => { + panic!("DMARC alignment value '{:?}' is not allowed", item); + } + } + } +} + +impl From for data_structures::DMARCPolicy { + fn from(item: String) -> Self { + match item.as_str() { + "none" => data_structures::DMARCPolicy::None(item), + "quarantine" => data_structures::DMARCPolicy::Quarantine(item), + "reject" => data_structures::DMARCPolicy::Reject(item), + _ => { + panic!("DMARC policy value '{:?}' is not allowed", item); + } + } + } +} + +impl From for data_structures::DMARCFailureOption { + fn from(item: String) -> Self { + match item.as_str() { + "0" => data_structures::DMARCFailureOption::AlignedPassFailure(item), + "1" => data_structures::DMARCFailureOption::OtherAlignedPassFailure(item), + "2" => data_structures::DMARCFailureOption::SignatureAlignmentFailure(item), + "3" => data_structures::DMARCFailureOption::SPFFailure(item), + _ => { + panic!("DMARC failure option '{:?}' is not allowed", item); + } + } + } +} + +impl data_structures::DMARCReport { + pub fn from_raw_report(report: data_structures::Feedback) -> data_structures::DMARCReport { + data_structures::DMARCReport { + id: report.report_metadata.report_id, + org: report.report_metadata.org_name, + record: data_structures::DMARCRecord { + domain: report.policy_published.domain, + adkim: data_structures::DMARCAlignment::from(report.policy_published.adkim), + aspf: data_structures::DMARCAlignment::from(report.policy_published.aspf), + policy: data_structures::DMARCPolicy::from(report.policy_published.p), + subdomain_policy: data_structures::DMARCPolicy::from(report.policy_published.sp), + percentage: match u64::from_str(&report.policy_published.pct) { + Ok(pct) => pct, + Err(_) => { + panic!( + "Percentage value '{:?}' for DMARC record is not valid", + report.policy_published.pct + ); + } + }, + failure_opt: data_structures::DMARCFailureOption::from(report.policy_published.fo), + }, + dkim_pass: match report.record.auth_results.dkim.result.as_str() { + "pass" => true, + _ => false, + }, + spf_pass: match report.record.auth_results.spf.result.as_str() { + "pass" => true, + _ => false, + }, + } + } +} + #[cfg(test)] mod test { use super::*; @@ -63,4 +137,19 @@ mod test { } } } + + #[test] + fn test_parse_dmarc_report() { + let dmarc_report_file = File::open("fixtures/dmarc_report_fixture.xml").unwrap(); + + let reader = BufReader::new(dmarc_report_file); + + let raw_report = deserialize_dmarc_report(reader).unwrap(); + + let expected_dmarc_report_id = "3374fb5148ba40c1a5cf8e3d36f34a34".to_string(); + + let dmarc_report = data_structures::DMARCReport::from_raw_report(raw_report); + + assert_eq!(dmarc_report.id, expected_dmarc_report_id); + } } -- cgit v1.2.3