svn commit: r1932516 - in spamassassin/trunk: lib/Mail/SpamAssassin/Plugin rules

[email protected]
Newsgroups gmane.mail.spam.spamassassin.cvs
Message-ID <177438384491.3022381.11136348235597158584@svn03-he-fi>
Author: fkento
Date: Tue Mar 24 20:24:04 2026
New Revision: 1932516

Log:
Handle DMARC permerror/temperror without falling back to Mail::DMARC::PurePerl

When an Authentication-Results header contains dmarc=permerror or
dmarc=temperror, these are terminal results that should not trigger a
fallback to Mail::DMARC::PurePerl for a redundant DNS lookup.

Also fixes the Mail::DMARC::PurePerl path where "too many policies"
was incorrectly setting dmarc_policy to "no policy available", which
caused check_dmarc_missing to fire instead of the more accurate
permerror result.

Adds new check_dmarc_permerror and check_dmarc_temperror eval rules
with corresponding DMARC_PERMERROR and DMARC_TEMPERROR rules.

Modified:
   spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/DMARC.pm
   spamassassin/trunk/rules/25_dmarc.cf

Modified: spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/DMARC.pm
==============================================================================
--- spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/DMARC.pm	Tue Mar 24 20:10:31 2026	(r1932515)
+++ spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/DMARC.pm	Tue Mar 24 20:24:04 2026	(r1932516)
@@ -50,6 +50,16 @@ Mail::SpamAssassin::Plugin::DMARC - chec
     describe DMARC_MISSING Missing DMARC policy
     tflags DMARC_MISSING net
     score DMARC_MISSING 0.001
+
+    header DMARC_PERMERROR eval:check_dmarc_permerror()
+    describe DMARC_PERMERROR DMARC permanent error
+    tflags DMARC_PERMERROR net
+    score DMARC_PERMERROR 0.001
+
+    header DMARC_TEMPERROR eval:check_dmarc_temperror()
+    describe DMARC_TEMPERROR DMARC temporary error
+    tflags DMARC_TEMPERROR net
+    score DMARC_TEMPERROR 0.001
   endif
 
 =head1 DESCRIPTION
@@ -97,6 +107,8 @@ sub new {
   $self->register_eval_rule("check_dmarc_quarantine", $Mail::SpamAssassin::Conf::TYPE_HEAD_EVALS);
   $self->register_eval_rule("check_dmarc_none", $Mail::SpamAssassin::Conf::TYPE_HEAD_EVALS);
   $self->register_eval_rule("check_dmarc_missing", $Mail::SpamAssassin::Conf::TYPE_HEAD_EVALS);
+  $self->register_eval_rule("check_dmarc_permerror", $Mail::SpamAssassin::Conf::TYPE_HEAD_EVALS);
+  $self->register_eval_rule("check_dmarc_temperror", $Mail::SpamAssassin::Conf::TYPE_HEAD_EVALS);
 
   return $self;
 }
@@ -217,6 +229,28 @@ sub check_dmarc_missing {
   return $self->_check_eval($pms, $result);
 }
 
+sub check_dmarc_permerror {
+  my ($self, $pms, $name) = @_;
+
+  my $result = sub {
+    defined $pms->{dmarc_result} &&
+      $pms->{dmarc_result} eq 'permerror';
+  };
+
+  return $self->_check_eval($pms, $result);
+}
+
+sub check_dmarc_temperror {
+  my ($self, $pms, $name) = @_;
+
+  my $result = sub {
+    defined $pms->{dmarc_result} &&
+      $pms->{dmarc_result} eq 'temperror';
+  };
+
+  return $self->_check_eval($pms, $result);
+}
+
 sub check_tick {
   my ($self, $opts) = @_;
 
@@ -368,8 +402,9 @@ sub _check_dmarc {
   } elsif (defined $pms->{dmarc_result}) {
     if (defined $result->reason->[0]{comment} &&
           $result->reason->[0]{comment} eq 'too many policies') {
-      dbg("result: no policy available (too many policies)");
-      $pms->{dmarc_policy} = 'no policy available';
+      dbg("result: permerror (too many policies)");
+      $pms->{dmarc_result} = 'permerror';
+      $pms->{dmarc_policy} = 'permerror';
     } elsif ($result->result eq 'pass') {
       dbg("result: pass");
       my $policy = eval { $result->published->p };
@@ -410,6 +445,12 @@ sub _check_dmarc_authres {
   if ($result eq 'none') {
     # dmarc=none means no DMARC record published, no policy to look up
     $policy = 'no policy available';
+  } elsif ($result eq 'permerror' || $result eq 'temperror') {
+    # error results are terminal, no point in falling back to local check
+    $pms->{dmarc_result} = $result;
+    $pms->{dmarc_policy} = $result;
+    dbg("using Authentication-Results: result=%s", $result);
+    return 1;
   } else {
     # Try to get policy from A-R header properties
     foreach my $parsed (@{$pms->{authres_parsed}{dmarc} || []}) {

Modified: spamassassin/trunk/rules/25_dmarc.cf
==============================================================================
--- spamassassin/trunk/rules/25_dmarc.cf	Tue Mar 24 20:10:31 2026	(r1932515)
+++ spamassassin/trunk/rules/25_dmarc.cf	Tue Mar 24 20:24:04 2026	(r1932516)
@@ -58,5 +58,17 @@ priority DMARC_MISSING 500
 tflags DMARC_MISSING net
 reuse DMARC_MISSING
 
+header DMARC_PERMERROR eval:check_dmarc_permerror()
+describe DMARC_PERMERROR DMARC permanent error
+priority DMARC_PERMERROR 500
+tflags DMARC_PERMERROR net
+reuse DMARC_PERMERROR
+
+header DMARC_TEMPERROR eval:check_dmarc_temperror()
+describe DMARC_TEMPERROR DMARC temporary error
+priority DMARC_TEMPERROR 500
+tflags DMARC_TEMPERROR net
+reuse DMARC_TEMPERROR
+
 endif
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.