BIND 9.19.21

-----BEGIN SSH SIGNATURE-----
 U1NIU0lHAAAAAQAAARcAAAAHc3NoLXJzYQAAAAMBAAEAAAEBANamVSTMToLcHCXRu1f52e
 tTJWV3T1GSVrPYXwAGe6EVC7m9CTl06FZ9ZG/ymn1S1++dk4ByVZXf6dODe2Mu0RuqGmyf
 MUEMKXVdj3cEQhgRaMjBXvIZoYAsQlbHO2BEttomq8PhrpLRizDBq4Bv2aThM0XN2QqSGS
 ozwYMcPiGUoMVNcVrC4ZQ+Cptb5C4liqAcpRqrSo8l1vcNg5b1Hk6r7NFPdx542gsGMLae
 wZrnKn3LWz3ZXTGeK2cRmBxm/bydiVSCsc9XjB+tWtIGUpQsfaXqZ7Hs6t+1f1vsnu88oJ
 oi1dRBo3YNRl49UiCukXWayQrPJa8wwxURS9W28JMAAAADZ2l0AAAAAAAAAAZzaGE1MTIA
 AAEUAAAADHJzYS1zaGEyLTUxMgAAAQBSREyaosd+mY8kovqAvGYR8pOui/7gOi6pBprPGw
 RlOB5z6YOx5FOjbVL/YvBhKk2gbox++o8jCMEmdNNbWeO3U3uBvxCa+8QGARbuMV6vdoR4
 qjnOgOfryXyaRw7PQX0ZH0gPw1B1036y5bnW7WPkqrTvGgxW34O1q6j0EumE0vh90E24/l
 PAWKDCTqDR/+slGDuWgtPcCZuClljw1Mh0dAliKkGhp0l80qMQSr6O/p66A44UxzKwtnnt
 lagtO0j4nZ+BxC/hyaFc/FlCzeoc48qFQRIt0ZjYKU+XK0CUr2RTpYFdi/n7y3BNd7bDkD
 nIkEDddn/lXP5rkAdkmDCa
 -----END SSH SIGNATURE-----
gpgsig -----BEGIN SSH SIGNATURE-----
 U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAg25GGAuUyFX1gxo7QocNm8V6J/8
 frHSduYX7Aqk4iJLwAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5
 AAAAQEGqBHXwCtEJxRzHbTp6CfBNjqwIAjRD9G+HC4M7q77KBEBgc6dRf15ZRRgiWJCk5P
 iHMZkEMyWCnELMzhiTzgE=
 -----END SSH SIGNATURE-----

Merge tag 'v9.19.21'

BIND 9.19.21
This commit is contained in:
Michał Kępień 2024-02-14 13:24:56 +01:00
commit 8610799317
26 changed files with 1393 additions and 740 deletions

26
CHANGES
View file

@ -77,25 +77,37 @@
'host -C' commands when one of the name servers returns
SERVFAIL. [GL #4508]
--- 9.19.21 released ---
6323. [placeholder]
6322. [placeholder]
6322. [security] Specific DNS answers could cause a denial-of-service
condition due to DNS validation taking a long time.
(CVE-2023-50387) [GL #4424]
6321. [placeholder]
6321. [security] Change 6315 inadvertently introduced regressions that
could cause named to crash. [GL #4234]
6320. [placeholder]
6319. [placeholder]
--- 9.19.20 released ---
6319. [func] Limit isc_async_run() overhead for RBTDB tree pruning.
[GL #4383]
6318. [placeholder]
6317. [placeholder]
6317. [security] Restore DNS64 state when handling a serve-stale timeout.
(CVE-2023-5679) [GL #4334]
6316. [placeholder]
6316. [security] Specific queries could trigger an assertion check with
nxdomain-redirect enabled. (CVE-2023-5517) [GL #4281]
6315. [placeholder]
6315. [security] Speed up parsing of DNS messages with many different
names. (CVE-2023-4408) [GL #4234]
6314. [placeholder]
6314. [bug] Address race conditions in dns_tsigkey_find().
[GL #4182]
6313. [bug] When dnssec-policy is in effect the DNSKEY's TTLs in
the zone where not being updated to match the policy.

View file

@ -5458,6 +5458,21 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config,
INSIST(result == ISC_R_SUCCESS);
dns_resolver_setmaxqueries(view->resolver, cfg_obj_asuint32(obj));
obj = NULL;
result = named_config_get(maps, "max-validations-per-fetch", &obj);
if (result == ISC_R_SUCCESS) {
dns_resolver_setmaxvalidations(view->resolver,
cfg_obj_asuint32(obj));
}
obj = NULL;
result = named_config_get(maps, "max-validation-failures-per-fetch",
&obj);
if (result == ISC_R_SUCCESS) {
dns_resolver_setmaxvalidationfails(view->resolver,
cfg_obj_asuint32(obj));
}
obj = NULL;
result = named_config_get(maps, "fetches-per-zone", &obj);
INSIST(result == ISC_R_SUCCESS);

View file

@ -347,18 +347,18 @@ if changes_added_lines:
# MR.
release_notes_regex = re.compile(r"doc/(arm|notes)/notes-.*\.(rst|xml)")
release_notes_changed = list(filter(release_notes_regex.match, modified_files))
release_notes_changed = list(filter(release_notes_regex.match, affected_files))
release_notes_label_set = "Release Notes" in mr_labels
if not release_notes_changed:
if release_notes_label_set:
fail(
"This merge request has the *Release Notes* label set. "
"Add a release note or unset the *Release Notes* label."
"Update release notes or unset the *Release Notes* label."
)
elif "Customer" in mr_labels:
warn(
"This merge request has the *Customer* label set. "
"Add a release note unless the changes introduced are trivial."
"Update release notes unless the changes introduced are trivial."
)
if release_notes_changed and not release_notes_label_set:
fail(
@ -367,7 +367,9 @@ if release_notes_changed and not release_notes_label_set:
)
if release_notes_changed:
notes_added_lines = added_lines(target_branch, release_notes_changed)
modified_or_new_files = danger.git.modified_files + danger.git.created_files
release_notes_added = list(filter(release_notes_regex.match, modified_or_new_files))
notes_added_lines = added_lines(target_branch, release_notes_added)
identifiers_found = filter(relnotes_issue_or_mr_id_regex.search, notes_added_lines)
if notes_added_lines and not any(identifiers_found):
warn("No valid issue/MR identifiers found in added release notes.")

View file

@ -39,6 +39,8 @@ information about each release, and source code.
.. include:: ../notes/notes-known-issues.rst
.. include:: ../notes/notes-current.rst
.. include:: ../notes/notes-9.19.21.rst
.. include:: ../notes/notes-9.19.20.rst
.. include:: ../notes/notes-9.19.19.rst
.. include:: ../notes/notes-9.19.18.rst
.. include:: ../notes/notes-9.19.17.rst

View file

@ -3736,6 +3736,21 @@ system.
set to zero, :any:`max-clients-per-query` no longer applies and there is no
upper bound, other than that imposed by :any:`recursive-clients`.
.. namedconf:statement:: max-validations-per-fetch
:tags: server
:short: Set the maximum number of DNSSEC validations that can happen in single fetch
This is an **experimental** setting to set the maximum number of DNSSEC
validations that can happen in a single resolver fetch. The default is 16.
.. namedconf:statement:: max-validation-failures-per-fetch
:tags: server
:short: Set the maximum number of DNSSEC validation failures that can happen in single fetch
This is an **experimental** setting to set the maximum number of DNSSEC
validation failures that can happen in a single resolver fetch. The default
is 1.
.. namedconf:statement:: fetches-per-zone
:tags: server, query
:short: Sets the maximum number of simultaneous iterative queries allowed to any one domain before the server blocks new queries for data in or beneath that zone.

View file

@ -193,6 +193,8 @@ options {
max-transfer-time-in <integer>;
max-transfer-time-out <integer>;
max-udp-size <integer>;
max-validation-failures-per-fetch <integer>; // experimental
max-validations-per-fetch <integer>; // experimental
max-zone-ttl ( unlimited | <duration> ); // deprecated
memstatistics <boolean>;
memstatistics-file <quoted_string>;
@ -475,6 +477,8 @@ view <string> [ <class> ] {
max-transfer-time-in <integer>;
max-transfer-time-out <integer>;
max-udp-size <integer>;
max-validation-failures-per-fetch <integer>; // experimental
max-validations-per-fetch <integer>; // experimental
max-zone-ttl ( unlimited | <duration> ); // deprecated
message-compression <boolean>;
min-cache-ttl <duration>;

View file

@ -0,0 +1,19 @@
.. Copyright (C) Internet Systems Consortium, Inc. ("ISC")
..
.. SPDX-License-Identifier: MPL-2.0
..
.. 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 https://mozilla.org/MPL/2.0/.
..
.. See the COPYRIGHT file distributed with this work for additional
.. information regarding copyright ownership.
Notes for BIND 9.19.20
----------------------
.. note::
The BIND 9.19.20 release was withdrawn after the discovery of a
regression in a security fix in it during pre-release testing. ISC
would like to acknowledge the assistance of Curtis Tuplin of SaskTel.

View file

@ -0,0 +1,70 @@
.. Copyright (C) Internet Systems Consortium, Inc. ("ISC")
..
.. SPDX-License-Identifier: MPL-2.0
..
.. 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 https://mozilla.org/MPL/2.0/.
..
.. See the COPYRIGHT file distributed with this work for additional
.. information regarding copyright ownership.
Notes for BIND 9.19.21
----------------------
Security Fixes
~~~~~~~~~~~~~~
- Validating DNS messages containing a lot of DNSSEC signatures could
cause excessive CPU load, leading to a denial-of-service condition.
This has been fixed. :cve:`2023-50387`
ISC would like to thank Elias Heftrig, Haya Schulmann, Niklas Vogel,
and Michael Waidner from the German National Research Center for
Applied Cybersecurity ATHENE for bringing this vulnerability to our
attention. :gl:`#4424`
- Parsing DNS messages with many different names could cause excessive
CPU load. This has been fixed. :cve:`2023-4408`
ISC would like to thank Shoham Danino from Reichman University, Anat
Bremler-Barr from Tel-Aviv University, Yehuda Afek from Tel-Aviv
University, and Yuval Shavitt from Tel-Aviv University for bringing
this vulnerability to our attention. :gl:`#4234`
- Specific queries could cause :iscman:`named` to crash with an
assertion failure when :any:`nxdomain-redirect` was enabled. This has
been fixed. :cve:`2023-5517` :gl:`#4281`
- A bad interaction between DNS64 and serve-stale could cause
:iscman:`named` to crash with an assertion failure, when both of these
features were enabled. This has been fixed. :cve:`2023-5679`
:gl:`#4334`
Feature Changes
~~~~~~~~~~~~~~~
- :iscman:`named-compilezone` no longer performs zone integrity checks
by default; this allows faster conversion of a zone file from one
format to another. :gl:`#4364`
Zone checks can be performed by running :iscman:`named-checkzone`
separately, or the previous default behavior can be restored by using:
::
named-compilezone -i full -k fail -n fail -r warn -m warn -M warn -S warn -T warn -W warn -C check-svcb:fail
Bug Fixes
~~~~~~~~~
- The counters exported via the statistics channel were changed back to
64-bit signed values; they were being inadvertently truncated to
unsigned 32-bit values since BIND 9.15.0. :gl:`#4467`
Known Issues
~~~~~~~~~~~~
- There are no new known issues with this release. See :ref:`above
<relnotes_known_issues>` for a list of all known issues affecting this
BIND 9 branch.

View file

@ -145,12 +145,4 @@ typedef struct {
bool exiting;
} db_nodelock_t;
/*%
* Prune context
*/
typedef struct {
dns_db_t *db;
dns_dbnode_t *node;
} db_prune_t;
ISC_LANG_ENDDECLS

View file

@ -164,7 +164,8 @@ computeid(dst_key_t *key);
static isc_result_t
frombuffer(const dns_name_t *name, unsigned int alg, unsigned int flags,
unsigned int protocol, dns_rdataclass_t rdclass,
isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp);
isc_buffer_t *source, isc_mem_t *mctx, bool no_rdata,
dst_key_t **keyp);
static isc_result_t
algorithm_status(unsigned int alg);
@ -754,6 +755,13 @@ dst_key_todns(const dst_key_t *key, isc_buffer_t *target) {
isc_result_t
dst_key_fromdns(const dns_name_t *name, dns_rdataclass_t rdclass,
isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp) {
return (dst_key_fromdns_ex(name, rdclass, source, mctx, false, keyp));
}
isc_result_t
dst_key_fromdns_ex(const dns_name_t *name, dns_rdataclass_t rdclass,
isc_buffer_t *source, isc_mem_t *mctx, bool no_rdata,
dst_key_t **keyp) {
uint8_t alg, proto;
uint32_t flags, extflags;
dst_key_t *key = NULL;
@ -784,7 +792,7 @@ dst_key_fromdns(const dns_name_t *name, dns_rdataclass_t rdclass,
}
result = frombuffer(name, alg, flags, proto, rdclass, source, mctx,
&key);
no_rdata, &key);
if (result != ISC_R_SUCCESS) {
return (result);
}
@ -805,7 +813,7 @@ dst_key_frombuffer(const dns_name_t *name, unsigned int alg, unsigned int flags,
REQUIRE(dst_initialized);
result = frombuffer(name, alg, flags, protocol, rdclass, source, mctx,
&key);
false, &key);
if (result != ISC_R_SUCCESS) {
return (result);
}
@ -2313,7 +2321,8 @@ computeid(dst_key_t *key) {
static isc_result_t
frombuffer(const dns_name_t *name, unsigned int alg, unsigned int flags,
unsigned int protocol, dns_rdataclass_t rdclass,
isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp) {
isc_buffer_t *source, isc_mem_t *mctx, bool no_rdata,
dst_key_t **keyp) {
dst_key_t *key;
isc_result_t ret;
@ -2335,10 +2344,12 @@ frombuffer(const dns_name_t *name, unsigned int alg, unsigned int flags,
return (DST_R_UNSUPPORTEDALG);
}
ret = key->func->fromdns(key, source);
if (ret != ISC_R_SUCCESS) {
dst_key_free(&key);
return (ret);
if (!no_rdata) {
ret = key->func->fromdns(key, source);
if (ret != ISC_R_SUCCESS) {
dst_key_free(&key);
return (ret);
}
}
}

View file

@ -860,27 +860,6 @@ dns_message_findtype(const dns_name_t *name, dns_rdatatype_t type,
*\li #ISC_R_NOTFOUND -- the desired type does not exist.
*/
isc_result_t
dns_message_find(const dns_name_t *name, dns_rdataclass_t rdclass,
dns_rdatatype_t type, dns_rdatatype_t covers,
dns_rdataset_t **rdataset);
/*%<
* Search the name for the specified rdclass and type. If it is found,
* *rdataset is filled in with a pointer to that rdataset.
*
* Requires:
*\li if '**rdataset' is non-NULL, *rdataset needs to be NULL.
*
*\li 'type' be a valid type, and NOT dns_rdatatype_any.
*
*\li If 'type' is dns_rdatatype_rrsig, 'covers' must be a valid type.
* Otherwise it should be 0.
*
* Returns:
*\li #ISC_R_SUCCESS -- all is well.
*\li #ISC_R_NOTFOUND -- the desired type does not exist.
*/
void
dns_message_addname(dns_message_t *msg, dns_name_t *name,
dns_section_t section);

View file

@ -68,6 +68,7 @@
#include <stdio.h>
#include <isc/buffer.h>
#include <isc/hashmap.h>
#include <isc/lang.h>
#include <isc/magic.h>
#include <isc/region.h> /* Required for storage size of dns_label_t. */
@ -119,6 +120,7 @@ struct dns_name {
isc_buffer_t *buffer;
ISC_LINK(dns_name_t) link;
ISC_LIST(dns_rdataset_t) list;
isc_hashmap_t *hashmap;
};
#define DNS_NAME_MAGIC ISC_MAGIC('D', 'N', 'S', 'n')

View file

@ -112,6 +112,12 @@ struct dns_rbtnode {
*/
ISC_LINK(dns_rbtnode_t) deadlink;
/*%
* This linked list is used to store nodes from which tree pruning can
* be started.
*/
ISC_LINK(dns_rbtnode_t) prunelink;
/*@{*/
/*!
* These values are used in the RBT DB implementation. The appropriate

View file

@ -578,6 +578,14 @@ dns_resolver_printbadcache(dns_resolver_t *resolver, FILE *fp);
* \li resolver to be valid.
*/
void
dns_resolver_setmaxvalidations(dns_resolver_t *resolver, uint32_t max);
void
dns_resolver_setmaxvalidationfails(dns_resolver_t *resolver, uint32_t max);
/*%
* Set maximum numbers of validations and maximum validation failures per fetch.
*/
void
dns_resolver_setmaxdepth(dns_resolver_t *resolver, unsigned int maxdepth);
unsigned int

View file

@ -53,6 +53,7 @@
#include <isc/refcount.h>
#include <dns/fixedname.h>
#include <dns/rdata.h>
#include <dns/rdataset.h>
#include <dns/rdatastruct.h> /* for dns_rdata_rrsig_t */
#include <dns/types.h>
@ -144,6 +145,13 @@ struct dns_validator {
unsigned int authcount;
unsigned int authfail;
isc_stdtime_t start;
bool digest_sha1;
bool supported_algorithm;
dns_rdata_t rdata;
bool resume;
uint32_t *nvalidations;
uint32_t *nfails;
};
/*%
@ -161,6 +169,7 @@ dns_validator_create(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type,
dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset,
dns_message_t *message, unsigned int options,
isc_loop_t *loop, isc_job_cb cb, void *arg,
uint32_t *nvalidations, uint32_t *nfails,
dns_validator_t **validatorp);
/*%<
* Start a DNSSEC validation.

View file

@ -482,6 +482,10 @@ dst_key_tofile(const dst_key_t *key, int type, const char *directory);
*/
isc_result_t
dst_key_fromdns_ex(const dns_name_t *name, dns_rdataclass_t rdclass,
isc_buffer_t *source, isc_mem_t *mctx, bool no_rdata,
dst_key_t **keyp);
isc_result_t
dst_key_fromdns(const dns_name_t *name, dns_rdataclass_t rdclass,
isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp);
/*%<

View file

@ -22,6 +22,8 @@
#include <stdbool.h>
#include <isc/buffer.h>
#include <isc/hash.h>
#include <isc/hashmap.h>
#include <isc/mem.h>
#include <isc/result.h>
#include <isc/string.h>
@ -779,6 +781,11 @@ ISC_REFCOUNT_TRACE_IMPL(dns_message, dns__message_destroy);
ISC_REFCOUNT_IMPL(dns_message, dns__message_destroy);
#endif
static bool
name_match(void *node, const void *key) {
return (dns_name_equal(node, key));
}
static isc_result_t
findname(dns_name_t **foundname, const dns_name_t *target,
dns_namelist_t *section) {
@ -796,26 +803,25 @@ findname(dns_name_t **foundname, const dns_name_t *target,
return (ISC_R_NOTFOUND);
}
isc_result_t
dns_message_find(const dns_name_t *name, dns_rdataclass_t rdclass,
dns_rdatatype_t type, dns_rdatatype_t covers,
dns_rdataset_t **rdatasetp) {
dns_rdataset_t *rds = NULL;
static uint32_t
rds_hash(dns_rdataset_t *rds) {
isc_hash32_t state;
REQUIRE(name != NULL);
REQUIRE(rdatasetp == NULL || *rdatasetp == NULL);
isc_hash32_init(&state);
isc_hash32_hash(&state, &rds->rdclass, sizeof(rds->rdclass), true);
isc_hash32_hash(&state, &rds->type, sizeof(rds->type), true);
isc_hash32_hash(&state, &rds->covers, sizeof(rds->covers), true);
ISC_LIST_FOREACH_REV (name->list, rds, link) {
if (rds->rdclass == rdclass && rds->type == type &&
rds->covers == covers)
{
SET_IF_NOT_NULL(rdatasetp, rds);
return (isc_hash32_finalize(&state));
}
return (ISC_R_SUCCESS);
}
}
static bool
rds_match(void *node, const void *key0) {
const dns_rdataset_t *rds = node;
const dns_rdataset_t *key = key0;
return (ISC_R_NOTFOUND);
return (rds->rdclass == key->rdclass && rds->type == key->type &&
rds->covers == key->covers);
}
isc_result_t
@ -939,22 +945,38 @@ getrdata(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t dctx,
} \
} while (0)
static void
cleanup_name_hashmaps(dns_namelist_t *section) {
dns_name_t *name = NULL;
ISC_LIST_FOREACH (*section, name, link) {
if (name->hashmap != NULL) {
isc_hashmap_destroy(&name->hashmap);
}
}
}
static isc_result_t
getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t dctx,
unsigned int options) {
isc_region_t r;
unsigned int count;
dns_name_t *name = NULL;
dns_name_t *name2 = NULL;
dns_name_t *found_name = NULL;
dns_rdataset_t *rdataset = NULL;
dns_rdatalist_t *rdatalist = NULL;
isc_result_t result;
isc_result_t result = ISC_R_SUCCESS;
dns_rdatatype_t rdtype;
dns_rdataclass_t rdclass;
dns_namelist_t *section = &msg->sections[DNS_SECTION_QUESTION];
bool best_effort = ((options & DNS_MESSAGEPARSE_BESTEFFORT) != 0);
bool seen_problem = false;
bool free_name = false;
bool free_hashmaps = false;
isc_hashmap_t *name_map = NULL;
if (msg->counts[DNS_SECTION_QUESTION] > 1) {
isc_hashmap_create(msg->mctx, 1, &name_map);
}
for (count = 0; count < msg->counts[DNS_SECTION_QUESTION]; count++) {
name = NULL;
@ -972,13 +994,21 @@ getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t dctx,
goto cleanup;
}
/* If there is only one QNAME, skip the duplicity checks */
if (name_map == NULL) {
result = ISC_R_SUCCESS;
goto skip_name_check;
}
/*
* Run through the section, looking to see if this name
* is already there. If it is found, put back the allocated
* name since we no longer need it, and set our name pointer
* to point to the name we found.
*/
result = findname(&name2, name, section);
result = isc_hashmap_add(name_map, dns_name_hash(name),
name_match, name, name,
(void **)&found_name);
/*
* If it is the first name in the section, accept it.
@ -990,19 +1020,25 @@ getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t dctx,
* this should be legal or not. In either case we no longer
* need this name pointer.
*/
if (result != ISC_R_SUCCESS) {
skip_name_check:
switch (result) {
case ISC_R_SUCCESS:
if (!ISC_LIST_EMPTY(*section)) {
DO_ERROR(DNS_R_FORMERR);
}
ISC_LIST_APPEND(*section, name, link);
free_name = false;
} else {
break;
case ISC_R_EXISTS:
dns_message_puttempname(msg, &name);
name = name2;
name2 = NULL;
free_name = false;
name = found_name;
found_name = NULL;
break;
default:
UNREACHABLE();
}
free_name = false;
/*
* Get type and class.
*/
@ -1032,53 +1068,86 @@ getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t dctx,
msg->tkey = 1;
}
/*
* Can't ask the same question twice.
*/
result = dns_message_find(name, rdclass, rdtype, 0, NULL);
if (result == ISC_R_SUCCESS) {
DO_ERROR(DNS_R_FORMERR);
}
/*
* Allocate a new rdatalist.
*/
rdatalist = newrdatalist(msg);
if (rdatalist == NULL) {
result = ISC_R_NOMEMORY;
goto cleanup;
}
rdataset = isc_mempool_get(msg->rdspool);
rdatalist->type = rdtype;
rdatalist->rdclass = rdclass;
rdatalist->covers = 0;
/*
* Convert rdatalist to rdataset, and attach the latter to
* the name.
*/
rdatalist->type = rdtype;
rdatalist->rdclass = rdclass;
dns_rdataset_init(rdataset);
dns_message_gettemprdataset(msg, &rdataset);
dns_rdatalist_tordataset(rdatalist, rdataset);
rdataset->attributes |= DNS_RDATASETATTR_QUESTION;
/*
* Skip the duplicity check for first rdataset
*/
if (ISC_LIST_EMPTY(name->list)) {
result = ISC_R_SUCCESS;
goto skip_rds_check;
}
/*
* Can't ask the same question twice.
*/
if (name->hashmap == NULL) {
isc_hashmap_create(msg->mctx, 1, &name->hashmap);
free_hashmaps = true;
INSIST(ISC_LIST_HEAD(name->list) ==
ISC_LIST_TAIL(name->list));
dns_rdataset_t *old_rdataset =
ISC_LIST_HEAD(name->list);
result = isc_hashmap_add(
name->hashmap, rds_hash(old_rdataset),
rds_match, old_rdataset, old_rdataset, NULL);
INSIST(result == ISC_R_SUCCESS);
}
result = isc_hashmap_add(name->hashmap, rds_hash(rdataset),
rds_match, rdataset, rdataset, NULL);
if (result == ISC_R_EXISTS) {
DO_ERROR(DNS_R_FORMERR);
}
skip_rds_check:
ISC_LIST_APPEND(name->list, rdataset, link);
rdataset = NULL;
}
if (seen_problem) {
return (DNS_R_RECOVERABLE);
result = DNS_R_RECOVERABLE;
}
return (ISC_R_SUCCESS);
cleanup:
if (rdataset != NULL) {
if (dns_rdataset_isassociated(rdataset)) {
dns_rdataset_disassociate(rdataset);
}
dns_message_puttemprdataset(msg, &rdataset);
}
if (free_name) {
dns_message_puttempname(msg, &name);
}
if (free_hashmaps) {
cleanup_name_hashmaps(section);
}
if (name_map != NULL) {
isc_hashmap_destroy(&name_map);
}
return (result);
}
@ -1105,7 +1174,6 @@ auth_signed(dns_namelist_t *section) {
ISC_LIST_FOREACH (*section, name, link) {
int auth_dnssec = 0, auth_rrsig = 0;
dns_rdataset_t *rds = NULL;
ISC_LIST_FOREACH (name->list, rds, link) {
switch (rds->type) {
case dns_rdatatype_ds:
@ -1152,19 +1220,26 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t dctx,
isc_region_t r;
unsigned int count, rdatalen;
dns_name_t *name = NULL;
dns_name_t *name2 = NULL;
dns_name_t *found_name = NULL;
dns_rdataset_t *rdataset = NULL;
dns_rdataset_t *found_rdataset = NULL;
dns_rdatalist_t *rdatalist = NULL;
isc_result_t result;
isc_result_t result = ISC_R_SUCCESS;
dns_rdatatype_t rdtype, covers;
dns_rdataclass_t rdclass;
dns_rdata_t *rdata = NULL;
dns_ttl_t ttl;
dns_namelist_t *section = &msg->sections[sectionid];
bool free_name = false, free_rdataset = false, seen_problem = false;
bool free_name = false, seen_problem = false;
bool free_hashmaps = false;
bool preserve_order = ((options & DNS_MESSAGEPARSE_PRESERVEORDER) != 0);
bool best_effort = ((options & DNS_MESSAGEPARSE_BESTEFFORT) != 0);
bool isedns, issigzero, istsig;
isc_hashmap_t *name_map = NULL;
if (msg->counts[sectionid] > 1) {
isc_hashmap_create(msg->mctx, 1, &name_map);
}
for (count = 0; count < msg->counts[sectionid]; count++) {
int recstart = source->current;
@ -1172,10 +1247,10 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t dctx,
skip_name_search = false;
skip_type_search = false;
free_rdataset = false;
isedns = false;
issigzero = false;
istsig = false;
found_rdataset = NULL;
name = NULL;
dns_message_gettempname(msg, &name);
@ -1212,8 +1287,8 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t dctx,
if (msg->rdclass_set == 0 &&
rdtype != dns_rdatatype_opt && /* class is UDP SIZE */
rdtype != dns_rdatatype_tsig && /* class is ANY */
rdtype != dns_rdatatype_tkey)
{ /* class is undefined */
rdtype != dns_rdatatype_tkey) /* class is undefined */
{
msg->rdclass = rdclass;
msg->rdclass_set = 1;
}
@ -1320,10 +1395,6 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t dctx,
* Then put the meta-class back into the finished rdata.
*/
rdata = newrdata(msg);
if (rdata == NULL) {
result = ISC_R_NOMEMORY;
goto cleanup;
}
if (msg->opcode == dns_opcode_update &&
update(sectionid, rdclass))
{
@ -1412,34 +1483,63 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t dctx,
free_name = false;
}
} else {
if (name_map == NULL) {
result = ISC_R_SUCCESS;
goto skip_name_check;
}
/*
* Run through the section, looking to see if this name
* is already there. If it is found, put back the
* allocated name since we no longer need it, and set
* our name pointer to point to the name we found.
*/
result = findname(&name2, name, section);
result = isc_hashmap_add(name_map, dns_name_hash(name),
name_match, name, name,
(void **)&found_name);
/*
* If it is a new name, append to the section.
*/
if (result == ISC_R_SUCCESS) {
dns_message_puttempname(msg, &name);
name = name2;
} else {
skip_name_check:
switch (result) {
case ISC_R_SUCCESS:
ISC_LIST_APPEND(*section, name, link);
break;
case ISC_R_EXISTS:
dns_message_puttempname(msg, &name);
name = found_name;
found_name = NULL;
break;
default:
UNREACHABLE();
}
free_name = false;
}
rdatalist = newrdatalist(msg);
rdatalist->type = rdtype;
rdatalist->covers = covers;
rdatalist->rdclass = rdclass;
rdatalist->ttl = ttl;
dns_message_gettemprdataset(msg, &rdataset);
dns_rdatalist_tordataset(rdatalist, rdataset);
dns_rdataset_setownercase(rdataset, name);
rdatalist = NULL;
/*
* Search name for the particular type and class.
* Skip this stage if in update mode or this is a meta-type.
*/
if (preserve_order || msg->opcode == dns_opcode_update ||
skip_type_search)
if (isedns || istsig || issigzero) {
/* Skip adding the rdataset to the tables */
} else if (preserve_order || msg->opcode == dns_opcode_update ||
skip_type_search)
{
result = ISC_R_NOTFOUND;
result = ISC_R_SUCCESS;
ISC_LIST_APPEND(name->list, rdataset, link);
} else {
/*
* If this is a type that can only occur in
@ -1449,57 +1549,74 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t dctx,
DO_ERROR(DNS_R_FORMERR);
}
rdataset = NULL;
result = dns_message_find(name, rdclass, rdtype, covers,
&rdataset);
}
if (ISC_LIST_EMPTY(name->list)) {
result = ISC_R_SUCCESS;
goto skip_rds_check;
}
if (name->hashmap == NULL) {
isc_hashmap_create(msg->mctx, 1,
&name->hashmap);
free_hashmaps = true;
INSIST(ISC_LIST_HEAD(name->list) ==
ISC_LIST_TAIL(name->list));
dns_rdataset_t *old_rdataset =
ISC_LIST_HEAD(name->list);
result = isc_hashmap_add(
name->hashmap, rds_hash(old_rdataset),
rds_match, old_rdataset, old_rdataset,
NULL);
INSIST(result == ISC_R_SUCCESS);
}
result = isc_hashmap_add(
name->hashmap, rds_hash(rdataset), rds_match,
rdataset, rdataset, (void **)&found_rdataset);
/*
* If we found an rdataset that matches, we need to
* append this rdata to that set. If we did not, we
* need to create a new rdatalist, store the important
* bits there, convert it to an rdataset, and link the
* latter to the name. Yuck. When appending, make
* certain that the type isn't a singleton type, such as
* SOA or CNAME.
*
* Note that this check will be bypassed when preserving
* order, the opcode is an update, or the type search is
* skipped.
*/
skip_rds_check:
switch (result) {
case ISC_R_EXISTS:
/* Free the rdataset we used as the key */
dns__message_putassociatedrdataset(msg,
&rdataset);
result = ISC_R_SUCCESS;
rdataset = found_rdataset;
if (!dns_rdatatype_issingleton(rdtype)) {
break;
}
/*
* If we found an rdataset that matches, we need to
* append this rdata to that set. If we did not, we need
* to create a new rdatalist, store the important bits there,
* convert it to an rdataset, and link the latter to the name.
* Yuck. When appending, make certain that the type isn't
* a singleton type, such as SOA or CNAME.
*
* Note that this check will be bypassed when preserving order,
* the opcode is an update, or the type search is skipped.
*/
if (result == ISC_R_SUCCESS) {
if (dns_rdatatype_issingleton(rdtype)) {
dns_rdata_t *first;
dns_rdatalist_fromrdataset(rdataset,
&rdatalist);
first = ISC_LIST_HEAD(rdatalist->rdata);
dns_rdata_t *first =
ISC_LIST_HEAD(rdatalist->rdata);
INSIST(first != NULL);
if (dns_rdata_compare(rdata, first) != 0) {
DO_ERROR(DNS_R_FORMERR);
}
}
}
if (result == ISC_R_NOTFOUND) {
rdataset = isc_mempool_get(msg->rdspool);
free_rdataset = true;
rdatalist = newrdatalist(msg);
if (rdatalist == NULL) {
result = ISC_R_NOMEMORY;
goto cleanup;
}
rdatalist->type = rdtype;
rdatalist->covers = covers;
rdatalist->rdclass = rdclass;
rdatalist->ttl = ttl;
dns_rdataset_init(rdataset);
dns_rdatalist_tordataset(rdatalist, rdataset);
dns_rdataset_setownercase(rdataset, name);
if (!isedns && !istsig && !issigzero) {
break;
case ISC_R_SUCCESS:
ISC_LIST_APPEND(name->list, rdataset, link);
free_rdataset = false;
break;
default:
UNREACHABLE();
}
}
@ -1534,8 +1651,6 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t dctx,
dns_rcode_t ercode;
msg->opt = rdataset;
rdataset = NULL;
free_rdataset = false;
ercode = (dns_rcode_t)((msg->opt->ttl &
DNS_MESSAGE_EDNSRCODE_MASK) >>
20);
@ -1546,8 +1661,6 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t dctx,
msg->sig0 = rdataset;
msg->sig0name = name;
msg->sigstart = recstart;
rdataset = NULL;
free_rdataset = false;
free_name = false;
} else if (istsig) {
msg->tsig = rdataset;
@ -1557,23 +1670,17 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t dctx,
* Windows doesn't like TSIG names to be compressed.
*/
msg->tsigname->attributes.nocompress = true;
rdataset = NULL;
free_rdataset = false;
free_name = false;
}
rdataset = NULL;
if (seen_problem) {
if (free_name) {
dns_message_puttempname(msg, &name);
}
if (free_rdataset) {
dns__message_putassociatedrdataset(msg,
&rdataset);
}
free_name = free_rdataset = false;
free_name = false;
}
INSIST(!free_name);
INSIST(!free_rdataset);
}
/*
@ -1591,16 +1698,23 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t dctx,
}
if (seen_problem) {
return (DNS_R_RECOVERABLE);
result = DNS_R_RECOVERABLE;
}
return (ISC_R_SUCCESS);
cleanup:
if (rdataset != NULL && rdataset != found_rdataset) {
dns__message_putassociatedrdataset(msg, &rdataset);
}
if (free_name) {
dns_message_puttempname(msg, &name);
}
if (free_rdataset) {
dns__message_putassociatedrdataset(msg, &rdataset);
if (free_hashmaps) {
cleanup_name_hashmaps(section);
}
if (name_map != NULL) {
isc_hashmap_destroy(&name_map);
}
return (result);
@ -1661,6 +1775,7 @@ dns_message_parse(dns_message_t *msg, isc_buffer_t *source,
dctx = DNS_DECOMPRESS_ALWAYS;
ret = getquestions(source, msg, dctx, options);
if (ret == ISC_R_UNEXPECTEDEND && ignore_tc) {
goto truncated;
}
@ -2349,15 +2464,13 @@ dns_message_renderreset(dns_message_t *msg) {
dns_message_puttempname(msg, &msg->tsigname);
}
if (msg->tsig != NULL) {
dns_rdataset_disassociate(msg->tsig);
dns_message_puttemprdataset(msg, &msg->tsig);
dns__message_putassociatedrdataset(msg, &msg->tsig);
}
if (msg->sig0name != NULL) {
dns_message_puttempname(msg, &msg->sig0name);
}
if (msg->sig0 != NULL) {
dns_rdataset_disassociate(msg->sig0);
dns_message_puttemprdataset(msg, &msg->sig0);
dns__message_putassociatedrdataset(msg, &msg->sig0);
}
}
@ -2406,7 +2519,7 @@ dns_message_findname(dns_message_t *msg, dns_section_t section,
const dns_name_t *target, dns_rdatatype_t type,
dns_rdatatype_t covers, dns_name_t **name,
dns_rdataset_t **rdataset) {
dns_name_t *foundname;
dns_name_t *foundname = NULL;
isc_result_t result;
/*
@ -2524,6 +2637,10 @@ dns_message_puttempname(dns_message_t *msg, dns_name_t **itemp) {
REQUIRE(!ISC_LINK_LINKED(item, link));
REQUIRE(ISC_LIST_HEAD(item->list) == NULL);
if (item->hashmap != NULL) {
isc_hashmap_destroy(&item->hashmap);
}
/*
* we need to check this in case dns_name_dup() was used.
*/
@ -2551,8 +2668,7 @@ dns_message_puttemprdata(dns_message_t *msg, dns_rdata_t **item) {
static void
dns__message_putassociatedrdataset(dns_message_t *msg, dns_rdataset_t **item) {
dns_rdataset_disassociate(*item);
isc_mempool_put(msg->rdspool, *item);
*item = NULL;
dns_message_puttemprdataset(msg, item);
}
void
@ -2735,8 +2851,7 @@ dns_message_setopt(dns_message_t *msg, dns_rdataset_t *opt) {
return (ISC_R_SUCCESS);
cleanup:
dns_rdataset_disassociate(opt);
dns_message_puttemprdataset(msg, &opt);
dns__message_putassociatedrdataset(msg, &opt);
return (result);
}

View file

@ -1406,6 +1406,7 @@ rbtnode_new(isc_mem_t *mctx, const dns_name_t *name) {
};
ISC_LINK_INIT(node, deadlink);
ISC_LINK_INIT(node, prunelink);
isc_refcount_init(&node->references, 0);

View file

@ -464,6 +464,7 @@ free_rbtdb(dns_rbtdb_t *rbtdb, bool log) {
unsigned int i;
isc_result_t result;
char buf[DNS_NAME_FORMATSIZE];
dns_rbtnode_t *node = NULL;
dns_rbt_t **treep = NULL;
isc_time_t start;
@ -486,8 +487,6 @@ free_rbtdb(dns_rbtdb_t *rbtdb, bool log) {
* the overhead of unlinking all nodes here should be negligible.
*/
for (i = 0; i < rbtdb->node_lock_count; i++) {
dns_rbtnode_t *node = NULL;
node = ISC_LIST_HEAD(rbtdb->deadnodes[i]);
while (node != NULL) {
ISC_LIST_UNLINK(rbtdb->deadnodes[i], node, deadlink);
@ -495,6 +494,12 @@ free_rbtdb(dns_rbtdb_t *rbtdb, bool log) {
}
}
node = ISC_LIST_HEAD(rbtdb->prunenodes);
while (node != NULL) {
ISC_LIST_UNLINK(rbtdb->prunenodes, node, prunelink);
node = ISC_LIST_HEAD(rbtdb->prunenodes);
}
rbtdb->quantum = (rbtdb->loop != NULL) ? 100 : 0;
for (;;) {
@ -1173,16 +1178,26 @@ is_leaf(dns_rbtnode_t *node) {
node->left == NULL && node->right == NULL);
}
/*%
* The tree lock must be held when this function is called as it reads and
* updates rbtdb->prunenodes.
*/
static void
send_to_prune_tree(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
isc_rwlocktype_t locktype DNS__DB_FLARG) {
db_prune_t *prune = isc_mem_get(rbtdb->common.mctx, sizeof(*prune));
*prune = (db_prune_t){ .node = node };
bool pruning_queued = (ISC_LIST_HEAD(rbtdb->prunenodes) != NULL);
INSIST(locktype == isc_rwlocktype_write);
dns_db_attach((dns_db_t *)rbtdb, &prune->db);
dns__rbtdb_newref(rbtdb, node, locktype DNS__DB_FLARG_PASS);
INSIST(!ISC_LINK_LINKED(node, prunelink));
ISC_LIST_APPEND(rbtdb->prunenodes, node, prunelink);
isc_async_run(rbtdb->loop, prune_tree, prune);
if (!pruning_queued) {
dns_db_t *db = NULL;
dns_db_attach((dns_db_t *)rbtdb, &db);
isc_async_run(rbtdb->loop, prune_tree, db);
}
}
/*%
@ -1480,64 +1495,83 @@ restore_locks:
}
/*
* Prune the tree by recursively cleaning-up single leaves. In the worst
* case, the number of iteration is the number of tree levels, which is at
* most the maximum number of domain name labels, i.e, 127. In practice, this
* should be much smaller (only a few times), and even the worst case would be
* acceptable for a single event.
* Prune the tree by recursively cleaning up single leaves. Go through all
* nodes stored in the rbtdb->prunenodes list; for each of them, in the worst
* case, it will be necessary to traverse a number of tree levels equal to the
* maximum legal number of domain name labels (127); in practice, the number of
* tree levels to traverse will virtually always be much smaller (a few levels
* at most). While holding the tree lock throughout this entire operation is
* less than ideal, so is splitting the latter up by queueing a separate
* prune_tree() run for each node to start pruning from (as queueing requires
* allocating memory and can therefore potentially be exploited to exhaust
* available memory). Also note that actually freeing up the memory used by
* RBTDB nodes (which is what this function does) is essential to keeping cache
* memory use in check, so since the tree lock needs to be acquired anyway,
* freeing as many nodes as possible before the tree lock gets released is
* prudent.
*/
static void
prune_tree(void *arg) {
db_prune_t *prune = (db_prune_t *)arg;
dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)prune->db;
dns_rbtnode_t *node = prune->node;
dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)arg;
dns_rbtnode_t *node = NULL;
dns_rbtnode_t *parent = NULL;
unsigned int locknum;
isc_rwlocktype_t tlocktype = isc_rwlocktype_none;
isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
isc_mem_put(rbtdb->common.mctx, prune, sizeof(*prune));
TREE_WRLOCK(&rbtdb->tree_lock, &tlocktype);
locknum = node->locknum;
NODE_WRLOCK(&rbtdb->node_locks[locknum].lock, &nlocktype);
do {
parent = node->parent;
dns__rbtdb_decref(rbtdb, node, 0, &nlocktype, &tlocktype, true,
true DNS__DB_FILELINE);
if (parent != NULL && parent->down == NULL) {
/*
* node was the only down child of the parent and has
* just been removed. We'll then need to examine the
* parent. Keep the lock if possible; otherwise,
* release the old lock and acquire one for the parent.
*/
if (parent->locknum != locknum) {
NODE_UNLOCK(&rbtdb->node_locks[locknum].lock,
&nlocktype);
locknum = parent->locknum;
NODE_WRLOCK(&rbtdb->node_locks[locknum].lock,
&nlocktype);
while ((node = ISC_LIST_HEAD(rbtdb->prunenodes)) != NULL) {
locknum = node->locknum;
NODE_WRLOCK(&rbtdb->node_locks[locknum].lock, &nlocktype);
do {
if (ISC_LINK_LINKED(node, prunelink)) {
ISC_LIST_UNLINK(rbtdb->prunenodes, node,
prunelink);
}
/*
* We need to gain a reference to the node before
* decrementing it in the next iteration.
*/
if (ISC_LINK_LINKED(parent, deadlink)) {
ISC_LIST_UNLINK(rbtdb->deadnodes[locknum],
parent = node->parent;
dns__rbtdb_decref(rbtdb, node, 0, &nlocktype,
&tlocktype, true,
true DNS__DB_FILELINE);
if (parent != NULL && parent->down == NULL) {
/*
* node was the only down child of the parent
* and has just been removed. We'll then need
* to examine the parent. Keep the lock if
* possible; otherwise, release the old lock and
* acquire one for the parent.
*/
if (parent->locknum != locknum) {
NODE_UNLOCK(
&rbtdb->node_locks[locknum].lock,
&nlocktype);
locknum = parent->locknum;
NODE_WRLOCK(
&rbtdb->node_locks[locknum].lock,
&nlocktype);
}
/*
* We need to gain a reference to the node
* before decrementing it in the next iteration.
*/
if (ISC_LINK_LINKED(parent, deadlink)) {
ISC_LIST_UNLINK(
rbtdb->deadnodes[locknum],
parent, deadlink);
}
dns__rbtdb_newref(rbtdb, parent,
nlocktype DNS__DB_FILELINE);
} else {
parent = NULL;
}
dns__rbtdb_newref(rbtdb, parent,
nlocktype DNS__DB_FILELINE);
} else {
parent = NULL;
}
node = parent;
} while (node != NULL);
NODE_UNLOCK(&rbtdb->node_locks[locknum].lock, &nlocktype);
node = parent;
} while (node != NULL);
NODE_UNLOCK(&rbtdb->node_locks[locknum].lock, &nlocktype);
}
TREE_UNLOCK(&rbtdb->tree_lock, &tlocktype);
dns_db_detach((dns_db_t **)&rbtdb);
@ -3907,6 +3941,8 @@ dns__rbtdb_create(isc_mem_t *mctx, const dns_name_t *origin, dns_dbtype_t type,
ISC_LIST_INIT(rbtdb->deadnodes[i]);
}
ISC_LIST_INIT(rbtdb->prunenodes);
rbtdb->active = rbtdb->node_lock_count;
for (i = 0; i < (int)(rbtdb->node_lock_count); i++) {

View file

@ -152,6 +152,10 @@ struct dns_rbtdb {
*/
dns_rbtnodelist_t *deadnodes;
/* List of nodes from which recursive tree pruning can be started from.
* Locked by tree_lock. */
dns_rbtnodelist_t prunenodes;
/*
* Heaps. These are used for TTL based expiry in a cache,
* or for zone resigning in a zone DB. hmctx is the memory

View file

@ -178,6 +178,16 @@
*/
#define MINIMUM_QUERY_TIMEOUT (MAX_SINGLE_QUERY_TIMEOUT + 1000U)
/*
* The default maximum number of validations and validation failures per-fetch
*/
#ifndef DEFAULT_MAX_VALIDATIONS
#define DEFAULT_MAX_VALIDATIONS 16
#endif
#ifndef DEFAULT_MAX_VALIDATION_FAILURES
#define DEFAULT_MAX_VALIDATION_FAILURES 1
#endif
/* The default time in seconds for the whole query to live. */
#ifndef DEFAULT_QUERY_TIMEOUT
#define DEFAULT_QUERY_TIMEOUT MINIMUM_QUERY_TIMEOUT
@ -457,6 +467,9 @@ struct fetchctx {
dns_adbaddrinfo_t *addrinfo;
unsigned int depth;
char clientstr[ISC_SOCKADDR_FORMATSIZE];
uint32_t nvalidations;
uint32_t nfails;
};
#define FCTX_MAGIC ISC_MAGIC('F', '!', '!', '!')
@ -567,6 +580,9 @@ struct dns_resolver {
atomic_bool exiting;
atomic_bool priming;
atomic_uint_fast32_t maxvalidations;
atomic_uint_fast32_t maxvalidationfails;
/* Locked by lock. */
unsigned int spillat; /* clients-per-query */
@ -961,7 +977,8 @@ valcreate(fetchctx_t *fctx, dns_message_t *message, dns_adbaddrinfo_t *addrinfo,
result = dns_validator_create(
fctx->res->view, name, type, rdataset, sigrdataset, message,
valoptions, fctx->loop, validated, valarg, &validator);
valoptions, fctx->loop, validated, valarg, &fctx->nvalidations,
&fctx->nfails, &validator);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
inc_stats(fctx->res, dns_resstatscounter_val);
if ((valoptions & DNS_VALIDATOR_DEFER) == 0) {
@ -4518,6 +4535,8 @@ fctx_create(dns_resolver_t *res, isc_loop_t *loop, const dns_name_t *name,
.fwdpolicy = dns_fwdpolicy_none,
.result = ISC_R_FAILURE,
.loop = loop,
.nvalidations = atomic_load_relaxed(&res->maxvalidations),
.nfails = atomic_load_relaxed(&res->maxvalidationfails),
};
isc_mem_attach(mctx, &fctx->mctx);
@ -9960,6 +9979,8 @@ dns_resolver_create(dns_view_t *view, isc_loopmgr_t *loopmgr, isc_nm_t *nm,
.maxqueries = DEFAULT_MAX_QUERIES,
.alternates = ISC_LIST_INITIALIZER,
.nloops = isc_loopmgr_nloops(loopmgr),
.maxvalidations = DEFAULT_MAX_VALIDATIONS,
.maxvalidationfails = DEFAULT_MAX_VALIDATION_FAILURES,
};
RTRACE("create");
@ -10925,6 +10946,18 @@ dns_resolver_settimeout(dns_resolver_t *resolver, unsigned int timeout) {
resolver->query_timeout = timeout;
}
void
dns_resolver_setmaxvalidations(dns_resolver_t *resolver, uint32_t max) {
REQUIRE(VALID_RESOLVER(resolver));
atomic_store(&resolver->maxvalidations, max);
}
void
dns_resolver_setmaxvalidationfails(dns_resolver_t *resolver, uint32_t max) {
REQUIRE(VALID_RESOLVER(resolver));
atomic_store(&resolver->maxvalidationfails, max);
}
void
dns_resolver_setmaxdepth(dns_resolver_t *resolver, unsigned int maxdepth) {
REQUIRE(VALID_RESOLVER(resolver));

View file

@ -1539,38 +1539,44 @@ isc_result_t
dns_tsigkey_find(dns_tsigkey_t **tsigkey, const dns_name_t *name,
const dns_name_t *algorithm, dns_tsigkeyring_t *ring) {
dns_tsigkey_t *key = NULL;
isc_stdtime_t now = isc_stdtime_now();
isc_result_t result;
isc_rwlocktype_t locktype = isc_rwlocktype_read;
isc_stdtime_t now = isc_stdtime_now();
REQUIRE(name != NULL);
REQUIRE(VALID_TSIGKEYRING(ring));
REQUIRE(tsigkey != NULL && *tsigkey == NULL);
RWLOCK(&ring->lock, isc_rwlocktype_read);
again:
RWLOCK(&ring->lock, locktype);
result = isc_hashmap_find(ring->keys, dns_name_hash(name), tkey_match,
name, (void **)&key);
if (result == ISC_R_NOTFOUND) {
RWUNLOCK(&ring->lock, isc_rwlocktype_read);
RWUNLOCK(&ring->lock, locktype);
return (result);
}
if (algorithm != NULL && !dns_name_equal(key->algorithm, algorithm)) {
RWUNLOCK(&ring->lock, isc_rwlocktype_read);
RWUNLOCK(&ring->lock, locktype);
return (ISC_R_NOTFOUND);
}
if (key->inception != key->expire && isc_serial_lt(key->expire, now)) {
/*
* The key has expired.
*/
RWUNLOCK(&ring->lock, isc_rwlocktype_read);
RWLOCK(&ring->lock, isc_rwlocktype_write);
if (locktype == isc_rwlocktype_read) {
RWUNLOCK(&ring->lock, locktype);
locktype = isc_rwlocktype_write;
key = NULL;
goto again;
}
rm_lru(key);
rm_hashmap(key);
RWUNLOCK(&ring->lock, isc_rwlocktype_write);
RWUNLOCK(&ring->lock, locktype);
return (ISC_R_NOTFOUND);
}
RWUNLOCK(&ring->lock, isc_rwlocktype_read);
adjust_lru(key);
dns_tsigkey_ref(key);
RWUNLOCK(&ring->lock, locktype);
adjust_lru(key);
*tsigkey = key;
return (ISC_R_SUCCESS);
}

File diff suppressed because it is too large Load diff

View file

@ -138,6 +138,8 @@ static void
shutdown_trigger_close_cb(uv_handle_t *handle) {
isc_loop_t *loop = uv_handle_get_data(handle);
loop->shuttingdown = true;
isc_loop_detach(&loop);
}

View file

@ -2152,6 +2152,10 @@ static cfg_clausedef_t view_clauses[] = {
{ "max-recursion-queries", &cfg_type_uint32, 0 },
{ "max-stale-ttl", &cfg_type_duration, 0 },
{ "max-udp-size", &cfg_type_uint32, 0 },
{ "max-validations-per-fetch", &cfg_type_uint32,
CFG_CLAUSEFLAG_EXPERIMENTAL },
{ "max-validation-failures-per-fetch", &cfg_type_uint32,
CFG_CLAUSEFLAG_EXPERIMENTAL },
{ "message-compression", &cfg_type_boolean, 0 },
{ "min-cache-ttl", &cfg_type_duration, 0 },
{ "min-ncache-ttl", &cfg_type_duration, 0 },

View file

@ -479,10 +479,10 @@ static void
query_addnxrrsetnsec(query_ctx_t *qctx);
static isc_result_t
query_nxdomain(query_ctx_t *qctx, isc_result_t res);
query_nxdomain(query_ctx_t *qctx, isc_result_t result);
static isc_result_t
query_redirect(query_ctx_t *qctx);
query_redirect(query_ctx_t *qctx, isc_result_t result);
static isc_result_t
query_ncache(query_ctx_t *qctx, isc_result_t result);
@ -6253,6 +6253,13 @@ query_lookup_stale(ns_client_t *client) {
query_ctx_t qctx;
qctx_init(client, NULL, client->query.qtype, &qctx);
if (DNS64(client)) {
qctx.qtype = qctx.type = dns_rdatatype_a;
qctx.dns64 = true;
}
if (DNS64EXCLUDE(client)) {
qctx.dns64_exclude = true;
}
dns_db_attach(client->view->cachedb, &qctx.db);
client->query.attributes &= ~NS_QUERYATTR_RECURSIONOK;
client->query.dboptions |= DNS_DBFIND_STALETIMEOUT;
@ -7697,8 +7704,7 @@ query_usestale(query_ctx_t *qctx, isc_result_t result) {
* result from the search.
*/
static isc_result_t
query_gotanswer(query_ctx_t *qctx, isc_result_t res) {
isc_result_t result = res;
query_gotanswer(query_ctx_t *qctx, isc_result_t result) {
char errmsg[256];
CCTRACE(ISC_LOG_DEBUG(3), "query_gotanswer");
@ -7774,7 +7780,7 @@ root_key_sentinel:
return (query_coveringnsec(qctx));
case DNS_R_NCACHENXDOMAIN:
result = query_redirect(qctx);
result = query_redirect(qctx, result);
if (result != ISC_R_COMPLETE) {
return (result);
}
@ -9515,11 +9521,10 @@ query_addnxrrsetnsec(query_ctx_t *qctx) {
* Handle NXDOMAIN and empty wildcard responses.
*/
static isc_result_t
query_nxdomain(query_ctx_t *qctx, isc_result_t res) {
query_nxdomain(query_ctx_t *qctx, isc_result_t result) {
dns_section_t section;
uint32_t ttl;
isc_result_t result = res;
bool empty_wild = (res == DNS_R_EMPTYWILD);
bool empty_wild = (result == DNS_R_EMPTYWILD);
CCTRACE(ISC_LOG_DEBUG(3), "query_nxdomain");
@ -9528,7 +9533,7 @@ query_nxdomain(query_ctx_t *qctx, isc_result_t res) {
INSIST(qctx->is_zone || REDIRECT(qctx->client));
if (!empty_wild) {
result = query_redirect(qctx);
result = query_redirect(qctx, result);
if (result != ISC_R_COMPLETE) {
return (result);
}
@ -9616,7 +9621,7 @@ cleanup:
* redirecting, so query processing should continue past it.
*/
static isc_result_t
query_redirect(query_ctx_t *qctx) {
query_redirect(query_ctx_t *qctx, isc_result_t saved_result) {
isc_result_t result;
CCTRACE(ISC_LOG_DEBUG(3), "query_redirect");
@ -9657,7 +9662,7 @@ query_redirect(query_ctx_t *qctx) {
SAVE(qctx->client->query.redirect.rdataset, qctx->rdataset);
SAVE(qctx->client->query.redirect.sigrdataset,
qctx->sigrdataset);
qctx->client->query.redirect.result = DNS_R_NCACHENXDOMAIN;
qctx->client->query.redirect.result = saved_result;
dns_name_copy(qctx->fname, qctx->client->query.redirect.fname);
qctx->client->query.redirect.authoritative =
qctx->authoritative;
@ -10252,7 +10257,7 @@ query_coveringnsec(query_ctx_t *qctx) {
* We now have the proof that we have an NXDOMAIN. Apply
* NXDOMAIN redirection if configured.
*/
result = query_redirect(qctx);
result = query_redirect(qctx, DNS_R_COVERINGNSEC);
if (result != ISC_R_COMPLETE) {
redirected = true;
goto cleanup;