[packages/opendmarc] Rel 4; init scripts, subdomain dmarc fixes, PSL usage
arekm
arekm at pld-linux.org
Wed May 13 15:53:18 CEST 2026
commit 3378ced5c2e467af89be417e783fc592a0d3cba8
Author: Arkadiusz Miśkiewicz <arekm at maven.pl>
Date: Wed May 13 15:52:46 2026 +0200
Rel 4; init scripts, subdomain dmarc fixes, PSL usage
issue-54-activate.patch | 18 ++
issue-54.patch | 449 ++++++++++++++++++++++++++++++++++++++++++++++++
opendmarc.init | 99 +++++++++++
opendmarc.service | 16 ++
opendmarc.spec | 43 +++--
opendmarc.sysconfig | 5 +
pld-defaults.patch | 31 ++++
7 files changed, 648 insertions(+), 13 deletions(-)
---
diff --git a/opendmarc.spec b/opendmarc.spec
index 8bd95f2..b723c0f 100644
--- a/opendmarc.spec
+++ b/opendmarc.spec
@@ -1,17 +1,22 @@
-# TODO
-# - pldize initscript
+#
+# Conditional build:
+%bcond_without tests # build-time test suite
+
%define ver 1-4-2
%define ver_dot %(echo %{ver} | tr '-' '.')
Summary: DMARC milter and library
Summary(pl.UTF-8): Milter i biblioteka DMARC
Name: opendmarc
Version: %{ver_dot}
-Release: 3
+Release: 4
License: BSD
Group: Daemons
Source0: https://github.com/trusteddomainproject/OpenDMARC/archive/refs/tags/rel-%{name}-%{ver}.tar.gz
# Source0-md5: 658d951db84a0305b0c5d9312eff5b64
Source1: %{name}.tmpfiles
+Source2: %{name}.init
+Source3: %{name}.sysconfig
+Source4: %{name}.service
Patch0: ticket168.patch
Patch1: ticket193.patch
Patch2: ticket204.patch
@@ -29,6 +34,9 @@ Patch13: check-correct-domain.patch
Patch14: arcares-segfaults.patch
Patch15: parse-arc-leaks.patch
Patch16: cve-2024-25768.patch
+Patch17: pld-defaults.patch
+Patch18: issue-54.patch
+Patch19: issue-54-activate.patch
URL: http://www.trusteddomain.org/opendmarc.html
BuildRequires: autoconf >= 2.61
BuildRequires: automake
@@ -45,6 +53,7 @@ Requires(pre): /usr/sbin/useradd
Requires(postun): /usr/sbin/groupdel
Requires(postun): /usr/sbin/userdel
Requires: libopendmarc = %{version}-%{release}
+Requires: publicsuffix-list
Provides: group(opendmarc)
Provides: user(opendmarc)
BuildRoot: %{tmpdir}/%{name}-%{version}-root-%(id -u -n)
@@ -112,6 +121,9 @@ do tworzenia aplikacji wykorzystujących bibliotekę libopendmarc.
%patch -P14 -p1
%patch -P15 -p1
%patch -P16 -p1
+%patch -P17 -p1
+%patch -P18 -p1
+%patch -P19 -p1
%build
%{__libtoolize}
@@ -127,23 +139,23 @@ do tworzenia aplikacji wykorzystujących bibliotekę libopendmarc.
--with-spf2-lib=%{_libdir}
%{__make}
+%if %{with tests}
+%{__make} check
+%endif
+
%install
rm -rf $RPM_BUILD_ROOT
-install -d $RPM_BUILD_ROOT{%{_sysconfdir},/etc/rc.d/init.d,%{systemdtmpfilesdir}} \
+install -d $RPM_BUILD_ROOT{%{_sysconfdir}/sysconfig,/etc/rc.d/init.d,%{systemdtmpfilesdir},%{systemdunitdir}} \
$RPM_BUILD_ROOT%{_localstatedir}/{run,spool}/%{name}
%{__make} install \
DESTDIR=$RPM_BUILD_ROOT
-install -p contrib/init/redhat/%{name} $RPM_BUILD_ROOT/etc/rc.d/init.d/%{name}
+install -p %{SOURCE2} $RPM_BUILD_ROOT/etc/rc.d/init.d/%{name}
+cp -p %{SOURCE3} $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/%{name}
+cp -p %{SOURCE4} $RPM_BUILD_ROOT%{systemdunitdir}/%{name}.service
cp -p opendmarc/%{name}.conf.sample $RPM_BUILD_ROOT%{_sysconfdir}/%{name}.conf
-# Set some basic settings in the default config file
-perl -pi -e 's|^# (HistoryFile /var/run)/(opendmarc.dat)|$1/opendmarc/$2/;
- s|^# (Socket )|$1|;
- s|^# (UserId )|$1|;
- ' $RPM_BUILD_ROOT%{_sysconfdir}/%{name}.conf
-
cp -p %{SOURCE1} $RPM_BUILD_ROOT%{systemdtmpfilesdir}/%{name}.conf
install -d $RPM_BUILD_ROOT%{_includedir}/%{name}
@@ -163,18 +175,21 @@ rm -rf $RPM_BUILD_ROOT
%post
/sbin/chkconfig --add %{name}
%service %{name} restart
+%systemd_post %{name}.service
%preun
-if [ $1 -eq 0 ]; then
- /sbin/chkconfig --del %{name}
+if [ "$1" = "0" ]; then
%service %{name} stop
+ /sbin/chkconfig --del %{name}
fi
+%systemd_preun %{name}.service
%postun
if [ "$1" = "0" ]; then
%userremove opendmarc
%groupremove opendmarc
fi
+%systemd_reload
%post -n libopendmarc -p /sbin/ldconfig
%postun -n libopendmarc -p /sbin/ldconfig
@@ -184,6 +199,7 @@ fi
%doc CONTRIBUTING INSTALL README README.md RELEASE_NOTES
%doc db/README.schema db/schema.mysql
%config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/%{name}.conf
+%config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/sysconfig/%{name}
%attr(754,root,root) /etc/rc.d/init.d/%{name}
%attr(755,root,root) %{_sbindir}/opendmarc
%attr(755,root,root) %{_sbindir}/opendmarc-check
@@ -194,6 +210,7 @@ fi
%attr(755,root,root) %{_sbindir}/opendmarc-reports
%{_mandir}/man5/opendmarc.conf.5*
%{_mandir}/man8/opendmarc*.8*
+%{systemdunitdir}/%{name}.service
%{systemdtmpfilesdir}/%{name}.conf
%dir %attr(700,opendmarc,opendmarc) %{_localstatedir}/spool/%{name}
%dir %attr(700,opendmarc,opendmarc) %{_localstatedir}/run/%{name}
diff --git a/issue-54-activate.patch b/issue-54-activate.patch
new file mode 100644
index 0000000..bcf6649
--- /dev/null
+++ b/issue-54-activate.patch
@@ -0,0 +1,18 @@
+PLD-only: activate the Public Suffix List in the shipped
+opendmarc.conf.sample. With PSL configured, opendmarc computes the
+organizational domain via RFC 7489 §6.6.3 instead of falling back to
+the label-walk heuristic added in issue-54.patch. The path points at
+the file shipped by the publicsuffix-list package, which is pulled in
+via a Requires: on the opendmarc subpackage.
+
+--- a/opendmarc/opendmarc.conf.sample
++++ b/opendmarc/opendmarc.conf.sample
+@@ -293,7 +293,7 @@
+ ## domain will be evaluated. This file should be periodically updated.
+ ## One location to retrieve the file from is https://publicsuffix.org/list/
+ #
+-# PublicSuffixList path
++PublicSuffixList /usr/share/publicsuffix/public_suffix_list.dat
+
+ ## RecordAllMessages { true | false }
+ ## default "false"
diff --git a/issue-54.patch b/issue-54.patch
new file mode 100644
index 0000000..7d27c5c
--- /dev/null
+++ b/issue-54.patch
@@ -0,0 +1,449 @@
+PLD note: upstream commit 7d7457b also touches opendmarc/opendmarc.c to log
+a syslog warning when the label-walk fallback was used. That hunk dereferences
+cc->cctx_dmarc->org_domain_from_fallback directly, but DMARC_POLICY_T is opaque
+in the public dmarc.h header (opendmarc.c does not include opendmarc_internal.h
+and should not). The milter hunk has been dropped here; the RFC fix itself
+lives in libopendmarc/opendmarc_policy.c. The warning can be reinstated once
+upstream exposes an accessor.
+
+From 7d7457bfc703700c90c4551e334b7a734566c1f6 Mon Sep 17 00:00:00 2001
+From: Dan Mahoney <github at gushiorg>
+Date: Mon, 11 May 2026 07:30:17 -0700
+Subject: [PATCH 12/12] Fix subdomain DMARC fallback to parent domain (issue
+ #54)
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Per RFC 7489 §6.6.3, when a subdomain has no _dmarc record, the
+organizational domain's DMARC record should be consulted.
+
+The existing code already handled this when a public suffix list (PSL)
+is configured: opendmarc_get_tld() returns the organizational domain
+(e.g. "example.com" for "sub.example.com") and the code queries
+_dmarc.<org-domain>.
+
+The bug: when no PSL is loaded, opendmarc_get_tld() returns the input
+domain unchanged. The fallback then re-queried _dmarc.<same-domain>,
+which also had no record, and returned DMARC_DNS_ERROR_NO_RECORD
+unconditionally. The milter then reported dmarc=none regardless of
+whether a parent domain had a DMARC record.
+
+Fix: restructure the fallback in opendmarc_policy_query_dmarc():
+
+ - If the PSL identifies a different organizational domain, query only
+ that domain and stop (preserving the existing RFC-compliant path).
+
+ - If the PSL returns the same domain as the query (no PSL loaded, or
+ no PSL match), walk up the label tree one level at a time, stopping
+ before bare TLDs. This is not strictly RFC 7489-compliant (which
+ requires a PSL to locate the organizational domain boundary), but it
+ correctly handles the common case where no PSL is configured and a
+ parent domain has a DMARC record.
+
+When the label-walk fallback is used, a LOG_WARNING is emitted via
+syslog to advise the operator that the result may not be RFC-compliant
+and that PublicSuffixList should be set in opendmarc.conf.
+
+Add regression tests (test_subdomain_fallback) using the fake-DNS
+mechanism to verify the fix without live DNS:
+ - Subdomain with no record falls back to parent's p=reject (no PSL)
+ - Domain with its own record is not affected
+ - Deeply nested subdomain walks up to grandparent (no PSL)
+ - No record at any level returns DMARC_DNS_ERROR_NO_RECORD correctly
+ - PSL-based fallback continues to work when TLD file is loaded
+---
+ libopendmarc/opendmarc_internal.h | 1 +
+ libopendmarc/opendmarc_policy.c | 58 +++-
+ libopendmarc/tests/Makefile.am | 5 +-
+ libopendmarc/tests/test_subdomain_fallback.c | 262 +++++++++++++++++++
+ opendmarc/opendmarc.c | 16 ++
+ 5 files changed, 338 insertions(+), 4 deletions(-)
+ create mode 100644 libopendmarc/tests/test_subdomain_fallback.c
+
+diff --git a/libopendmarc/opendmarc_internal.h b/libopendmarc/opendmarc_internal.h
+index c48d6d3..8d37480 100644
+--- a/libopendmarc/opendmarc_internal.h
++++ b/libopendmarc/opendmarc_internal.h
+@@ -159,6 +159,7 @@ typedef struct dmarc_policy_t {
+ */
+ u_char * from_domain; /* Input: From: header domain */
+ u_char * organizational_domain;
++ int org_domain_from_fallback; /* Non-zero if PSL was absent and label-walk was used */
+
+ /*
+ * Found in the _dmarc record or supplied to us.
+diff --git a/libopendmarc/opendmarc_policy.c b/libopendmarc/opendmarc_policy.c
+index 32053db..e81dd1c 100644
+--- a/libopendmarc/opendmarc_policy.c
++++ b/libopendmarc/opendmarc_policy.c
+@@ -798,7 +798,13 @@ opendmarc_policy_query_dmarc(DMARC_POLICY_T *pctx, u_char *domain)
+ tld_reply = opendmarc_get_tld(domain, tld, sizeof tld);
+ if (tld_reply != 0)
+ goto dns_failed;
+- if (strlen(tld) > 0)
++
++ /*
++ * If the PSL identified an organizational domain distinct from the
++ * queried domain, try exactly that domain and stop. Per RFC 7489
++ * §6.6.3 we look at one domain: the organizational domain.
++ */
++ if (strlen(tld) > 0 && strcasecmp((char *)tld, (char *)domain) != 0)
+ {
+ pctx->organizational_domain = strdup(tld);
+
+@@ -811,8 +817,7 @@ opendmarc_policy_query_dmarc(DMARC_POLICY_T *pctx, u_char *domain)
+ if (bp != NULL)
+ goto got_record;
+ /*
+- * Was a CNAME was found that the resolver did
+- * not follow on its own?
++ * Was a CNAME found that the resolver did not follow on its own?
+ */
+ if (bp == NULL && *buf != '\0')
+ {
+@@ -820,6 +825,53 @@ opendmarc_policy_query_dmarc(DMARC_POLICY_T *pctx, u_char *domain)
+ if (--loop_count != 0)
+ goto query_again2;
+ }
++ /* Organizational domain has no DMARC record; do not try further. */
++ goto dns_failed;
++ }
++
++ /*
++ * No PSL was loaded, or the PSL could not identify an organizational
++ * domain boundary (it returned the input domain unchanged). Walk up
++ * the label tree as a best-effort fallback, stopping before bare TLDs.
++ * This is not strictly per RFC 7489 (which requires a PSL to determine
++ * the organizational domain), but it handles the common case where no
++ * PSL is configured and a parent domain has a DMARC record.
++ * Configure PublicSuffixList in opendmarc.conf for RFC-compliant behavior.
++ */
++ {
++ u_char *cur = (u_char *)domain;
++ u_char *dot;
++
++ while ((dot = (u_char *)strchr((char *)cur, '.')) != NULL)
++ {
++ cur = dot + 1;
++
++ /* Stop before bare TLDs (labels with no further dot). */
++ if (strchr((char *)cur, '.') == NULL)
++ break;
++
++ loop_count = DNS_MAX_RETRIES;
++ (void) strlcpy(copy, "_dmarc.", sizeof copy);
++ (void) strlcat(copy, cur, sizeof copy);
++query_again3:
++ (void) memset(buf, '\0', sizeof buf);
++ bp = dmarc_dns_get_record(copy, &dns_reply, buf, sizeof buf);
++ if (bp != NULL)
++ {
++ pctx->organizational_domain = strdup(cur);
++ pctx->org_domain_from_fallback = 1;
++ goto got_record;
++ }
++ /*
++ * Was a CNAME found that the resolver did not follow on its own?
++ */
++ if (bp == NULL && *buf != '\0')
++ {
++ (void) strlcpy(copy, buf, sizeof copy);
++ if (--loop_count != 0)
++ goto query_again3;
++ }
++ }
+ }
+ dns_failed:
+ switch (dns_reply)
+diff --git a/libopendmarc/tests/Makefile.am b/libopendmarc/tests/Makefile.am
+index b8b01bc..56b3e96 100644
+--- a/libopendmarc/tests/Makefile.am
++++ b/libopendmarc/tests/Makefile.am
+@@ -8,7 +8,8 @@ check_PROGRAMS = test_tld \
+ test_dmarc_fetch \
+ test_xml_parse \
+ test_parse_to_buf \
+- test_alignment
++ test_alignment \
++ test_subdomain_fallback
+ if LIVE_TESTS
+ #check_PROGRAMS += test_dns_lookup
+ #test_dns_lookup_SOURCES = test_dns_lookup.c
+@@ -33,6 +34,8 @@ test_xml_parse_SOURCES = test_xml_parse.c
+
+ test_alignment_SOURCES = test_alignment.c
+
++test_subdomain_fallback_SOURCES = test_subdomain_fallback.c
++
+ TESTS = $(check_PROGRAMS)
+
+ EXTRA_DIST = testfiles/effective_tld_names.dat \
+diff --git a/libopendmarc/tests/test_subdomain_fallback.c b/libopendmarc/tests/test_subdomain_fallback.c
+new file mode 100644
+index 0000000..3457524
+--- /dev/null
++++ b/libopendmarc/tests/test_subdomain_fallback.c
+@@ -0,0 +1,262 @@
++/*
++** test_subdomain_fallback.c -- regression test for issue #54
++**
++** When a subdomain has no _dmarc record, opendmarc_policy_query_dmarc()
++** must fall back to the organizational/parent domain's DMARC record
++** per RFC 7489 §6.6.3.
++**
++** Before the fix, when no PublicSuffixList was configured, opendmarc_get_tld()
++** returned the queried domain unchanged. The fallback then re-queried the
++** same _dmarc name, which also had no record, and the function returned
++** DMARC_DNS_ERROR_NO_RECORD regardless of whether a parent had a record.
++**
++** The fix adds a label-walking fallback for the no-PSL case, and keeps the
++** existing PSL-based single-lookup path for the case where a PSL is loaded.
++**
++** Distinct domain names are used for each test to avoid cross-contamination
++** of the global fake-DNS table (entries persist for the process lifetime).
++*/
++
++#include "../opendmarc_internal.h"
++#include "../dmarc.h"
++
++#define TESTFILE "testfiles/effective_tld_names.dat"
++
++#define CHECK(cond, msg) \
++ do { \
++ count++; \
++ if (cond) { \
++ pass++; \
++ } else { \
++ printf("\t%s(%d): %s: FAIL\n", __FILE__, __LINE__, msg); \
++ fails++; \
++ } \
++ } while (0)
++
++int
++main(int argc, char **argv)
++{
++ int pass = 0, fails = 0, count = 0;
++ DMARC_POLICY_T *pctx;
++ OPENDMARC_STATUS_T status;
++ int p;
++ u_char utilized[256];
++ char *srcdir;
++
++ srcdir = getenv("srcdir");
++ if (srcdir != NULL)
++ {
++ if (chdir(srcdir) != 0)
++ {
++ perror(srcdir);
++ return 1;
++ }
++ }
++
++ /*
++ * Set up the fake DNS table. Once any entry is added, dmarc_dns_get_record()
++ * consults only this table for all lookups; names absent from the table
++ * return NO_DATA (simulating "no record found").
++ *
++ * Use a distinct domain per test to avoid cross-contamination.
++ */
++
++ /* Test domains and their fake records. */
++ opendmarc_dns_fake_record("_dmarc.parent1.example",
++ "v=DMARC1; p=reject; rua=mailto:dmarc at parent1.example");
++ /* _dmarc.sub.parent1.example: absent from table → NO_DATA */
++
++ opendmarc_dns_fake_record("_dmarc.own-record.example",
++ "v=DMARC1; p=quarantine");
++
++ opendmarc_dns_fake_record("_dmarc.parent3.example",
++ "v=DMARC1; p=none; sp=reject");
++ /* _dmarc.a.b.parent3.example and _dmarc.b.parent3.example: absent → NO_DATA */
++
++ /* _dmarc.sub.nodmarc.example and _dmarc.nodmarc.example: absent → NO_DATA */
++
++ /* PSL test: bcx.com is recognized as registrable by the test TLD file. */
++ opendmarc_dns_fake_record("_dmarc.bcx.com",
++ "v=DMARC1; p=reject");
++ /* _dmarc.sub.bcx.com: absent → NO_DATA */
++
++ /*
++ * === Test 1 ===
++ * No PSL. Subdomain has no record; parent has p=reject.
++ *
++ * Before the fix: returned DMARC_DNS_ERROR_NO_RECORD.
++ * After the fix: label-walking finds _dmarc.parent1.example → DMARC_PARSE_OKAY.
++ */
++ pctx = opendmarc_policy_connect_init((u_char *)"1.2.3.4", 0);
++ if (pctx == NULL) { fprintf(stderr, "connect_init failed\n"); return 1; }
++
++ (void) opendmarc_policy_store_from_domain(pctx, (u_char *)"sub.parent1.example");
++ status = opendmarc_policy_query_dmarc(pctx, (u_char *)"sub.parent1.example");
++
++ CHECK(status == DMARC_PARSE_OKAY,
++ "no-PSL subdomain fallback: query should return DMARC_PARSE_OKAY");
++ if (status == DMARC_PARSE_OKAY)
++ {
++ opendmarc_policy_fetch_p(pctx, &p);
++ CHECK(p == DMARC_RECORD_P_REJECT,
++ "no-PSL subdomain fallback: inherited p= should be reject");
++
++ (void) memset(utilized, '\0', sizeof utilized);
++ opendmarc_policy_fetch_utilized_domain(pctx, utilized, sizeof utilized);
++ CHECK(strcasecmp((char *)utilized, "parent1.example") == 0,
++ "no-PSL subdomain fallback: utilized domain should be parent1.example");
++ }
++
++ pctx = opendmarc_policy_connect_shutdown(pctx);
++
++ /*
++ * === Test 2 ===
++ * Domain with its own DMARC record — no fallback should occur.
++ */
++ pctx = opendmarc_policy_connect_init((u_char *)"1.2.3.4", 0);
++ if (pctx == NULL) { fprintf(stderr, "connect_init failed\n"); return 1; }
++
++ (void) opendmarc_policy_store_from_domain(pctx, (u_char *)"own-record.example");
++ status = opendmarc_policy_query_dmarc(pctx, (u_char *)"own-record.example");
++
++ CHECK(status == DMARC_PARSE_OKAY,
++ "domain with own record: query should return DMARC_PARSE_OKAY");
++ if (status == DMARC_PARSE_OKAY)
++ {
++ opendmarc_policy_fetch_p(pctx, &p);
++ CHECK(p == DMARC_RECORD_P_QUARANTINE,
++ "domain with own record: p= should be quarantine");
++ }
++
++ pctx = opendmarc_policy_connect_shutdown(pctx);
++
++ /*
++ * === Test 3 ===
++ * No PSL. Deeply nested subdomain: a.b.parent3.example.
++ * Neither _dmarc.b.parent3.example nor _dmarc.a.b.parent3.example exist.
++ * Label walk should reach _dmarc.parent3.example (p=none, sp=reject).
++ */
++ pctx = opendmarc_policy_connect_init((u_char *)"1.2.3.4", 0);
++ if (pctx == NULL) { fprintf(stderr, "connect_init failed\n"); return 1; }
++
++ (void) opendmarc_policy_store_from_domain(pctx, (u_char *)"a.b.parent3.example");
++ status = opendmarc_policy_query_dmarc(pctx, (u_char *)"a.b.parent3.example");
++
++ CHECK(status == DMARC_PARSE_OKAY,
++ "no-PSL deeply nested subdomain: query should return DMARC_PARSE_OKAY");
++ if (status == DMARC_PARSE_OKAY)
++ {
++ opendmarc_policy_fetch_p(pctx, &p);
++ CHECK(p == DMARC_RECORD_P_NONE,
++ "no-PSL deeply nested subdomain: p= from parent should be none");
++
++ (void) memset(utilized, '\0', sizeof utilized);
++ opendmarc_policy_fetch_utilized_domain(pctx, utilized, sizeof utilized);
++ CHECK(strcasecmp((char *)utilized, "parent3.example") == 0,
++ "no-PSL deeply nested subdomain: utilized domain should be parent3.example");
++ }
++
++ pctx = opendmarc_policy_connect_shutdown(pctx);
++
++ /*
++ * === Test 4 ===
++ * No PSL. No DMARC record at subdomain or any parent.
++ * Should return DMARC_DNS_ERROR_NO_RECORD (graceful failure).
++ */
++ pctx = opendmarc_policy_connect_init((u_char *)"1.2.3.4", 0);
++ if (pctx == NULL) { fprintf(stderr, "connect_init failed\n"); return 1; }
++
++ (void) opendmarc_policy_store_from_domain(pctx, (u_char *)"sub.nodmarc.example");
++ status = opendmarc_policy_query_dmarc(pctx, (u_char *)"sub.nodmarc.example");
++
++ CHECK(status == DMARC_DNS_ERROR_NO_RECORD,
++ "no-PSL no record anywhere: should return DMARC_DNS_ERROR_NO_RECORD");
++
++ pctx = opendmarc_policy_connect_shutdown(pctx);
++
++ printf("Subdomain fallback (no PSL): pass=%d, fail=%d\n", pass, fails);
++
++ /*
++ * === Tests 5-6: repeat key cases with PSL loaded ===
++ *
++ * With a PSL, opendmarc_get_tld() returns the correct organizational
++ * domain (e.g. bcx.com for sub.bcx.com), so the existing PSL-based
++ * code path is used rather than the new label-walking fallback.
++ */
++ if (opendmarc_tld_read_file(TESTFILE, "//", "*.", "!") != 0)
++ {
++ printf("PSL tests: %s: could not read TLD file, skipping.\n", TESTFILE);
++ }
++ else
++ {
++ int psl_pass = 0, psl_fails = 0, psl_count = 0;
++
++#undef CHECK
++#define CHECK(cond, msg) \
++ do { \
++ psl_count++; \
++ if (cond) { \
++ psl_pass++; \
++ } else { \
++ printf("\t%s(%d): %s: FAIL\n", __FILE__, __LINE__, msg); \
++ psl_fails++; \
++ } \
++ } while (0)
++
++ /*
++ * Test 5: PSL loaded; sub.bcx.com has no record, bcx.com has p=reject.
++ * opendmarc_get_tld("sub.bcx.com") → "bcx.com" (different), so the
++ * PSL path queries _dmarc.bcx.com and finds the record.
++ */
++ pctx = opendmarc_policy_connect_init((u_char *)"1.2.3.4", 0);
++ if (pctx == NULL) { fprintf(stderr, "connect_init failed\n"); return 1; }
++
++ (void) opendmarc_policy_store_from_domain(pctx, (u_char *)"sub.bcx.com");
++ status = opendmarc_policy_query_dmarc(pctx, (u_char *)"sub.bcx.com");
++
++ CHECK(status == DMARC_PARSE_OKAY,
++ "PSL subdomain fallback: query should return DMARC_PARSE_OKAY");
++ if (status == DMARC_PARSE_OKAY)
++ {
++ opendmarc_policy_fetch_p(pctx, &p);
++ CHECK(p == DMARC_RECORD_P_REJECT,
++ "PSL subdomain fallback: inherited p= should be reject");
++
++ (void) memset(utilized, '\0', sizeof utilized);
++ opendmarc_policy_fetch_utilized_domain(pctx, utilized, sizeof utilized);
++ CHECK(strcasecmp((char *)utilized, "bcx.com") == 0,
++ "PSL subdomain fallback: utilized domain should be bcx.com");
++ }
++
++ pctx = opendmarc_policy_connect_shutdown(pctx);
++
++ /*
++ * Test 6: PSL loaded; domain with its own record — no fallback.
++ */
++ pctx = opendmarc_policy_connect_init((u_char *)"1.2.3.4", 0);
++ if (pctx == NULL) { fprintf(stderr, "connect_init failed\n"); return 1; }
++
++ (void) opendmarc_policy_store_from_domain(pctx, (u_char *)"own-record.example");
++ status = opendmarc_policy_query_dmarc(pctx, (u_char *)"own-record.example");
++
++ CHECK(status == DMARC_PARSE_OKAY,
++ "PSL domain with own record: query should return DMARC_PARSE_OKAY");
++ if (status == DMARC_PARSE_OKAY)
++ {
++ opendmarc_policy_fetch_p(pctx, &p);
++ CHECK(p == DMARC_RECORD_P_QUARANTINE,
++ "PSL domain with own record: p= should be quarantine");
++ }
++
++ pctx = opendmarc_policy_connect_shutdown(pctx);
++
++ printf("Subdomain fallback (with PSL): pass=%d, fail=%d\n",
++ psl_pass, psl_fails);
++
++ pass += psl_pass;
++ fails += psl_fails;
++ }
++
++ printf("Subdomain fallback overall: pass=%d, fail=%d\n", pass, fails);
++ return fails;
++}
diff --git a/opendmarc.init b/opendmarc.init
new file mode 100644
index 0000000..4f7b8cb
--- /dev/null
+++ b/opendmarc.init
@@ -0,0 +1,99 @@
+#!/bin/sh
+# opendmarc DMARC (Domain-based Message Authentication, Reporting & Conformance) milter
+# chkconfig: 345 86 14
+# description: OpenDMARC implements the DMARC milter spec for domain auth
+# and a milter-based filter application that can plug in to
+# any milter-aware MTA.
+# processname: opendmarc
+# pidfile: /var/run/opendmarc/opendmarc.pid
+# config: /etc/opendmarc.conf
+
+# Source function library
+. /etc/rc.d/init.d/functions
+
+prog="/usr/sbin/opendmarc"
+svname="opendmarc"
+
+sysconfig="/etc/sysconfig/$svname"
+lockfile="/var/lock/subsys/$svname"
+pidfile="/var/run/$svname/$svname.pid"
+conffile="/etc/$svname.conf"
+
+# Get service config
+[ -f $sysconfig ] && . $sysconfig
+
+start() {
+ # Check if the service is already running?
+ if [ ! -f $lockfile ]; then
+ msg_starting "$svname"
+ daemon $prog -c $conffile -P $pidfile $OPTIONS
+ RETVAL=$?
+ [ $RETVAL -eq 0 ] && touch $lockfile
+ else
+ msg_already_running "$svname"
+ fi
+}
+
+stop() {
+ # Stop daemons.
+ if [ -f $lockfile ]; then
+ msg_stopping "$svname"
+ killproc -p $pidfile $prog
+ RETVAL=$?
+ rm -f $lockfile $pidfile >/dev/null 2>&1
+ else
+ msg_not_running "$svname"
+ fi
+}
+
+reload() {
+ if [ -f $lockfile ]; then
+ msg_reloading "$svname"
+ killproc -p $pidfile $prog -USR1
+ RETVAL=$?
+ else
+ msg_not_running "$svname"
+ RETVAL=7
+ fi
+}
+
+condrestart() {
+ if [ ! -f $lockfile ]; then
+ msg_not_running "$svname"
+ RETVAL=$1
+ return
+ fi
+ stop
+ start
+}
+
+RETVAL=0
+# See how we were called.
+case "$1" in
+ start)
+ start
+ ;;
+ stop)
+ stop
+ ;;
+ restart)
+ stop
+ start
+ ;;
+ try-restart)
+ condrestart 0
+ ;;
+ force-reload|reload)
+ reload
+ ;;
+ status)
+ status --pidfile $pidfile $svname
+ RETVAL=$?
+ ;;
+ *)
+ msg_usage "$0 {start|stop|restart|try-restart|reload|force-reload|status}"
+ exit 3
+ ;;
+esac
+
+exit $RETVAL
diff --git a/opendmarc.service b/opendmarc.service
new file mode 100644
index 0000000..b6854ad
--- /dev/null
+++ b/opendmarc.service
@@ -0,0 +1,16 @@
+[Unit]
+Description=Domain-based Message Authentication, Reporting & Conformance (DMARC) Milter
+Documentation=man:opendmarc(8) man:opendmarc.conf(5) man:opendmarc-import(8) man:opendmarc-reports(8)
+After=network.target nss-lookup.target
+
+[Service]
+Type=forking
+PIDFile=/var/run/opendmarc/opendmarc.pid
+EnvironmentFile=-/etc/sysconfig/opendmarc
+ExecStart=/usr/sbin/opendmarc -c /etc/opendmarc.conf -P /var/run/opendmarc/opendmarc.pid $OPTIONS
+ExecReload=/bin/kill -USR1 $MAINPID
+User=opendmarc
+Group=opendmarc
+
+[Install]
+WantedBy=multi-user.target
diff --git a/opendmarc.sysconfig b/opendmarc.sysconfig
new file mode 100644
index 0000000..81a9a24
--- /dev/null
+++ b/opendmarc.sysconfig
@@ -0,0 +1,5 @@
+# Configuration for the opendmarc init script.
+# Sourced by /etc/rc.d/init.d/opendmarc.
+
+# Extra arguments to pass to the daemon (in addition to -c and -P).
+#OPTIONS=
diff --git a/pld-defaults.patch b/pld-defaults.patch
new file mode 100644
index 0000000..14058c0
--- /dev/null
+++ b/pld-defaults.patch
@@ -0,0 +1,31 @@
+PLD-only: enable PLD-appropriate defaults in the shipped
+opendmarc.conf.sample. HistoryFile lands under the per-package state
+directory; Socket and UserID are uncommented so the milter is usable
+out of the box.
+
+--- a/opendmarc/opendmarc.conf.sample
++++ b/opendmarc/opendmarc.conf.sample
+@@ -211,7 +211,7 @@
+ ## rather periodically imported into a relational database from which the
+ ## aggregate reports can be extracted by a tool such as opendmarc-import(8).
+ #
+-# HistoryFile /var/run/opendmarc.dat
++HistoryFile /var/run/opendmarc/opendmarc.dat
+
+ ## HoldQuarantinedMessages { true | false }
+ ## default "false"
+@@ -357,7 +357,7 @@
+ ## either in the configuration file or on the command line. If an IP
+ ## address is used, it must be enclosed in square brackets.
+ #
+-# Socket inet:8893 at localhost
++Socket inet:8893 at localhost
+
+ ## SoftwareHeader { true | false }
+ ## default "false"
+@@ -436,4 +436,4 @@
+ ## The process will be assigned all of the groups and primary group ID of
+ ## the named userid unless an alternate group is specified.
+ #
+-# UserID opendmarc
++UserID opendmarc
================================================================
---- gitweb:
http://git.pld-linux.org/gitweb.cgi/packages/opendmarc.git/commitdiff/3378ced5c2e467af89be417e783fc592a0d3cba8
More information about the pld-cvs-commit
mailing list