From e980affba0ed40476d977d284d142988633b6a7d Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Tue, 17 Nov 2020 11:03:21 +1100 Subject: [PATCH] Fix DNAME when QTYPE is CNAME or ANY The synthesised CNAME is not supposed to be followed when the QTYPE is CNAME or ANY as the lookup is satisfied by the CNAME record. --- CHANGES | 3 ++ bin/tests/system/auth/ns1/example.com.db | 1 + bin/tests/system/auth/tests.sh | 48 ++++++++++++++++++++++++ doc/notes/notes-current.rst | 3 ++ lib/ns/query.c | 19 +++++++--- 5 files changed, 68 insertions(+), 6 deletions(-) diff --git a/CHANGES b/CHANGES index 8aa352925c..90a7585b92 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,6 @@ +5534. [bug] The synthesised CNAME from a DNAME was incorrectly + followed when the QTYPE was CNAME or ANY. [GL #2280] + 5533. [func] Add "stale-refresh-time" option, a time window that starts after a failed lookup, during which stale rrset will be served directly from cache before a new diff --git a/bin/tests/system/auth/ns1/example.com.db b/bin/tests/system/auth/ns1/example.com.db index a6c98de41c..a61c88229e 100644 --- a/bin/tests/system/auth/ns1/example.com.db +++ b/bin/tests/system/auth/ns1/example.com.db @@ -20,3 +20,4 @@ ns A 10.53.0.1 www CNAME server.example.net. inzone CNAME a.example.com. a A 10.53.0.1 +dname DNAME @ diff --git a/bin/tests/system/auth/tests.sh b/bin/tests/system/auth/tests.sh index f9daf43426..ecfd451ad5 100644 --- a/bin/tests/system/auth/tests.sh +++ b/bin/tests/system/auth/tests.sh @@ -127,6 +127,54 @@ grep "a.example.com.*A.*10.53.0.1" dig.out.test$n > /dev/null || ret=1 [ $ret -eq 0 ] || echo_i "failed" status=`expr $status + $ret` +n=`expr $n + 1` +echo_i "check that in-zone CNAME records does not return target data when QTYPE is CNAME (rd=1/ra=1) ($n)" +ret=0 +$DIG $DIGOPTS @10.53.0.2 -t cname inzone.example.com > dig.out.test$n || ret=1 +grep 'ANSWER: 1,' dig.out.test$n > /dev/null || ret=1 +grep 'flags: qr aa rd ra;' dig.out.test$n > /dev/null || ret=1 +grep 'inzone\.example\.com\..*CNAME.a\.example\.com\.' dig.out.test$n > /dev/null || ret=1 +grep 'a\.example\.com\..*A.10\.53\.0\.1' dig.out.test$n > /dev/null && ret=1 +[ $ret -eq 0 ] || echo_i "failed" +status=`expr $status + $ret` + +n=`expr $n + 1` +echo_i "check that in-zone CNAME records does not return target data when QTYPE is ANY (rd=1/ra=1) ($n)" +ret=0 +$DIG $DIGOPTS @10.53.0.2 -t any inzone.example.com > dig.out.test$n || ret=1 +grep 'ANSWER: 1,' dig.out.test$n > /dev/null || ret=1 +grep 'flags: qr aa rd ra;' dig.out.test$n > /dev/null || ret=1 +grep 'inzone\.example\.com\..*CNAME.a\.example\.com\.' dig.out.test$n > /dev/null || ret=1 +grep 'a\.example\.com\..*A.10\.53\.0\.1' dig.out.test$n > /dev/null && ret=1 +[ $ret -eq 0 ] || echo_i "failed" +status=`expr $status + $ret` + +n=`expr $n + 1` +echo_i "check that in-zone DNAME records does not return target data when QTYPE is CNAME (rd=1/ra=1) ($n)" +ret=0 +$DIG $DIGOPTS @10.53.0.2 -t cname inzone.dname.example.com > dig.out.test$n || ret=1 +grep 'ANSWER: 2,' dig.out.test$n > /dev/null || ret=1 +grep 'flags: qr aa rd ra;' dig.out.test$n > /dev/null || ret=1 +grep 'dname\.example\.com\..*DNAME.example\.com\.' dig.out.test$n > /dev/null || ret=1 +grep 'inzone\.dname\.example\.com\..*CNAME.inzone\.example\.com\.' dig.out.test$n > /dev/null || ret=1 +grep 'inzone\.example\.com\..*CNAME.a\.example\.com\.' dig.out.test$n > /dev/null && ret=1 +grep 'a\.example\.com\..*A.10\.53\.0\.1' dig.out.test$n > /dev/null && ret=1 +[ $ret -eq 0 ] || echo_i "failed" +status=`expr $status + $ret` + +n=`expr $n + 1` +echo_i "check that in-zone DNAME records does not return target data when QTYPE is ANY (rd=1/ra=1) ($n)" +ret=0 +$DIG $DIGOPTS @10.53.0.2 -t any inzone.dname.example.com > dig.out.test$n || ret=1 +grep 'ANSWER: 2,' dig.out.test$n > /dev/null || ret=1 +grep 'flags: qr aa rd ra;' dig.out.test$n > /dev/null || ret=1 +grep 'dname\.example\.com\..*DNAME.example\.com\.' dig.out.test$n > /dev/null || ret=1 +grep 'inzone\.dname\.example\.com\..*CNAME.inzone\.example\.com\.' dig.out.test$n > /dev/null || ret=1 +grep 'inzone\.example\.com.*CNAME.a\.example\.com\.' dig.out.test$n > /dev/null && ret=1 +grep 'a\.example\.com.*A.10\.53\.0\.1' dig.out.test$n > /dev/null && ret=1 +[ $ret -eq 0 ] || echo_i "failed" +status=`expr $status + $ret` + n=`expr $n + 1` echo_i "check that CHAOS addresses are compared correctly ($n)" ret=0 diff --git a/doc/notes/notes-current.rst b/doc/notes/notes-current.rst index 2a784e95fd..412993e09f 100644 --- a/doc/notes/notes-current.rst +++ b/doc/notes/notes-current.rst @@ -65,3 +65,6 @@ Bug Fixes - ``named`` could crash with an assertion failure if a TCP connection is closed while the request is still processing. [GL #2227] + +- The synthesised CNAME from a DNAME was incorrectly followed when the QTYPE + was CNAME or ANY. [GL #2280] diff --git a/lib/ns/query.c b/lib/ns/query.c index 33c7b6a2db..e329b55e4c 100644 --- a/lib/ns/query.c +++ b/lib/ns/query.c @@ -9897,13 +9897,20 @@ query_dname(query_ctx_t *qctx) { } /* - * Switch to the new qname and restart. + * If the original query was not for a CNAME or ANY then follow the + * CNAME. */ - ns_client_qnamereplace(qctx->client, qctx->fname); - qctx->fname = NULL; - qctx->want_restart = true; - if (!WANTRECURSION(qctx->client)) { - qctx->options |= DNS_GETDB_NOLOG; + if (qctx->qtype != dns_rdatatype_cname && + qctx->qtype != dns_rdatatype_any) { + /* + * Switch to the new qname and restart. + */ + ns_client_qnamereplace(qctx->client, qctx->fname); + qctx->fname = NULL; + qctx->want_restart = true; + if (!WANTRECURSION(qctx->client)) { + qctx->options |= DNS_GETDB_NOLOG; + } } query_addauth(qctx);