audit 4: DNS outages unknown, DMARC limits said, typed Health, one TLS mode per upstream
test / test (push) Canceled after 0s
test / test (push) Canceled after 0s
M14, M15: queryEach gives each resolver 2 s within the check's context and asks the one that answered last first; an address or PTR lookup not answered is Unknown, an answered mismatch beside it still an error; Checker.Server keeps the last answer's addresses, so SPF does not go Unknown with it. Notifications report faults only, now tested with SPF unknown alone and beside a wrong DKIM. DMARC reports: tests of malformed XML, gzip and zip bombs, a message over the limit, the service's Resync, hosted suggestion and validation. The finding: both limits cut silently and an oversized report failed as damaged; readAtMost refuses it for its size (MaxMessage 10 MiB, moved into IngestMessage). healthReadings replaces map[string]any for Health and Overview. L5: an inbound upstream has one TLS mode - a second domain to the same [host]:port with another mode is refused, naming the first; the TLS policy map holds each next hop once. Tested on the test server: go vet + go test of 25 packages, make e2e (all pass, 631 s) on this tree. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -53,6 +53,9 @@ func (f *fakeResolver) LookupIPAddr(_ context.Context, host string) ([]net.IPAdd
|
||||
|
||||
func (f *fakeResolver) LookupAddr(_ context.Context, addr string) ([]string, error) {
|
||||
f.lookups++
|
||||
if f.fail[addr] {
|
||||
return nil, &net.DNSError{Err: "i/o timeout", Name: addr, IsTimeout: true}
|
||||
}
|
||||
if v, ok := f.ptr[addr]; ok {
|
||||
return v, nil
|
||||
}
|
||||
@@ -411,3 +414,50 @@ func TestInboundMXAbsent(t *testing.T) {
|
||||
t.Fatalf("status = %q, want error", got.Status)
|
||||
}
|
||||
}
|
||||
|
||||
// A PTR lookup the resolvers did not answer is not known, not "no PTR record"
|
||||
// (audit 2026-10-12, M15); an answered address that does not match is still
|
||||
// an error.
|
||||
func TestServerPTRTimeoutIsUnknown(t *testing.T) {
|
||||
f := &fakeResolver{
|
||||
addr: map[string][]net.IPAddr{"mail.example.com": ipAddrs("203.0.113.10", "203.0.113.11")},
|
||||
ptr: map[string][]string{"203.0.113.10": {"mail.example.com."}},
|
||||
fail: map[string]bool{"203.0.113.11": true},
|
||||
}
|
||||
srv := newTestChecker(f).Server("mail.example.com", false)
|
||||
if srv.PTR.Status != health.StatusUnknown || !strings.Contains(srv.PTR.Detail, "Could not check the reverse DNS") {
|
||||
t.Fatalf("status = %q (%s), want Unknown", srv.PTR.Status, srv.PTR.Detail)
|
||||
}
|
||||
if len(srv.IPs) != 2 {
|
||||
t.Errorf("IPs = %v", srv.IPs)
|
||||
}
|
||||
|
||||
f.ptr["203.0.113.10"] = []string{"other.example."}
|
||||
srv = newTestChecker(f).Server("mail.example.com", true)
|
||||
if srv.PTR.Status != health.StatusError {
|
||||
t.Errorf("a mismatch beside a timeout = %q, want the error the mismatch is", srv.PTR.Status)
|
||||
}
|
||||
}
|
||||
|
||||
// The address lookup not answered is not known, not "does not resolve"; the
|
||||
// addresses the last answer gave are kept, so a domain's SPF check that needs
|
||||
// them does not go Unknown with it (audit 2026-10-12, M15).
|
||||
func TestServerAddressTimeoutIsUnknownAndKeepsTheAddresses(t *testing.T) {
|
||||
f := &fakeResolver{
|
||||
addr: map[string][]net.IPAddr{"mail.example.com": ipAddrs("203.0.113.10")},
|
||||
ptr: map[string][]string{"203.0.113.10": {"mail.example.com."}},
|
||||
fail: map[string]bool{},
|
||||
}
|
||||
c := newTestChecker(f)
|
||||
if srv := c.Server("mail.example.com", false); srv.PTR.Status != health.StatusOK {
|
||||
t.Fatalf("first check = %q", srv.PTR.Status)
|
||||
}
|
||||
f.fail["mail.example.com"] = true
|
||||
srv := c.Server("mail.example.com", true)
|
||||
if srv.PTR.Status != health.StatusUnknown || strings.Contains(srv.PTR.Detail, "does not resolve") {
|
||||
t.Fatalf("status = %q (%s), want Unknown", srv.PTR.Status, srv.PTR.Detail)
|
||||
}
|
||||
if len(srv.IPs) != 1 || srv.IPs[0] != "203.0.113.10" {
|
||||
t.Errorf("IPs = %v, want the ones the last answer gave", srv.IPs)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user