From 3bffae34eb3196fbe9a2cbc33c5c52e59461a5f4 Mon Sep 17 00:00:00 2001 From: shukuru Date: Sun, 18 Feb 2024 22:11:16 +0100 Subject: Update XML parsing feature - Implements DMARC raw deserialization - Add test - Add a fixture --- Cargo.lock | 1 + Cargo.toml | 2 +- examples/dmarc_report_example.xml | 50 --------------- fixtures/dmarc_report_fixture.xml | 50 +++++++++++++++ src/data_structures.rs | 125 ++++++++++++++++++++++++++++++++------ src/main.rs | 3 + src/parser/deserializer.rs | 86 ++++++++++++++++++++++++++ src/parser/mod.rs | 1 + 8 files changed, 248 insertions(+), 70 deletions(-) delete mode 100644 examples/dmarc_report_example.xml create mode 100644 fixtures/dmarc_report_fixture.xml create mode 100644 src/parser/deserializer.rs create mode 100644 src/parser/mod.rs diff --git a/Cargo.lock b/Cargo.lock index f1524d1..4afd2e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6,6 +6,7 @@ version = 3 name = "cramd" version = "0.1.0" dependencies = [ + "serde", "serde-xml-rs", ] diff --git a/Cargo.toml b/Cargo.toml index 5639855..54749e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,10 +9,10 @@ homepage="https://mail.boitalett.re" description="DMARC reports reader daemon" authors=["shukuru "] readme="README.md" -license="BSD-3-Clause" license-file="LICENSE" keywords=["dmarc", "mail", "network", "daemon"] [dependencies] +serde = { version = "1.0.196", features = ["serde_derive"] } serde-xml-rs = "0.6.0" diff --git a/examples/dmarc_report_example.xml b/examples/dmarc_report_example.xml deleted file mode 100644 index d560d85..0000000 --- a/examples/dmarc_report_example.xml +++ /dev/null @@ -1,50 +0,0 @@ - - - 1.0 - - Enterprise Outlook - dmarcreport@microsoft.com - 3374fb5148ba40c1a5cf8e3d36f34a34 - - 1707436800 - 1707523200 - - - - boitalett.re - s - s -

reject

- reject - 100 - 0 -
- - - 195.154.102.43 - 1 - - none - pass - pass - - - - babilou.com - boitalett.re - boitalett.re - - - - boitalett.re - 2023101501 - pass - - - boitalett.re - mfrom - pass - - - -
\ No newline at end of file diff --git a/fixtures/dmarc_report_fixture.xml b/fixtures/dmarc_report_fixture.xml new file mode 100644 index 0000000..d560d85 --- /dev/null +++ b/fixtures/dmarc_report_fixture.xml @@ -0,0 +1,50 @@ + + + 1.0 + + Enterprise Outlook + dmarcreport@microsoft.com + 3374fb5148ba40c1a5cf8e3d36f34a34 + + 1707436800 + 1707523200 + + + + boitalett.re + s + s +

reject

+ reject + 100 + 0 +
+ + + 195.154.102.43 + 1 + + none + pass + pass + + + + babilou.com + boitalett.re + boitalett.re + + + + boitalett.re + 2023101501 + pass + + + boitalett.re + mfrom + pass + + + +
\ No newline at end of file diff --git a/src/data_structures.rs b/src/data_structures.rs index a90b7b0..ef1bcc2 100644 --- a/src/data_structures.rs +++ b/src/data_structures.rs @@ -1,35 +1,122 @@ -enum DMARCPolicy { +use serde::Deserialize; + +// Raw DS to deserialize XML report + +#[derive(Debug, Deserialize)] +pub struct Feedback { + pub version: String, + pub report_metadata: ReportMetadata, + pub policy_published: PolicyPublished, + pub record: Record, +} + +#[derive(Debug, Deserialize)] +pub struct ReportMetadata { + pub org_name: String, + pub email: String, + pub report_id: String, + pub date_range: DateRange, +} + +#[derive(Debug, Deserialize)] +pub struct DateRange { + pub begin: String, + pub end: String, +} + +#[derive(Debug, Deserialize)] +pub struct PolicyPublished { + pub domain: String, + pub adkim: String, + pub aspf: String, + pub p: String, + pub sp: String, + pub pct: String, + pub fo: String, +} + +#[derive(Debug, Deserialize)] +pub struct Record { + pub row: Row, + pub identifiers: Identifiers, + pub auth_results: AuthResults, +} + +#[derive(Debug, Deserialize)] +pub struct Row { + pub source_ip: String, + pub count: String, + pub policy_evaluated: PolicyEvaluated, +} + +#[derive(Debug, Deserialize)] +pub struct PolicyEvaluated { + pub disposition: String, + pub dkim: String, + pub spf: String, +} + +#[derive(Debug, Deserialize)] +pub struct Identifiers { + pub envelope_to: String, + pub envelope_from: String, + pub header_from: String, +} + +#[derive(Debug, Deserialize)] +pub struct AuthResults { + pub dkim: DKIM, + pub spf: SPF, +} + +#[derive(Debug, Deserialize)] +pub struct DKIM { + pub domain: String, + pub selector: String, + pub result: String, +} + +#[derive(Debug, Deserialize)] +pub struct SPF { + pub domain: String, + pub scope: String, + pub result: String, +} + +// Rust interface DS for HTML generation + +pub enum DMARCPolicy { None, Quarantine, - Reject + Reject, } -enum DMARCAlignment { +pub enum DMARCAlignment { Relaxed, - Strict + Strict, } -enum DMARCFailureOption { +pub enum DMARCFailureOption { AlignedPassFailure, OtherAlignedPassFailure, SignatureAlignmentFailure, - SPFFailure + SPFFailure, } -struct DMARCRecord { - domain: String, - adkim: DMARCAlignment, - aspf: DMARCAlignment, - policy: DMARCPolicy, - subdomain_policy: DMARCPolicy, - percentage: u64, - failure_opt: DMARCFailureOption +pub struct DMARCRecord { + pub domain: String, + pub adkim: DMARCAlignment, + pub aspf: DMARCAlignment, + pub policy: DMARCPolicy, + pub subdomain_policy: DMARCPolicy, + pub percentage: u64, + pub failure_opt: DMARCFailureOption, } pub struct DMARCReport { - id: String, - org: String, - record: DMARCRecord, - dkim_pass: bool, - spf_pass: bool + pub id: String, + pub org: String, + pub record: DMARCRecord, + pub dkim_pass: bool, + pub spf_pass: bool, } diff --git a/src/main.rs b/src/main.rs index e7a11a9..2125cf4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,6 @@ +pub mod data_structures; +pub mod parser; + fn main() { println!("Hello, world!"); } diff --git a/src/parser/deserializer.rs b/src/parser/deserializer.rs new file mode 100644 index 0000000..75d3c6e --- /dev/null +++ b/src/parser/deserializer.rs @@ -0,0 +1,86 @@ +use std::io::Read; + +use crate::data_structures; + +fn deserialize_dmarc_report(reader: R) -> +Result { + serde_xml_rs::from_reader(reader) +} + +#[cfg(test)] +mod test { + use super::*; + + use std::fs::File; + use std::io::BufReader; + + #[test] + fn test_deserialize_dmarc_report() { + let dmarc_report_file = File::open("fixtures/dmarc_report_fixture.xml").unwrap(); + + let reader = BufReader::new(dmarc_report_file); + + let should_be = data_structures::Feedback { + version: String::from("1.0"), + report_metadata: data_structures::ReportMetadata { + org_name: String::from("Enterprise Outlook"), + email: String::from("dmarcreport@microsoft.com"), + report_id: String::from("3374fb5148ba40c1a5cf8e3d36f34a34"), + date_range: data_structures::DateRange { + begin: String::from("1707436800"), + end: String::from("1707523200"), + }, + }, + policy_published: data_structures::PolicyPublished { + domain: String::from("boitalett.re"), + adkim: String::from("s"), + aspf: String::from("s"), + p: String::from("reject"), + sp: String::from("reject"), + pct: String::from("100"), + fo: String::from("0"), + }, + record: data_structures::Record { + row: data_structures::Row { + source_ip: String::from("195.154.102.43"), + count: String::from("1"), + policy_evaluated: data_structures::PolicyEvaluated { + disposition: String::from("none"), + dkim: String::from("pass"), + spf: String::from("pass"), + }, + }, + identifiers: data_structures::Identifiers { + envelope_to: String::from("babilou.com"), + envelope_from: String::from("boitalett.re"), + header_from: String::from("boitalett.re"), + }, + auth_results: data_structures::AuthResults { + dkim: data_structures::DKIM { + domain: String::from("boitalett.re"), + selector: String::from("2023101501"), + result: String::from("pass"), + }, + spf: data_structures::SPF { + domain: String::from("boitalett.re"), + scope: String::from("mfrom"), + result: String::from("pass"), + }, + }, + }, + }; + + match deserialize_dmarc_report(reader) { + Ok(report) => { + assert_eq!( + report.report_metadata.report_id, + should_be.report_metadata.report_id + ); + println!("{:?}", report); + }, + Err(err) => { + panic!("{:?}", err); + } + } + } +} diff --git a/src/parser/mod.rs b/src/parser/mod.rs new file mode 100644 index 0000000..218d426 --- /dev/null +++ b/src/parser/mod.rs @@ -0,0 +1 @@ +pub mod deserializer; -- cgit v1.2.3