dnscheck: query recursive resolvers directly, not the system one

The PTR check reported a correctly published record as wrong. The lookups
went through the container's resolver (127.0.0.11) which forwards to the
host's systemd-resolved, and systemd-resolved synthesises the reverse
lookup of the machine's own addresses from the local hostname rather than
asking public DNS. On the production host that meant

    203.0.113.10 -> provider-assigned-hostname (does not match)

while public DNS has had 203.0.113.10 -> selfpost.example.com all along.

These checks exist to report what a receiving mail server sees, so they
now dial recursive resolvers themselves, defaulting to 1.1.1.1, 8.8.8.8
and 9.9.9.9 and overridable with SELFPOST_DNS_RESOLVERS. The e2e stand
sets it to its CoreDNS, which the `dns:` directive alone no longer covers.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-03 21:53:24 +03:00
parent 5e8a330cf1
commit 4003a299a6
10 changed files with 259 additions and 5 deletions
+5 -4
View File
@@ -103,10 +103,11 @@ type cached[T any] struct {
expires time.Time
}
// New returns a Checker using the process resolver and the package's default
// timeout and cache lifetimes.
func New() *Checker {
return newChecker(net.DefaultResolver, lookupTimeout, serverTTL, domainTTL)
// New returns a Checker querying the given recursive resolvers (empty means
// DefaultResolvers) with the package's default timeout and cache lifetimes.
// The lookups deliberately bypass the system resolver — see externalResolver.
func New(resolvers []string) *Checker {
return newChecker(newExternalResolver(resolvers), lookupTimeout, serverTTL, domainTTL)
}
func newChecker(r resolver, timeout, srvTTL, domTTL time.Duration) *Checker {