From: Michał Kępień Date: Fri, 30 May 2025 16:08:54 +0000 (+0200) Subject: Properly handle CNAMEs when preparing responses X-Git-Tag: v9.21.9~19^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1b8ceec580aad69b0c869bc01c126d778040caab;p=thirdparty%2Fbind9.git Properly handle CNAMEs when preparing responses dnspython does not treat CNAME records in zone files in any special way; they are just RRsets belonging to zone nodes. Process CNAMEs when preparing zone-based responses just like a normal authoritative DNS server would. --- diff --git a/bin/tests/system/isctest/asyncserver.py b/bin/tests/system/isctest/asyncserver.py index 8f9a3c07585..bcf26a41a1c 100644 --- a/bin/tests/system/isctest/asyncserver.py +++ b/bin/tests/system/isctest/asyncserver.py @@ -266,11 +266,16 @@ class QueryContext: soa: Optional[dns.rrset.RRset] = None node: Optional[dns.node.Node] = None answer: Optional[dns.rdataset.Rdataset] = None + alias: Optional[dns.name.Name] = None @property def qname(self) -> dns.name.Name: return self.query.question[0].name + @property + def current_qname(self) -> dns.name.Name: + return self.alias or self.qname + @property def qclass(self) -> RdataClass: return self.query.question[0].rdclass @@ -809,23 +814,28 @@ class AsyncDnsServer(AsyncServer): if self._nxdomain_response(qctx): return + if self._cname_response(qctx): + return + if self._nodata_response(qctx): return self._noerror_response(qctx) def _refused_response(self, qctx: QueryContext) -> bool: - qctx.zone = self._zone_tree.find_best_zone(qctx.qname) - if qctx.zone: + zone = self._zone_tree.find_best_zone(qctx.current_qname) + if zone: + qctx.zone = zone return False - qctx.response.set_rcode(dns.rcode.REFUSED) + if not qctx.response.answer: + qctx.response.set_rcode(dns.rcode.REFUSED) return True def _delegation_response(self, qctx: QueryContext) -> bool: assert qctx.zone - name = qctx.qname + name = qctx.current_qname delegation = None while name != qctx.zone.origin: @@ -870,9 +880,9 @@ class AsyncDnsServer(AsyncServer): qctx.soa = qctx.zone.find_rrset(qctx.zone.origin, dns.rdatatype.SOA) assert qctx.soa - qctx.node = qctx.zone.get_node(qctx.qname) + qctx.node = qctx.zone.get_node(qctx.current_qname) if qctx.node or not any( - n for n in qctx.zone.nodes if n.is_subdomain(qctx.qname) + n for n in qctx.zone.nodes if n.is_subdomain(qctx.current_qname) ): return False @@ -890,6 +900,21 @@ class AsyncDnsServer(AsyncServer): qctx.response.authority.append(qctx.soa) return True + def _cname_response(self, qctx: QueryContext) -> bool: + assert qctx.node + + cname = qctx.node.get_rdataset(qctx.qclass, dns.rdatatype.CNAME) + if not cname: + return False + + cname_rrset = dns.rrset.RRset(qctx.current_qname, qctx.qclass, cname.rdtype) + cname_rrset.update(cname) + qctx.response.answer.append(cname_rrset) + + qctx.alias = cname[0].target + self._prepare_response_from_zone_data(qctx) + return True + def _nodata_response(self, qctx: QueryContext) -> bool: assert qctx.node assert qctx.soa @@ -899,13 +924,14 @@ class AsyncDnsServer(AsyncServer): return False qctx.response.set_rcode(dns.rcode.NOERROR) - qctx.response.authority.append(qctx.soa) + if not qctx.response.answer: + qctx.response.authority.append(qctx.soa) return True def _noerror_response(self, qctx: QueryContext) -> None: assert qctx.answer - answer_rrset = dns.rrset.RRset(qctx.qname, qctx.qclass, qctx.qtype) + answer_rrset = dns.rrset.RRset(qctx.current_qname, qctx.qclass, qctx.qtype) answer_rrset.update(qctx.answer) qctx.response.set_rcode(dns.rcode.NOERROR)