Expand description
DMARC aggregate report parser (RFC 7489).
Parse DMARC aggregate feedback reports from their XML representation as defined in RFC 7489 Appendix C.
§Library usage
dmarc-report-parser exposes a small, focused API for parsing DMARC aggregate
feedback reports from their XML representation as defined in
RFC 7489 Appendix C.
§Parsing a report
The primary entry point is parse, which accepts an XML string and returns a
Report:
let xml = r#"<?xml version="1.0" encoding="UTF-8"?>
<feedback>
<report_metadata>
<org_name>Example Corp</org_name>
<email>dmarc@example.com</email>
<report_id>abc123</report_id>
<date_range>
<begin>1609459200</begin>
<end>1609545600</end>
</date_range>
</report_metadata>
<policy_published>
<domain>example.com</domain>
<adkim>r</adkim>
<aspf>r</aspf>
<p>none</p>
<sp>none</sp>
<pct>100</pct>
</policy_published>
<record>
<row>
<source_ip>192.0.2.1</source_ip>
<count>5</count>
<policy_evaluated>
<disposition>none</disposition>
<dkim>pass</dkim>
<spf>pass</spf>
</policy_evaluated>
</row>
<identifiers>
<envelope_from>example.com</envelope_from>
<header_from>example.com</header_from>
</identifiers>
<auth_results>
<spf>
<domain>example.com</domain>
<result>pass</result>
</spf>
</auth_results>
</record>
</feedback>"#;
let report = dmarc_report_parser::parse(xml).unwrap();
assert_eq!(report.report_metadata.org_name, "Example Corp");If you have raw bytes instead of a string, use parse_bytes:
let report = dmarc_report_parser::parse_bytes(xml_bytes).unwrap();§Trait-based parsing
Report implements FromStr, TryFrom<&str>, and
[TryFrom<&[u8]>] so you can use whichever style fits your code:
use std::str::FromStr;
use dmarc_report_parser::Report;
// Using FromStr
let report = Report::from_str(xml).unwrap();
// Using str::parse
let report: Report = xml.parse().unwrap();
// Using TryFrom
let report = Report::try_from(xml).unwrap();§Error handling
Both parse and parse_bytes return Result<Report, Error>. The Error
type has two variants:
Error::Parse— the XML is malformed or does not match the DMARC report schema.Error::Utf8— the input bytes are not valid UTF-8 (only fromparse_bytes/TryFrom<&[u8]>).
§Combining multiple reports
If you have several parsed reports and want to view them together — for
example to compute totals across a quarter’s worth of feedback — wrap them in
an Aggregate. It does not fabricate merged metadata; instead it preserves
each underlying Report and lets you iterate every record paired with its
source report.
use dmarc_report_parser::{parse, Aggregate};
let reports = vec![parse(xml).unwrap(), parse(xml).unwrap()];
let agg = Aggregate::from_reports(reports);
assert_eq!(agg.reports.len(), 2);
assert_eq!(agg.total_messages(), 8);
assert_eq!(agg.date_span(), Some((0, 86_400)));
for (report, record) in agg.records() {
println!(
"{} sent {} message(s) (from report {})",
record.row.source_ip,
record.row.count,
report.report_metadata.report_id,
);
}§Working with the report
Once parsed, you can access every field of the RFC 7489 schema through the strongly-typed structs:
use dmarc_report_parser::{parse, DmarcResult};
let report = parse(xml).unwrap();
// Iterate over records and check results
for record in &report.records {
let ip = &record.row.source_ip;
let count = record.row.count;
let dkim = record.row.policy_evaluated.dkim;
let spf = record.row.policy_evaluated.spf;
if dkim == DmarcResult::Fail || spf == DmarcResult::Fail {
println!("{ip} sent {count} message(s) that failed DMARC");
}
}§CLI
§CLI usage
The dmarc-report binary is an optional CLI tool for rendering DMARC aggregate
reports in the terminal, as HTML, or as Markdown. It is built when the cli
Cargo feature is enabled.
§Installation
cargo install dmarc-report-parser --features cli§Synopsis
dmarc-report [OPTIONS] <FILES>...§Arguments
| Argument | Description |
|---|---|
<FILES>... | One or more DMARC report files (.xml, .xml.gz, .gz, or .zip). When more than one file is given, the output is an aggregate view across all of them. |
§Options
| Option | Default | Description |
|---|---|---|
-f, --format <FORMAT> | terminal | Output format: terminal, html, or markdown |
-o, --output <FILE> | stdout | Write rendered output to a file |
-h, --help | Print help information | |
-V, --version | Print version |
§Output formats
§Terminal (default)
Produces colorized, human-readable output suitable for viewing in a terminal emulator. Passing and failing results are highlighted with colors.
dmarc-report report.xml§HTML
Generates a standalone HTML document with embedded CSS styles. This can be opened directly in a browser or served from a web server.
dmarc-report report.xml --format html --output report.html§Markdown
Renders the report as Markdown tables. Useful for pasting into issues, wikis, or other documentation.
dmarc-report report.xml.gz --format markdown§Supported input formats
The CLI automatically detects the file format from the file extension:
| Extension | Handling |
|---|---|
.xml | Parsed directly as XML |
.gz, .xml.gz | Decompressed with gzip, then parsed |
.zip | The first .xml entry is extracted and parsed |
§Aggregating multiple reports
Pass two or more files to render them as a single aggregate view. The output contains:
- An overview with the total reports, records, messages, and the combined date span.
- A reports section listing each contributing report (organisation, report ID, domain, period, record and message counts).
- A combined records table with an extra
Reportcolumn that identifies which report each row came from.
Each report retains its own metadata; nothing is fabricated by combining
fields like policy_published across reports.
# Aggregate every gzip-compressed report in the current directory
dmarc-report *.xml.gz
# Render an aggregate as a single HTML document
dmarc-report q1/*.xml q2/*.xml --format html --output combined.html§Examples
# View a plain XML report in the terminal
dmarc-report report.xml
# View a gzip-compressed report
dmarc-report report.xml.gz
# Extract and view a report from a zip archive
dmarc-report report.zip
# Save an HTML report to disk
dmarc-report report.xml --format html --output report.html
# Pipe Markdown output into another tool
dmarc-report report.xml --format markdown | less
# Aggregate multiple reports into one Markdown table
dmarc-report jan/*.xml.gz --format markdownStructs§
- Aggregate
- A combined view across multiple DMARC aggregate reports.
- Auth
Results - Authentication results for a message (
AuthResultType). - Date
Range - UTC time range covered by a report, expressed as Unix timestamps.
- Dkim
Auth Result - Result of evaluating a single DKIM signature (
DKIMAuthResultType). - Identifiers
- Message identifiers (
IdentifierType). - Policy
Evaluated - Results of applying DMARC to the messages in this row (
PolicyEvaluatedType). - Policy
Override Reason - A reason why the applied policy may differ from the published policy
(
PolicyOverrideReasonType). - Policy
Published - The DMARC policy published for the organizational domain (
PolicyPublishedType). - Record
- A single message record within a feedback report (
RecordType). - Report
- Top-level DMARC aggregate feedback report (
<feedback>). - Report
Metadata - Report generator metadata (
ReportMetadataType). - Row
- Per-message data row (
RowType). - SpfAuth
Result - Result of an SPF check (
SPFAuthResultType).
Enums§
- Alignment
Mode - DKIM / SPF alignment mode (
AlignmentType). - Disposition
- Policy action applied to a message (
DispositionType). - Dkim
Result - DKIM verification result (
DKIMResultType), per RFC 5451. - Dmarc
Result - The DMARC-aligned authentication result (
DMARCResultType). - Error
- Errors that can occur while parsing a DMARC aggregate report.
- Policy
Override - Reason type for a policy override (
PolicyOverrideType). - SpfDomain
Scope - SPF identity scope (
SPFDomainScope). - SpfResult
- SPF evaluation result (
SPFResultType), per RFC 7208.
Functions§
- parse
- Parse a DMARC aggregate report from an XML string.
- parse_
bytes - Parse a DMARC aggregate report from a byte slice.