diff --git a/CHANGES b/CHANGES index 64fdd64740..78b4b267b1 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,6 @@ +2748. [func] Identify bad answers from GTLD servers and treat them + as referrals. [RT #18884] + 2747. [bug] Journal roll forwards failed to set the re-signing time of RRSIGs correctly. [RT #20541] diff --git a/bin/tests/system/resolver/ans2/ans.pl b/bin/tests/system/resolver/ans2/ans.pl index 2d49de836f..25932f6bc0 100644 --- a/bin/tests/system/resolver/ans2/ans.pl +++ b/bin/tests/system/resolver/ans2/ans.pl @@ -15,7 +15,7 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -# $Id: ans.pl,v 1.12 2009/05/29 23:47:49 tbox Exp $ +# $Id: ans.pl,v 1.13 2009/11/04 02:15:30 marka Exp $ # # Ad hoc name server @@ -68,6 +68,7 @@ for (;;) { $qname eq "foo.baddname.example.org" || $qname eq "foo.gooddname.example.org") { # Data for address/alias filtering. + $packet->header->aa(1); if ($qtype eq "A") { $packet->push("answer", new Net::DNS::RR($qname . diff --git a/bin/tests/system/resolver/ans3/ans.pl b/bin/tests/system/resolver/ans3/ans.pl index 30cc3ff6c3..966c3fc72e 100644 --- a/bin/tests/system/resolver/ans3/ans.pl +++ b/bin/tests/system/resolver/ans3/ans.pl @@ -15,7 +15,7 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -# $Id: ans.pl,v 1.11 2009/05/29 23:47:49 tbox Exp $ +# $Id: ans.pl,v 1.12 2009/11/04 02:15:30 marka Exp $ # # Ad hoc name server @@ -49,6 +49,7 @@ for (;;) { $packet->print; $packet->header->qr(1); + $packet->header->aa(1); my @questions = $packet->question; my $qname = $questions[0]->qname; diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index bf9c42859c..bc5f62b6f6 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: resolver.c,v 1.408 2009/10/28 18:04:29 each Exp $ */ +/* $Id: resolver.c,v 1.409 2009/11/04 02:15:29 marka Exp $ */ /*! \file */ @@ -4819,7 +4819,9 @@ mark_related(dns_name_t *name, dns_rdataset_t *rdataset, } static isc_result_t -check_related(void *arg, dns_name_t *addname, dns_rdatatype_t type) { +check_section(void *arg, dns_name_t *addname, dns_rdatatype_t type, + dns_section_t section) +{ fetchctx_t *fctx = arg; isc_result_t result; dns_name_t *name; @@ -4830,15 +4832,19 @@ check_related(void *arg, dns_name_t *addname, dns_rdatatype_t type) { REQUIRE(VALID_FCTX(fctx)); +#if CHECK_FOR_GLUE_IN_ANSWER + if (section == DNS_SECTION_ANSWER && type != dns_rdatatype_a) + return (ISC_R_SUCCESS); +#endif + if (GLUING(fctx)) gluing = ISC_TRUE; else gluing = ISC_FALSE; name = NULL; rdataset = NULL; - result = dns_message_findname(fctx->rmessage, DNS_SECTION_ADDITIONAL, - addname, dns_rdatatype_any, 0, &name, - NULL); + result = dns_message_findname(fctx->rmessage, section, addname, + dns_rdatatype_any, 0, &name, NULL); if (result == ISC_R_SUCCESS) { external = ISC_TF(!dns_name_issubdomain(name, &fctx->domain)); if (type == dns_rdatatype_a) { @@ -4876,6 +4882,21 @@ check_related(void *arg, dns_name_t *addname, dns_rdatatype_t type) { return (ISC_R_SUCCESS); } +static isc_result_t +check_related(void *arg, dns_name_t *addname, dns_rdatatype_t type) { + return (check_section(arg, addname, type, DNS_SECTION_ADDITIONAL)); +} + +#ifndef CHECK_FOR_GLUE_IN_ANSWER +#define CHECK_FOR_GLUE_IN_ANSWER 0 +#endif +#if CHECK_FOR_GLUE_IN_ANSWER +static isc_result_t +check_answer(void *arg, dns_name_t *addname, dns_rdatatype_t type) { + return (check_section(arg, addname, type, DNS_SECTION_ANSWER)); +} +#endif + static void chase_additional(fetchctx_t *fctx) { isc_boolean_t rescan; @@ -5103,14 +5124,17 @@ is_answertarget_allowed(dns_view_t *view, dns_name_t *name, /* * Handle a no-answer response (NXDOMAIN, NXRRSET, or referral). - * If bind8_ns_resp is ISC_TRUE, this is a suspected BIND 8 - * response to an NS query that should be treated as a referral - * even though the NS records occur in the answer section - * rather than the authority section. + * If look_in_options has LOOK_FOR_NS_IN_ANSWER then we look in the answer + * section for the NS RRset if the query type is NS; if it has + * LOOK_FOR_GLUE_IN_ANSWER we look for glue incorrectly returned in the answer + * section for A and AAAA queries. */ +#define LOOK_FOR_NS_IN_ANSWER 0x1 +#define LOOK_FOR_GLUE_IN_ANSWER 0x2 + static isc_result_t noanswer_response(fetchctx_t *fctx, dns_name_t *oqname, - isc_boolean_t bind8_ns_resp) + unsigned int look_in_options) { isc_result_t result; dns_message_t *message; @@ -5118,11 +5142,16 @@ noanswer_response(fetchctx_t *fctx, dns_name_t *oqname, dns_rdataset_t *rdataset, *ns_rdataset; isc_boolean_t aa, negative_response; dns_rdatatype_t type; - dns_section_t section = - bind8_ns_resp ? DNS_SECTION_ANSWER : DNS_SECTION_AUTHORITY; + dns_section_t section; FCTXTRACE("noanswer_response"); + if ((look_in_options & LOOK_FOR_NS_IN_ANSWER) != 0) { + INSIST(fctx->type == dns_rdatatype_ns); + section = DNS_SECTION_ANSWER; + } else + section = DNS_SECTION_AUTHORITY; + message = fctx->rmessage; /* @@ -5403,6 +5432,20 @@ noanswer_response(fetchctx_t *fctx, dns_name_t *oqname, fctx->attributes |= FCTX_ATTR_GLUING; (void)dns_rdataset_additionaldata(ns_rdataset, check_related, fctx); +#if CHECK_FOR_GLUE_IN_ANSWER + /* + * Look in the answer section for "glue" that is incorrectly + * returned as a answer. This is needed if the server also + * minimizes the response size by not adding records to the + * additional section that are in the answer section or if + * the record gets dropped due to message size constraints. + */ + if ((look_in_options & LOOK_FOR_GLUE_IN_ANSWER) != 0 && + (fctx->type == dns_rdatatype_aaaa || + fctx->type == dns_rdatatype_a)) + (void)dns_rdataset_additionaldata(ns_rdataset, + check_answer, fctx); +#endif fctx->attributes &= ~FCTX_ATTR_GLUING; /* * NS rdatasets with 0 TTL cause problems. @@ -5817,7 +5860,7 @@ answer_response(fetchctx_t *fctx) { * If it isn't a noanswer response, no harm will be * done. */ - return (noanswer_response(fctx, qname, ISC_FALSE)); + return (noanswer_response(fctx, qname, 0)); } /* @@ -6137,6 +6180,16 @@ log_packet(dns_message_t *message, int level, isc_mem_t *mctx) { isc_mem_put(mctx, buf, len); } +static isc_boolean_t +iscname(fetchctx_t *fctx) { + isc_result_t result; + + result = dns_message_findname(fctx->rmessage, DNS_SECTION_ANSWER, + &fctx->name, dns_rdatatype_cname, 0, + NULL, NULL); + return (result == ISC_R_SUCCESS ? ISC_TRUE : ISC_FALSE); +} + static void resquery_response(isc_task_t *task, isc_event_t *event) { isc_result_t result = ISC_R_SUCCESS; @@ -6576,27 +6629,56 @@ resquery_response(isc_task_t *task, isc_event_t *event) { (message->rcode == dns_rcode_noerror || message->rcode == dns_rcode_nxdomain)) { /* - * We've got answers. However, if we sent - * a BIND 8 server an NS query, it may have - * incorrectly responded with a non-authoritative - * answer instead of a referral. Since this - * answer lacks the SIGs necessary to do DNSSEC - * validation, we must invoke the following special - * kludge to treat it as a referral. + * [normal case] + * We've got answers. If it has an authoritative answer or an + * answer from a forwarder, we're done. */ - if (fctx->type == dns_rdatatype_ns && - (message->flags & DNS_MESSAGEFLAG_AA) == 0 && - !ISFORWARDER(query->addrinfo)) - { - result = noanswer_response(fctx, NULL, ISC_TRUE); + if ((message->flags & DNS_MESSAGEFLAG_AA) != 0 || + ISFORWARDER(query->addrinfo)) + result = answer_response(fctx); + else if (iscname(fctx) && + fctx->type != dns_rdatatype_any && + fctx->type != dns_rdatatype_cname) { + /* + * A BIND8 server could return a non-authoritative + * answer when a CNAME is followed. We should treat + * it as a valid answer. + */ + result = answer_response(fctx); + } else { + if (fctx->type == dns_rdatatype_ns) { + /* + * A BIND 8 server could incorrectly return a + * non-authoritative answer to an NS query + * instead of a referral. Since this answer + * lacks the SIGs necessary to do DNSSEC + * validation, we must invoke the following + * special kludge to treat it as a referral. + */ + result = noanswer_response(fctx, NULL, + LOOK_FOR_NS_IN_ANSWER); + } else { + /* + * Some other servers may still somehow include + * an answer when it should return a referral + * with an empty answer. Check to see if we can + * treat this as a referral by ignoring the + * answer. Further more, there may be an + * implementation that moves A/AAAA glue records + * to the answer section for that type of + * delegation when the query is for that glue + * record. LOOK_FOR_GLUE_IN_ANSWER will handle + * such a corner case. + */ + result = noanswer_response(fctx, NULL, + LOOK_FOR_GLUE_IN_ANSWER); + } if (result != DNS_R_DELEGATION) { /* - * The answer section must have contained - * something other than the NS records - * we asked for. Since AA is not set - * and the server is not a forwarder, - * it is technically lame and it's easier - * to treat it as such than to figure out + * At this point, AA is not set, the response + * is not a referral, and the server is not a + * forwarder. It is technically lame and it's + * easier to treat it as such than to figure out * some more elaborate course of action. */ broken_server = DNS_R_LAME; @@ -6605,7 +6687,6 @@ resquery_response(isc_task_t *task, isc_event_t *event) { } goto force_referral; } - result = answer_response(fctx); if (result != ISC_R_SUCCESS) { if (result == DNS_R_FORMERR) keep_trying = ISC_TRUE; @@ -6617,7 +6698,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) { /* * NXDOMAIN, NXRDATASET, or referral. */ - result = noanswer_response(fctx, NULL, ISC_FALSE); + result = noanswer_response(fctx, NULL, 0); if (result == DNS_R_CHASEDSSERVERS) { } else if (result == DNS_R_DELEGATION) { force_referral: