From: W.C.A. Wijngaards Date: Tue, 21 Apr 2026 08:09:02 +0000 (+0200) Subject: - Fix for iterator RCODE handling of YXDOMAIN. This fixes X-Git-Tag: release-1.25.0rc1~7 X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=197a425c7dc1cb265895da52c4955d83b3699a63;p=thirdparty%2Funbound.git - Fix for iterator RCODE handling of YXDOMAIN. This fixes that the server only accepts YXDOMAIN answers that contain a DNAME record. This stops bad answers, and checks that the authoritative server gives correct replies. Thanks to Qifan Zhang, Palo Alto Networks for the report. --- diff --git a/doc/Changelog b/doc/Changelog index 430ccd9a6..a96f08a11 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -14,6 +14,11 @@ the server does not echo extended rcode values after class chaos queries. Thanks to Qifan Zhang, Palo Alto Networks for the report. + - Fix for iterator RCODE handling of YXDOMAIN. This fixes + that the server only accepts YXDOMAIN answers that contain + a DNAME record. This stops bad answers, and checks that + the authoritative server gives correct replies. + Thanks to Qifan Zhang, Palo Alto Networks for the report. 20 April 2026: Wouter - Fix compile warnings for thread setname routine, and test compile. diff --git a/iterator/iterator.c b/iterator/iterator.c index b8bfc4327..cc38348b3 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -3224,8 +3224,19 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, } else iter_scrub_ds(iq->response, NULL, NULL); if(type == RESPONSE_TYPE_THROWAWAY && FLAGS_GET_RCODE(iq->response->rep->flags) == LDNS_RCODE_YXDOMAIN) { - /* YXDOMAIN is a permanent error, no need to retry */ - type = RESPONSE_TYPE_ANSWER; + /* YXDOMAIN is a permanent error for DNAME expansion overflow + * (RFC 6672 Section 2.2). Only accept if the response + * contains a DNAME record in the answer section; otherwise + * treat as invalid, to make sure the authoritative answer + * make sense. */ + size_t i; + for(i=0; iresponse->rep->an_numrrsets; i++) { + if(ntohs(iq->response->rep->rrsets[i]->rk.type) + == LDNS_RR_TYPE_DNAME) { + type = RESPONSE_TYPE_ANSWER; + break; + } + } } if(type == RESPONSE_TYPE_CNAME) origtypecname = 1; diff --git a/testdata/iter_scrub_promiscuous.rpl b/testdata/iter_scrub_promiscuous.rpl index febbee81c..e380277fd 100644 --- a/testdata/iter_scrub_promiscuous.rpl +++ b/testdata/iter_scrub_promiscuous.rpl @@ -204,6 +204,25 @@ RANGE_END ; ns.pollute4.mesa RANGE_BEGIN 0 400 ADDRESS 1.2.4.4 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +ns.pollute4.mesa. IN A +SECTION ANSWER +ns.pollute4.mesa. IN A 1.2.4.4 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +ns.pollute4.mesa. IN AAAA +SECTION AUTHORITY +pollute4.mesa. IN SOA ns.pollute4.mesa. host.pollute4.mesa 20701 3600 3600 604800 3600 +ENTRY_END ; This is the spoofed answer that is returned. ENTRY_BEGIN @@ -423,14 +442,18 @@ ENTRY_END STEP 130 CHECK_ANSWER ENTRY_BEGIN MATCH all -REPLY QR RD RA YXDOMAIN +REPLY QR RD RA SERVFAIL SECTION QUESTION test4.atkr.pollute4.mesa. IN A -SECTION ANSWER -test4.atkr.pollute4.mesa. 86400 IN A 1.2.3.4 -SECTION AUTHORITY -; removed record -;pollute4.mesa. 0 IN NS ns.attacker.mesa. +; Since the reply does not contain a DNAME, it is rejected as YXDOMAIN answer. +;REPLY QR RD RA YXDOMAIN +;SECTION QUESTION +;test4.atkr.pollute4.mesa. IN A +;SECTION ANSWER +;test4.atkr.pollute4.mesa. 86400 IN A 1.2.3.4 +;SECTION AUTHORITY +;; removed record +;;pollute4.mesa. 0 IN NS ns.attacker.mesa. ENTRY_END ; Check the cache contents, for query 4.