From 68e465bd1320e52b2f93671e77073d874ef89512 Mon Sep 17 00:00:00 2001 From: Dan Mahoney Date: Mon, 22 Jun 2026 18:03:47 -0400 Subject: [PATCH] feat: record VERP envelope senders in forensic report archive copies VERP only changes the SMTP envelope (MAIL FROM); it leaves no trace in the message itself, so an archived copy (--archive-dir) of a forensic report gave no way to confirm whether, or how, VERP fired for a given send -- not even by comparing against what the recipient's own server might stamp as Return-Path, since that's only ever visible to them. Archived forensic-report copies are now prefixed with a few "#" comment lines recording the envelope sender(s) actually used: the full recipient -> VERP-address mapping when --verp is on, or the plain --report-email address otherwise. This is deliberately not styled as RFC822 headers, since archive_report()'s purpose is to capture the message exactly as handed to SMTP, and inventing header lines that were never really part of it would misrepresent that more than a non-standard comment preamble does. The message actually transmitted over SMTP is unchanged. --- reports/opendmarc-reports.8.in | 9 ++++++++- reports/opendmarc-reports.in | 23 ++++++++++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/reports/opendmarc-reports.8.in b/reports/opendmarc-reports.8.in index fe24ec9f..43b4bb03 100644 --- a/reports/opendmarc-reports.8.in +++ b/reports/opendmarc-reports.8.in @@ -35,7 +35,14 @@ one file per report, named .IR seconds-pid-seq-label.eml . Each file contains the full message exactly as handed to SMTP (headers and MIME body), not just the gzipped XML or AFRF attachment, for later review -of RFC compliance or message structure. +of RFC compliance or message structure, with one exception: in forensic +mode, the archived copy is prefixed with one or more non-standard +.B # +comment lines recording the envelope sender(s) actually used to send it, +since that information (particularly the per-recipient address used when +.I --verp +is enabled) is otherwise never recorded anywhere -- the SMTP envelope +leaves no trace in the message itself. .I label is the destination domain for aggregate reports, or the (first) recipient for forensic reports, with characters outside diff --git a/reports/opendmarc-reports.in b/reports/opendmarc-reports.in index a6f08689..320cb5b9 100755 --- a/reports/opendmarc-reports.in +++ b/reports/opendmarc-reports.in @@ -762,7 +762,28 @@ if ($forensic) my $archive_label = $rcpts[0]; $archive_label .= "+" . (scalar(@rcpts) - 1) if @rcpts > 1; - archive_report($archive_label, $message); + + # Record the envelope sender(s) actually used for this report as a + # non-standard comment preamble on the archived copy. VERP only + # changes the SMTP envelope (MAIL FROM), which leaves no trace in the + # message itself, so without this the archive can't show whether, or + # how, VERP fired. Deliberately not formatted as RFC822 headers: + # archive_report()'s whole point is to capture the message exactly as + # it's handed to SMTP, and inventing header lines that were never + # really part of it would misrepresent that more than a few + # non-standard "#" lines on top do. + my $archive_preamble; + if ($verp) + { + $archive_preamble = "# VERP envelope senders used for this report:\n"; + $archive_preamble .= "# $_ -> " . verp_sender($repemail, $_) . "\n" + for @rcpts; + } + else + { + $archive_preamble = "# Envelope sender used for this report: $repemail\n"; + } + archive_report($archive_label, $archive_preamble . $message); $smtpfail = 0;