From 8eb36844a08d61d156b771fd8fe4f7cb4697078f Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Sun, 18 Jan 2026 12:50:57 +0000 Subject: [PATCH] Log SERVFAIL from usptream servers. If we're doing DNSSEC validation and fail because the upstream reply to our query is SERVFAIL, log this as the reason in validation result line. This will make maintainers lives easier when they get reports of "wrong" validation failure, which is sometimes an upstream problem. --- src/cache.c | 1 + src/dns-protocol.h | 1 + src/dnsmasq.h | 1 + src/dnssec.c | 10 +++++++--- src/forward.c | 5 +++++ 5 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/cache.c b/src/cache.c index c4edfd3..cb80cde 100644 --- a/src/cache.c +++ b/src/cache.c @@ -2250,6 +2250,7 @@ static char *edestr(int ede) case EDE_UNS_NS3_ITER: return "unsupported NSEC3 iterations value"; case EDE_UNABLE_POLICY: return "uanble to conform to policy"; case EDE_SYNTHESIZED: return "synthesized"; + case EDE_US_SERVFAIL: return "upstream returned SERVFAIL"; default: return "unknown"; } } diff --git a/src/dns-protocol.h b/src/dns-protocol.h index 0db5913..e71bedc 100644 --- a/src/dns-protocol.h +++ b/src/dns-protocol.h @@ -86,6 +86,7 @@ #define EDNS0_OPTION_UMBRELLA 20292 /* Cisco Umbrella temporary assignment */ /* RFC-8914 extended errors, negative values are our definitions */ +#define EDE_US_SERVFAIL -2 /* SERVFAIL from usptream */ #define EDE_UNSET -1 /* No extended DNS error available */ #define EDE_OTHER 0 /* Other */ #define EDE_USUPDNSKEY 1 /* Unsupported DNSKEY algo */ diff --git a/src/dnsmasq.h b/src/dnsmasq.h index ef7e96d..b794e55 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -778,6 +778,7 @@ struct dyndir { #define DNSSEC_FAIL_NSEC3_ITERS 0x0200 /* too many iterations in NSEC3 */ #define DNSSEC_FAIL_BADPACKET 0x0400 /* bad packet */ #define DNSSEC_FAIL_WORK 0x0800 /* too much crypto */ +#define DNSSEC_FAIL_UPSTREAM 0x1000 /* SERVFAIL from UPSTREAM */ #define STAT_ISEQUAL(a, b) (((a) & 0xffff0000) == (b)) diff --git a/src/dnssec.c b/src/dnssec.c index 81a6fce..83cc879 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -1993,8 +1993,11 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch if (neganswer) *neganswer = 0; + + if (RCODE(header) == SERVFAIL) + return STAT_BOGUS | DNSSEC_FAIL_UPSTREAM; - if (RCODE(header) == SERVFAIL || ntohs(header->qdcount) != 1) + if (ntohs(header->qdcount) != 1) return STAT_BOGUS; if (RCODE(header) != NXDOMAIN && RCODE(header) != NOERROR) @@ -2365,8 +2368,9 @@ int errflags_to_ede(int status) /* We can end up with more than one flag set for some errors, so this encodes a rough priority so the (eg) No sig is reported before no-unexpired-sig. */ - - if (status & DNSSEC_FAIL_NYV) + if (status & DNSSEC_FAIL_UPSTREAM) + return EDE_US_SERVFAIL; + else if (status & DNSSEC_FAIL_NYV) return EDE_SIG_NYV; else if (status & DNSSEC_FAIL_EXP) return EDE_SIG_EXP; diff --git a/src/forward.c b/src/forward.c index 1e935eb..30adedc 100644 --- a/src/forward.c +++ b/src/forward.c @@ -1399,6 +1399,9 @@ void return_reply(time_t now, struct frec *forward, struct dns_header *header, s a.log.ede = ede; log_query(F_SECSTAT, domain, &a, result, 0); + + if (ede == EDE_US_SERVFAIL) + ede = EDE_DNSSEC_BOGUS; } } @@ -2665,6 +2668,8 @@ unsigned char *tcp_request(int confd, time_t now, a.log.ede = ede; log_query(F_SECSTAT, domain, &a, result, 0); + if (ede == EDE_US_SERVFAIL) + ede = EDE_DNSSEC_BOGUS; if ((daemon->limit[LIMIT_CRYPTO] - validatecount) > (int)daemon->metrics[METRIC_CRYPTO_HWM]) daemon->metrics[METRIC_CRYPTO_HWM] = daemon->limit[LIMIT_CRYPTO] - validatecount;