Merge branch '1736-stub-zone-foiled-by-minimal-responses' into 'main'

Resolve "stub zone foiled by minimal-responses"

Closes #1736

See merge request isc-projects/bind9!4113
This commit is contained in:
Diego dos Santos Fronza 2020-10-21 15:06:04 +00:00
commit e64e3322a6
10 changed files with 635 additions and 134 deletions

View file

@ -1,3 +1,7 @@
5518. [bug] Fix stub zone not transferring nameserver addresses
from masters configured with 'minimal-responses yes'.
[GL #1736]
5517. [bug] Handle 'UV_EOF' differently and don't contribute it to
the RECVFAIL statistic count. [GL #2208]

View file

@ -226,7 +226,7 @@ dotests() {
$DIG $DIGOPTS -t NS rt.example @10.53.0.1 > dig.out.$n || ret=1
case $minimal in
yes)
grep 'ADDITIONAL: 1' dig.out.$n > /dev/null || ret=1
grep 'ADDITIONAL: 2' dig.out.$n > /dev/null || ret=1
;;
no)
grep 'ADDITIONAL: 2' dig.out.$n > /dev/null || ret=1

View file

@ -12,9 +12,10 @@
#
# Clean up after stub tests.
#
rm -f dig.out.ns3 ns3/child.example.st
rm -f dig.out.ns[35] ns3/child.example.st
rm -f */named.memstats
rm -f */named.conf
rm -f */named.run
rm -f ns*/named.lock
rm -f ns*/managed-keys.bind*
rm -f ns5/example.db

View file

@ -0,0 +1,21 @@
; Copyright (C) Internet Systems Consortium, Inc. ("ISC")
;
; This Source Code Form is subject to the terms of the Mozilla Public
; License, v. 2.0. If a copy of the MPL was not distributed with this
; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;
; See the COPYRIGHT file distributed with this work for additional
; information regarding copyright ownership.
$TTL 300 ; 5 minutes
@ IN SOA ns4.example. hostmaster.example. (
2000042795 ; serial
20 ; refresh (20 seconds)
20 ; retry (20 seconds)
1814400 ; expire (3 weeks)
3600 ; minimum (1 hour)
)
@ IN NS ns4
ns4 IN A 10.53.0.4
IN AAAA fd92:7065:b8e:ffff::4
target IN TXT "test"

View file

@ -0,0 +1,29 @@
/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
options {
query-source address 10.53.0.4;
notify-source 10.53.0.4;
transfer-source 10.53.0.4;
port @PORT@;
pid-file "named.pid";
listen-on { 10.53.0.4; };
listen-on-v6 { none; };
recursion no;
notify yes;
minimal-responses yes;
dnssec-validation no;
};
zone "example" {
type primary;
file "example.db";
};

View file

@ -0,0 +1,32 @@
/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
options {
query-source address 10.53.0.5;
notify-source 10.53.0.5;
transfer-source 10.53.0.5;
port @PORT@;
pid-file "named.pid";
listen-on { 10.53.0.5; };
listen-on-v6 { none; };
dnssec-validation no;
};
zone "." {
type hint;
file "../../common/root.hint";
};
zone "example" {
type stub;
file "example.db";
masters { 10.53.0.4 port @PORT@; };
};

View file

@ -14,3 +14,5 @@
copy_setports ns1/named.conf.in ns1/named.conf
copy_setports ns2/named.conf.in ns2/named.conf
copy_setports ns3/named.conf.in ns3/named.conf
copy_setports ns4/named.conf.in ns4/named.conf
copy_setports ns5/named.conf.in ns5/named.conf

View file

@ -59,5 +59,26 @@ digcomp knowngood.dig.out.rec dig.out.ns3 || ret=1
}
done
echo_i "check that glue record is correctly transferred from master when minimal-responses is on"
ret=0
# First ensure that zone data was transfered.
for i in 1 2 3 4 5 6 7; do
[ -f ns5/example.db ] && break
sleep 1
done
if [ -f ns5/example.db ]; then
# If NS glue wasn't transferred, this query would fail.
$DIG $DIGOPTS +nodnssec @10.53.0.5 target.example. txt > dig.out.ns5 || ret=1
grep 'target\.example.*TXT.*"test"' dig.out.ns5 > /dev/null || ret=1
# Ensure both ipv4 and ipv6 glue records were transferred.
grep -E 'ns4[[:space:]]+A[[:space:]]+10.53.0.4' ns5/example.db > /dev/null || ret=1
grep -E 'AAAA[[:space:]]+fd92:7065:b8e:ffff::4' ns5/example.db > /dev/null || ret=1
[ $ret = 0 ] || { status=1; echo_i "failed"; }
else
status=1
echo_i "failed: stub zone transfer failed ns4(master) <---> ns5/example.db"
fi
echo_i "exit status: $status"
[ $status -eq 0 ] || exit 1

View file

@ -614,6 +614,7 @@ struct dns_stub {
dns_zone_t *zone;
dns_db_t *db;
dns_dbversion_t *version;
atomic_uint_fast32_t pending_requests;
};
/*%
@ -937,6 +938,22 @@ struct ssevent {
uint32_t serial;
};
struct stub_cb_args {
dns_stub_t *stub;
dns_tsigkey_t *tsig_key;
isc_dscp_t dscp;
uint16_t udpsize;
int timeout;
bool reqnsid;
};
struct stub_glue_request {
dns_request_t *request;
dns_name_t name;
struct stub_cb_args *args;
bool ipv4;
};
/*%
* Increment resolver-related statistics counters. Zone must be locked.
*/
@ -12491,9 +12508,395 @@ cleanup1:
/***
*** Private
***/
static inline isc_result_t
create_query(dns_zone_t *zone, dns_rdatatype_t rdtype, dns_name_t *name,
dns_message_t **messagep) {
dns_message_t *message = NULL;
dns_name_t *qname = NULL;
dns_rdataset_t *qrdataset = NULL;
isc_result_t result;
dns_message_create(zone->mctx, DNS_MESSAGE_INTENTRENDER, &message);
message->opcode = dns_opcode_query;
message->rdclass = zone->rdclass;
result = dns_message_gettempname(message, &qname);
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
result = dns_message_gettemprdataset(message, &qrdataset);
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
/*
* Make question.
*/
dns_name_init(qname, NULL);
dns_name_clone(name, qname);
dns_rdataset_makequestion(qrdataset, zone->rdclass, rdtype);
ISC_LIST_APPEND(qname->list, qrdataset, link);
dns_message_addname(message, qname, DNS_SECTION_QUESTION);
*messagep = message;
return (ISC_R_SUCCESS);
cleanup:
if (qname != NULL) {
dns_message_puttempname(message, &qname);
}
if (qrdataset != NULL) {
dns_message_puttemprdataset(message, &qrdataset);
}
dns_message_detach(&message);
return (result);
}
static isc_result_t
add_opt(dns_message_t *message, uint16_t udpsize, bool reqnsid,
bool reqexpire) {
isc_result_t result;
dns_rdataset_t *rdataset = NULL;
dns_ednsopt_t ednsopts[DNS_EDNSOPTIONS];
int count = 0;
/* Set EDNS options if applicable. */
if (reqnsid) {
INSIST(count < DNS_EDNSOPTIONS);
ednsopts[count].code = DNS_OPT_NSID;
ednsopts[count].length = 0;
ednsopts[count].value = NULL;
count++;
}
if (reqexpire) {
INSIST(count < DNS_EDNSOPTIONS);
ednsopts[count].code = DNS_OPT_EXPIRE;
ednsopts[count].length = 0;
ednsopts[count].value = NULL;
count++;
}
result = dns_message_buildopt(message, &rdataset, 0, udpsize, 0,
ednsopts, count);
if (result != ISC_R_SUCCESS) {
return (result);
}
return (dns_message_setopt(message, rdataset));
}
/*
* Called when stub zone update is finished.
* Update zone refresh, retry, expire values accordingly with
* SOA received from master, sync database to file, restart
* zone management timer.
*/
static void
stub_finish_zone_update(dns_stub_t *stub, isc_time_t now) {
uint32_t refresh, retry, expire;
isc_result_t result;
isc_interval_t i;
unsigned int soacount;
dns_zone_t *zone = stub->zone;
/*
* Tidy up.
*/
dns_db_closeversion(stub->db, &stub->version, true);
ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_write);
if (zone->db == NULL) {
zone_attachdb(zone, stub->db);
}
result = zone_get_from_db(zone, zone->db, NULL, &soacount, NULL,
&refresh, &retry, &expire, NULL, NULL);
if (result == ISC_R_SUCCESS && soacount > 0U) {
zone->refresh = RANGE(refresh, zone->minrefresh,
zone->maxrefresh);
zone->retry = RANGE(retry, zone->minretry, zone->maxretry);
zone->expire = RANGE(expire, zone->refresh + zone->retry,
DNS_MAX_EXPIRE);
DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_HAVETIMERS);
}
ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_write);
dns_db_detach(&stub->db);
DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_REFRESH);
DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED);
DNS_ZONE_JITTER_ADD(&now, zone->refresh, &zone->refreshtime);
isc_interval_set(&i, zone->expire, 0);
DNS_ZONE_TIME_ADD(&now, zone->expire, &zone->expiretime);
if (zone->masterfile != NULL) {
zone_needdump(zone, 0);
}
zone_settimer(zone, &now);
}
/*
* Process answers for A and AAAA queries when
* resolving nameserver addresses for which glue
* was missing in a previous answer for a NS query.
*/
static void
stub_glue_response_cb(isc_task_t *task, isc_event_t *event) {
const char me[] = "stub_glue_response_cb";
dns_requestevent_t *revent = (dns_requestevent_t *)event;
dns_stub_t *stub = NULL;
dns_message_t *msg = NULL;
dns_zone_t *zone = NULL;
char master[ISC_SOCKADDR_FORMATSIZE];
char source[ISC_SOCKADDR_FORMATSIZE];
uint32_t addr_count, cnamecnt;
isc_result_t result;
isc_time_t now;
struct stub_glue_request *request;
struct stub_cb_args *cb_args;
dns_rdataset_t *addr_rdataset = NULL;
dns_dbnode_t *node = NULL;
UNUSED(task);
request = revent->ev_arg;
cb_args = request->args;
stub = cb_args->stub;
INSIST(DNS_STUB_VALID(stub));
zone = stub->zone;
ENTER;
TIME_NOW(&now);
LOCK_ZONE(zone);
if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_EXITING)) {
zone_debuglog(zone, me, 1, "exiting");
goto cleanup;
}
isc_sockaddr_format(&zone->masteraddr, master, sizeof(master));
isc_sockaddr_format(&zone->sourceaddr, source, sizeof(source));
if (revent->result != ISC_R_SUCCESS) {
dns_zonemgr_unreachableadd(zone->zmgr, &zone->masteraddr,
&zone->sourceaddr, &now);
dns_zone_log(zone, ISC_LOG_INFO,
"could not refresh stub from master %s"
" (source %s): %s",
master, source, dns_result_totext(revent->result));
goto cleanup;
}
dns_message_create(zone->mctx, DNS_MESSAGE_INTENTPARSE, &msg);
result = dns_request_getresponse(revent->request, msg, 0);
if (result != ISC_R_SUCCESS) {
dns_zone_log(zone, ISC_LOG_INFO,
"refreshing stub: unable to parse response (%s)",
isc_result_totext(result));
goto cleanup;
}
/*
* Unexpected rcode.
*/
if (msg->rcode != dns_rcode_noerror) {
char rcode[128];
isc_buffer_t rb;
isc_buffer_init(&rb, rcode, sizeof(rcode));
(void)dns_rcode_totext(msg->rcode, &rb);
dns_zone_log(zone, ISC_LOG_INFO,
"refreshing stub: "
"unexpected rcode (%.*s) from %s (source %s)",
(int)rb.used, rcode, master, source);
goto cleanup;
}
/*
* We need complete messages.
*/
if ((msg->flags & DNS_MESSAGEFLAG_TC) != 0) {
if (dns_request_usedtcp(revent->request)) {
dns_zone_log(zone, ISC_LOG_INFO,
"refreshing stub: truncated TCP "
"response from master %s (source %s)",
master, source);
}
goto cleanup;
}
/*
* If non-auth log.
*/
if ((msg->flags & DNS_MESSAGEFLAG_AA) == 0) {
dns_zone_log(zone, ISC_LOG_INFO,
"refreshing stub: "
"non-authoritative answer from "
"master %s (source %s)",
master, source);
goto cleanup;
}
/*
* Sanity checks.
*/
cnamecnt = message_count(msg, DNS_SECTION_ANSWER, dns_rdatatype_cname);
addr_count = message_count(msg, DNS_SECTION_ANSWER,
request->ipv4 ? dns_rdatatype_a
: dns_rdatatype_aaaa);
if (cnamecnt != 0) {
dns_zone_log(zone, ISC_LOG_INFO,
"refreshing stub: unexpected CNAME response "
"from master %s (source %s)",
master, source);
goto cleanup;
}
if (addr_count == 0) {
dns_zone_log(zone, ISC_LOG_INFO,
"refreshing stub: no %s records in response "
"from master %s (source %s)",
request->ipv4 ? "A" : "AAAA", master, source);
goto cleanup;
}
/*
* Extract A or AAAA RRset from message.
*/
result = dns_message_findname(msg, DNS_SECTION_ANSWER, &request->name,
request->ipv4 ? dns_rdatatype_a
: dns_rdatatype_aaaa,
dns_rdatatype_none, NULL, &addr_rdataset);
if (result != ISC_R_SUCCESS) {
if (result != DNS_R_NXDOMAIN && result != DNS_R_NXRRSET) {
char namebuf[DNS_NAME_FORMATSIZE];
dns_name_format(&request->name, namebuf,
sizeof(namebuf));
dns_zone_log(
zone, ISC_LOG_INFO,
"refreshing stub: dns_message_findname(%s/%s) "
"failed (%s)",
namebuf, request->ipv4 ? "A" : "AAAA",
isc_result_totext(result));
}
goto cleanup;
}
result = dns_db_findnode(stub->db, &request->name, true, &node);
if (result != ISC_R_SUCCESS) {
dns_zone_log(zone, ISC_LOG_INFO,
"refreshing stub: "
"dns_db_findnode() failed: %s",
dns_result_totext(result));
goto cleanup;
}
result = dns_db_addrdataset(stub->db, node, stub->version, 0,
addr_rdataset, 0, NULL);
if (result != ISC_R_SUCCESS) {
dns_zone_log(zone, ISC_LOG_INFO,
"refreshing stub: "
"dns_db_addrdataset() failed: %s",
dns_result_totext(result));
}
dns_db_detachnode(stub->db, &node);
cleanup:
if (msg != NULL) {
dns_message_detach(&msg);
}
isc_event_free(&event);
dns_name_free(&request->name, zone->mctx);
dns_request_destroy(&request->request);
isc_mem_put(zone->mctx, request, sizeof(*request));
/* If last request, release all related resources */
if (atomic_fetch_sub_release(&stub->pending_requests, 1) == 1) {
isc_mem_put(zone->mctx, cb_args, sizeof(*cb_args));
stub_finish_zone_update(stub, now);
UNLOCK_ZONE(zone);
stub->magic = 0;
dns_zone_idetach(&stub->zone);
INSIST(stub->db == NULL);
INSIST(stub->version == NULL);
isc_mem_put(stub->mctx, stub, sizeof(*stub));
} else {
UNLOCK_ZONE(zone);
}
}
/*
* Create and send an A or AAAA query to the master
* server of the stub zone given.
*/
static isc_result_t
stub_request_nameserver_address(struct stub_cb_args *args, bool ipv4,
const dns_name_t *name) {
dns_message_t *message = NULL;
dns_zone_t *zone;
isc_result_t result;
struct stub_glue_request *request;
zone = args->stub->zone;
request = isc_mem_get(zone->mctx, sizeof(*request));
request->request = NULL;
request->args = args;
request->name = (dns_name_t)DNS_NAME_INITEMPTY;
request->ipv4 = ipv4;
dns_name_dup(name, zone->mctx, &request->name);
result = create_query(zone, ipv4 ? dns_rdatatype_a : dns_rdatatype_aaaa,
&request->name, &message);
INSIST(result == ISC_R_SUCCESS);
if (!DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NOEDNS)) {
result = add_opt(message, args->udpsize, args->reqnsid, false);
if (result != ISC_R_SUCCESS) {
zone_debuglog(zone, "stub_send_query", 1,
"unable to add opt record: %s",
dns_result_totext(result));
goto fail;
}
}
atomic_fetch_add_release(&args->stub->pending_requests, 1);
result = dns_request_createvia(
zone->view->requestmgr, message, &zone->sourceaddr,
&zone->masteraddr, args->dscp, DNS_REQUESTOPT_TCP,
args->tsig_key, args->timeout * 3, args->timeout, 0, zone->task,
stub_glue_response_cb, request, &request->request);
if (result != ISC_R_SUCCESS) {
INSIST(atomic_fetch_sub_release(&args->stub->pending_requests,
1) > 1);
zone_debuglog(zone, "stub_send_query", 1,
"dns_request_createvia() failed: %s",
dns_result_totext(result));
goto fail;
}
dns_message_detach(&message);
return (ISC_R_SUCCESS);
fail:
dns_name_free(&request->name, zone->mctx);
isc_mem_put(zone->mctx, request, sizeof(*request));
if (message != NULL) {
dns_message_detach(&message);
}
return (result);
}
static inline isc_result_t
save_nsrrset(dns_message_t *message, dns_name_t *name, dns_db_t *db,
save_nsrrset(dns_message_t *message, dns_name_t *name,
struct stub_cb_args *cb_args, dns_db_t *db,
dns_dbversion_t *version) {
dns_rdataset_t *nsrdataset = NULL;
dns_rdataset_t *rdataset = NULL;
@ -12501,6 +12904,14 @@ save_nsrrset(dns_message_t *message, dns_name_t *name, dns_db_t *db,
dns_rdata_ns_t ns;
isc_result_t result;
dns_rdata_t rdata = DNS_RDATA_INIT;
bool has_glue = false;
dns_name_t *ns_name;
/*
* List of NS entries in answer, keep names that will be used
* to resolve missing A/AAAA glue for each entry.
*/
dns_namelist_t ns_list;
ISC_LIST_INIT(ns_list);
/*
* Extract NS RRset from message.
@ -12509,7 +12920,7 @@ save_nsrrset(dns_message_t *message, dns_name_t *name, dns_db_t *db,
dns_rdatatype_ns, dns_rdatatype_none,
NULL, &nsrdataset);
if (result != ISC_R_SUCCESS) {
goto fail;
goto done;
}
/*
@ -12517,12 +12928,12 @@ save_nsrrset(dns_message_t *message, dns_name_t *name, dns_db_t *db,
*/
result = dns_db_findnode(db, name, true, &node);
if (result != ISC_R_SUCCESS) {
goto fail;
goto done;
}
result = dns_db_addrdataset(db, node, version, 0, nsrdataset, 0, NULL);
dns_db_detachnode(db, &node);
if (result != ISC_R_SUCCESS) {
goto fail;
goto done;
}
/*
* Add glue rdatasets.
@ -12534,6 +12945,7 @@ save_nsrrset(dns_message_t *message, dns_name_t *name, dns_db_t *db,
result = dns_rdata_tostruct(&rdata, &ns, NULL);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
dns_rdata_reset(&rdata);
if (!dns_name_issubdomain(&ns.name, name)) {
continue;
}
@ -12543,41 +12955,92 @@ save_nsrrset(dns_message_t *message, dns_name_t *name, dns_db_t *db,
dns_rdatatype_none, NULL,
&rdataset);
if (result == ISC_R_SUCCESS) {
has_glue = true;
result = dns_db_findnode(db, &ns.name, true, &node);
if (result != ISC_R_SUCCESS) {
goto fail;
goto done;
}
result = dns_db_addrdataset(db, node, version, 0,
rdataset, 0, NULL);
dns_db_detachnode(db, &node);
if (result != ISC_R_SUCCESS) {
goto fail;
goto done;
}
}
rdataset = NULL;
result = dns_message_findname(
message, DNS_SECTION_ADDITIONAL, &ns.name,
dns_rdatatype_a, dns_rdatatype_none, NULL, &rdataset);
if (result == ISC_R_SUCCESS) {
has_glue = true;
result = dns_db_findnode(db, &ns.name, true, &node);
if (result != ISC_R_SUCCESS) {
goto fail;
goto done;
}
result = dns_db_addrdataset(db, node, version, 0,
rdataset, 0, NULL);
dns_db_detachnode(db, &node);
if (result != ISC_R_SUCCESS) {
goto fail;
goto done;
}
}
/*
* If no glue is found so far, we add the name to the list to
* resolve the A/AAAA glue later. If any glue is found in any
* iteration step, this list will be discarded and only the glue
* provided in this message will be used.
*/
if (!has_glue && dns_name_issubdomain(&ns.name, name)) {
dns_name_t *tmp_name;
tmp_name = isc_mem_get(cb_args->stub->mctx,
sizeof(*tmp_name));
dns_name_init(tmp_name, NULL);
dns_name_dup(&ns.name, cb_args->stub->mctx, tmp_name);
ISC_LIST_APPEND(ns_list, tmp_name, link);
}
}
if (result != ISC_R_NOMORE) {
goto done;
}
/*
* If no glue records were found, we attempt to resolve A/AAAA
* for each NS entry found in the answer.
*/
if (!has_glue) {
for (ns_name = ISC_LIST_HEAD(ns_list); ns_name != NULL;
ns_name = ISC_LIST_NEXT(ns_name, link))
{
/*
* Resolve NS IPv4 address/A.
*/
result = stub_request_nameserver_address(cb_args, true,
ns_name);
if (result != ISC_R_SUCCESS) {
goto done;
}
/*
* Resolve NS IPv6 address/AAAA.
*/
result = stub_request_nameserver_address(cb_args, false,
ns_name);
if (result != ISC_R_SUCCESS) {
goto done;
}
}
}
if (result != ISC_R_NOMORE) {
goto fail;
result = ISC_R_SUCCESS;
done:
while ((ns_name = ISC_LIST_HEAD(ns_list)) != NULL) {
ISC_LIST_UNLINK(ns_list, ns_name, link);
dns_name_free(ns_name, cb_args->stub->mctx);
isc_mem_put(cb_args->stub->mctx, ns_name, sizeof(*ns_name));
}
return (ISC_R_SUCCESS);
fail:
return (result);
}
@ -12590,14 +13053,15 @@ stub_callback(isc_task_t *task, isc_event_t *event) {
dns_zone_t *zone = NULL;
char master[ISC_SOCKADDR_FORMATSIZE];
char source[ISC_SOCKADDR_FORMATSIZE];
uint32_t nscnt, cnamecnt, refresh, retry, expire;
uint32_t nscnt, cnamecnt;
isc_result_t result;
isc_time_t now;
bool exiting = false;
isc_interval_t i;
unsigned int j, soacount;
unsigned int j;
struct stub_cb_args *cb_args;
stub = revent->ev_arg;
cb_args = revent->ev_arg;
stub = cb_args->stub;
INSIST(DNS_STUB_VALID(stub));
UNUSED(task);
@ -12725,10 +13189,13 @@ stub_callback(isc_task_t *task, isc_event_t *event) {
goto next_master;
}
atomic_fetch_add(&stub->pending_requests, 1);
/*
* Save answer.
*/
result = save_nsrrset(msg, &zone->origin, stub->db, stub->version);
result = save_nsrrset(msg, &zone->origin, cb_args, stub->db,
stub->version);
if (result != ISC_R_SUCCESS) {
dns_zone_log(zone, ISC_LOG_INFO,
"refreshing stub: unable to save NS records "
@ -12737,45 +13204,25 @@ stub_callback(isc_task_t *task, isc_event_t *event) {
goto next_master;
}
/*
* Tidy up.
*/
dns_db_closeversion(stub->db, &stub->version, true);
ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_write);
if (zone->db == NULL) {
zone_attachdb(zone, stub->db);
}
result = zone_get_from_db(zone, zone->db, NULL, &soacount, NULL,
&refresh, &retry, &expire, NULL, NULL);
if (result == ISC_R_SUCCESS && soacount > 0U) {
zone->refresh = RANGE(refresh, zone->minrefresh,
zone->maxrefresh);
zone->retry = RANGE(retry, zone->minretry, zone->maxretry);
zone->expire = RANGE(expire, zone->refresh + zone->retry,
DNS_MAX_EXPIRE);
DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_HAVETIMERS);
}
ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_write);
dns_db_detach(&stub->db);
dns_message_detach(&msg);
isc_event_free(&event);
dns_request_destroy(&zone->request);
DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_REFRESH);
DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED);
DNS_ZONE_JITTER_ADD(&now, zone->refresh, &zone->refreshtime);
isc_interval_set(&i, zone->expire, 0);
DNS_ZONE_TIME_ADD(&now, zone->expire, &zone->expiretime);
if (zone->masterfile != NULL) {
zone_needdump(zone, 0);
/*
* Check to see if there are no outstanding requests and
* finish off if that is so.
*/
if (atomic_fetch_sub(&stub->pending_requests, 1) == 1) {
isc_mem_put(zone->mctx, cb_args, sizeof(*cb_args));
stub_finish_zone_update(stub, now);
goto free_stub;
}
zone_settimer(zone, &now);
goto free_stub;
UNLOCK_ZONE(zone);
return;
next_master:
isc_mem_put(zone->mctx, cb_args, sizeof(*cb_args));
if (stub->version != NULL) {
dns_db_closeversion(stub->db, &stub->version, false);
}
@ -12836,6 +13283,7 @@ next_master:
goto free_stub;
same_master:
isc_mem_put(zone->mctx, cb_args, sizeof(*cb_args));
if (msg != NULL) {
dns_message_detach(&msg);
}
@ -13416,84 +13864,6 @@ queue_soa_query(dns_zone_t *zone) {
}
}
static inline isc_result_t
create_query(dns_zone_t *zone, dns_rdatatype_t rdtype,
dns_message_t **messagep) {
dns_message_t *message = NULL;
dns_name_t *qname = NULL;
dns_rdataset_t *qrdataset = NULL;
isc_result_t result;
dns_message_create(zone->mctx, DNS_MESSAGE_INTENTRENDER, &message);
message->opcode = dns_opcode_query;
message->rdclass = zone->rdclass;
result = dns_message_gettempname(message, &qname);
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
result = dns_message_gettemprdataset(message, &qrdataset);
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
/*
* Make question.
*/
dns_name_init(qname, NULL);
dns_name_clone(&zone->origin, qname);
dns_rdataset_makequestion(qrdataset, zone->rdclass, rdtype);
ISC_LIST_APPEND(qname->list, qrdataset, link);
dns_message_addname(message, qname, DNS_SECTION_QUESTION);
*messagep = message;
return (ISC_R_SUCCESS);
cleanup:
if (qname != NULL) {
dns_message_puttempname(message, &qname);
}
if (qrdataset != NULL) {
dns_message_puttemprdataset(message, &qrdataset);
}
dns_message_detach(&message);
return (result);
}
static isc_result_t
add_opt(dns_message_t *message, uint16_t udpsize, bool reqnsid,
bool reqexpire) {
isc_result_t result;
dns_rdataset_t *rdataset = NULL;
dns_ednsopt_t ednsopts[DNS_EDNSOPTIONS];
int count = 0;
/* Set EDNS options if applicable */
if (reqnsid) {
INSIST(count < DNS_EDNSOPTIONS);
ednsopts[count].code = DNS_OPT_NSID;
ednsopts[count].length = 0;
ednsopts[count].value = NULL;
count++;
}
if (reqexpire) {
INSIST(count < DNS_EDNSOPTIONS);
ednsopts[count].code = DNS_OPT_EXPIRE;
ednsopts[count].length = 0;
ednsopts[count].value = NULL;
count++;
}
result = dns_message_buildopt(message, &rdataset, 0, udpsize, 0,
ednsopts, count);
if (result != ISC_R_SUCCESS) {
return (result);
}
return (dns_message_setopt(message, rdataset));
}
static void
soa_query(isc_task_t *task, isc_event_t *event) {
const char me[] = "soa_query";
@ -13528,7 +13898,7 @@ soa_query(isc_task_t *task, isc_event_t *event) {
}
again:
result = create_query(zone, dns_rdatatype_soa, &message);
result = create_query(zone, dns_rdatatype_soa, &zone->origin, &message);
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
@ -13729,6 +14099,7 @@ ns_query(dns_zone_t *zone, dns_rdataset_t *soardataset, dns_stub_t *stub) {
bool reqnsid;
uint16_t udpsize = SEND_BUFFER_SIZE;
isc_dscp_t dscp = -1;
struct stub_cb_args *cb_args;
REQUIRE(DNS_ZONE_VALID(zone));
REQUIRE(LOCKED_ZONE(zone));
@ -13745,6 +14116,7 @@ ns_query(dns_zone_t *zone, dns_rdataset_t *soardataset, dns_stub_t *stub) {
stub->zone = NULL;
stub->db = NULL;
stub->version = NULL;
atomic_init(&stub->pending_requests, 0);
/*
* Attach so that the zone won't disappear from under us.
@ -13815,7 +14187,7 @@ ns_query(dns_zone_t *zone, dns_rdataset_t *soardataset, dns_stub_t *stub) {
/*
* XXX Optimisation: Create message when zone is setup and reuse.
*/
result = create_query(zone, dns_rdatatype_ns, &message);
result = create_query(zone, dns_rdatatype_ns, &zone->origin, &message);
INSIST(result == ISC_R_SUCCESS);
INSIST(zone->masterscnt > 0);
@ -13920,10 +14292,21 @@ ns_query(dns_zone_t *zone, dns_rdataset_t *soardataset, dns_stub_t *stub) {
if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DIALREFRESH)) {
timeout = 30;
}
/* Save request parameters so we can reuse them later on
for resolving missing glue A/AAAA records. */
cb_args = isc_mem_get(zone->mctx, sizeof(*cb_args));
cb_args->stub = stub;
cb_args->tsig_key = key;
cb_args->dscp = dscp;
cb_args->udpsize = udpsize;
cb_args->timeout = timeout;
cb_args->reqnsid = reqnsid;
result = dns_request_createvia(
zone->view->requestmgr, message, &zone->sourceaddr,
&zone->masteraddr, dscp, DNS_REQUESTOPT_TCP, key, timeout * 3,
timeout, 0, zone->task, stub_callback, stub, &zone->request);
timeout, 0, zone->task, stub_callback, cb_args, &zone->request);
if (result != ISC_R_SUCCESS) {
zone_debuglog(zone, me, 1, "dns_request_createvia() failed: %s",
dns_result_totext(result));

View file

@ -1683,7 +1683,9 @@ query_additional_cb(void *arg, const dns_name_t *name, dns_rdatatype_t qtype) {
* If we want only minimal responses and are here, then it must
* be for glue.
*/
if (qctx->view->minimalresponses == dns_minimal_yes) {
if (qctx->view->minimalresponses == dns_minimal_yes &&
client->query.qtype != dns_rdatatype_ns)
{
goto try_glue;
}
@ -11287,6 +11289,12 @@ ns_query_start(ns_client_t *client, isc_nmhandle_t *handle) {
{
client->query.attributes |= (NS_QUERYATTR_NOAUTHORITY |
NS_QUERYATTR_NOADDITIONAL);
} else if (qtype == dns_rdatatype_ns) {
/*
* Always turn on additional records for NS queries.
*/
client->query.attributes &= ~(NS_QUERYATTR_NOAUTHORITY |
NS_QUERYATTR_NOADDITIONAL);
}
/*