BIND 9.16.48

-----BEGIN PGP SIGNATURE-----
 
 iQJDBAABCgAtFiEENKwGS3ftSQfs1TU17QVz/8hFYQUFAmXIsC8PHG1pY2hhbEBp
 c2Mub3JnAAoJEO0Fc//IRWEFiKMP/RA9Xb1P21Gj235DghhIEEAKeU1ivhwa51KD
 KMajhrXA5x1ynmiR0EXlJtGOm7HPVo7k17PcVyVMao5alieqOmS5plapBcBv5Zpn
 ozm0AQfXC/kODk39JPrSb9n/sBcZ5cVnl70pomNnTxvLMRgvrw59Vmrft6/+edX0
 u9hib/HqzBOhl0MZacxPuqHXnEhK7cNhJxf6X364JkDxA10yT2h5FlR1W2XIQVky
 a7nFqKwF/8bMLndnOD8CeNHXp/6kUCfUlU6BSPBBqJlZjlHQTUzo7ky0tyMTewVt
 /elndS+2atNBDTGQOxkF0QtopN6gBqpx/t9cIH2n1OQFb95Lp+t/VKYRlKIKC293
 uMgHMufwEcoJHsDEjUJnReBtrBEbnAxJ5+xChKbH05Ga6l0e8h2G06nKBZgW97lX
 2HGEBVmyJZX3HYt2U9g2EVA6nRfHN+JUTgMulMD5bqE3WpN/nxdudRQJzy5ceP95
 vzl3ELwUxM0ZmHGJyEm5GXuf0S9mvY7VUATHzoJjjNmChMyfdaaKmv7VJS1f7vCu
 Y56ribLwWhM+t5uNiejJdxyZSdKvFETcLmOX2bTZKj66IVIKLfxskwPYdEJbeIdx
 P0xEB7ZHSSn0yhazq9jIkNxPitJqzHv9kvqyf0c71lQUOucJSo2GHDVT8nta6Ogf
 ODOKd88+
 =EZaJ
 -----END PGP SIGNATURE-----
gpgsig -----BEGIN SSH SIGNATURE-----
 U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAg25GGAuUyFX1gxo7QocNm8V6J/8
 frHSduYX7Aqk4iJLwAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5
 AAAAQLbh/3CvihAJrC9KrB5YcbPDGLaY5XDgvjv+P5NkrR4v1iWxsw7FchTtiJhQw8K1Pc
 hWNE/z2sph+06JblRssg4=
 -----END SSH SIGNATURE-----

Merge tag 'v9.16.48' into bind-9.16

BIND 9.16.48
This commit is contained in:
Michał Kępień 2024-02-14 13:41:33 +01:00
commit b56c18b477
38 changed files with 1180 additions and 510 deletions

32
CHANGES
View file

@ -2,6 +2,38 @@
records are put in the beginning of the slabheader
linked list. [GL !8675]
--- 9.16.48 released ---
6343. [bug] Fix case insensitive setting for isc_ht hashtable.
[GL #4568]
--- 9.16.47 released ---
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. [security] Change 6315 inadvertently introduced regressions that
could cause named to crash. [GL #4234]
--- 9.16.46 released ---
6319. [security] Query patterns that continuously triggered cache
database maintenance could exhaust all available memory
on the host running named. (CVE-2023-6516) [GL #4383]
6317. [security] Restore DNS64 state when handling a serve-stale timeout.
(CVE-2023-5679) [GL #4334]
6316. [security] Specific queries could trigger an assertion check with
nxdomain-redirect enabled. (CVE-2023-5517) [GL #4281]
6315. [security] Speed up parsing of DNS messages with many different
names. (CVE-2023-4408) [GL #4234]
6314. [bug] Address race conditions in dns_tsigkey_find().
[GL #4182]
6304. [bug] The wrong time was being used to determine what RRSIGs
where to be generated when dnssec-policy was in use.
[GL #4494]

View file

@ -350,7 +350,7 @@ plugin_register(const char *parameters, const void *cfg, const char *cfg_file,
cfg_line, mctx, lctx, actx));
}
isc_ht_init(&inst->ht, mctx, 16);
isc_ht_init(&inst->ht, mctx, 16, ISC_HT_CASE_SENSITIVE);
isc_mutex_init(&inst->hlock);
/*

View file

@ -47,3 +47,8 @@ rrsigonly A 10.0.0.29
cnameandkey CNAME @
cnamenokey CNAME @
dnameandkey DNAME @
mixedcase A 10.0.0.30
mixedCASE TXT "mixed case"
MIXEDcase AAAA 2002::
mIxEdCaSe LOC 37 52 56.788 N 121 54 55.02 W 1120m 10m 100m 10m

View file

@ -87,7 +87,9 @@ keyname=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" -n zone "$zone
cat "$infile" "$cnameandkey.key" "$dnameandkey.key" "$keyname.key" >"$zonefile"
"$SIGNER" -P -o "$zone" "$zonefile" >/dev/null
"$SIGNER" -P -D -o "$zone" "$zonefile" >/dev/null
cat "$zonefile" "$zonefile".signed >"$zonefile".tmp
mv "$zonefile".tmp "$zonefile".signed
zone=bogus.example.
infile=bogus.example.db.in

View file

@ -771,6 +771,21 @@ n=$((n + 1))
test "$ret" -eq 0 || echo_i "failed"
status=$((status + ret))
echo_i "checking mixed-case positive validation ($n)"
ret=0
for type in a txt aaaa loc; do
dig_with_opts +noauth mixedcase.secure.example. \
@10.53.0.3 $type >dig.out.$type.ns3.test$n || ret=1
dig_with_opts +noauth mixedcase.secure.example. \
@10.53.0.4 $type >dig.out.$type.ns4.test$n || ret=1
digcomp --lc dig.out.$type.ns3.test$n dig.out.$type.ns4.test$n || ret=1
grep "status: NOERROR" dig.out.$type.ns4.test$n >/dev/null || ret=1
grep "flags:.*ad.*QUERY" dig.out.$type.ns4.test$n >/dev/null || ret=1
done
n=$((n + 1))
test "$ret" -eq 0 || echo_i "failed"
status=$((status + ret))
echo_i "checking multi-stage positive validation NSEC/NSEC3 ($n)"
ret=0
dig_with_opts +noauth a.nsec3.example. \

View file

@ -293,7 +293,7 @@ status=$((status + ret))
echo_i "checking corrupt map files fail to load (bad node header) ($n)"
ret=0
cp map.5 badmap
stomp badmap 2754 2 99
stomp badmap 3706 2 99
$CHECKZONE -D -f map -F text -o text.5 example.nil badmap >/dev/null
[ $? = 1 ] || ret=1
n=$((n + 1))
@ -303,7 +303,7 @@ status=$((status + ret))
echo_i "checking corrupt map files fail to load (bad node data) ($n)"
ret=0
cp map.5 badmap
stomp badmap 2897 5 127
stomp badmap 3137 5 127
$CHECKZONE -D -f map -F text -o text.5 example.nil badmap >/dev/null
[ $? = 1 ] || ret=1
n=$((n + 1))

View file

@ -44,6 +44,9 @@ relnotes_issue_or_mr_id_regex = re.compile(rb":gl:`[#!][0-9]+`")
release_notes_regex = re.compile(r"doc/(arm|notes)/notes-.*\.(rst|xml)")
modified_files = danger.git.modified_files
affected_files = (
danger.git.modified_files + danger.git.created_files + danger.git.deleted_files
)
mr_labels = danger.gitlab.mr.labels
target_branch = danger.gitlab.mr.target_branch
is_backport = "Backport" in mr_labels or "Backport::Partial" in mr_labels
@ -341,18 +344,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(
@ -361,7 +364,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

@ -47,6 +47,9 @@ for Microsoft Windows operating systems.
.. include:: ../notes/notes-known-issues.rst
.. include:: ../notes/notes-current.rst
.. include:: ../notes/notes-9.16.48.rst
.. include:: ../notes/notes-9.16.47.rst
.. include:: ../notes/notes-9.16.46.rst
.. include:: ../notes/notes-9.16.45.rst
.. include:: ../notes/notes-9.16.44.rst
.. include:: ../notes/notes-9.16.43.rst

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.16.46
----------------------
.. note::
The BIND 9.16.46 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,20 @@
.. 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.16.47
----------------------
.. note::
The BIND 9.16.47 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 Vinzenz Vogel and Daniel
Stirnimann of SWITCH.

View file

@ -0,0 +1,69 @@
.. 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.16.48
----------------------
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`
- Preparing an NSEC3 closest encloser proof could cause excessive CPU
load, leading to a denial-of-service condition. This has been fixed.
:cve:`2023-50868` :gl:`#4459`
- 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 ``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`
- Query patterns that continuously triggered cache database maintenance
could cause an excessive amount of memory to be allocated, exceeding
``max-cache-size`` and potentially leading to all available memory on
the host running :iscman:`named` being exhausted. This has been fixed.
:cve:`2023-6516`
ISC would like to thank Infoblox for bringing this vulnerability to
our attention. :gl:`#4383`
Removed Features
~~~~~~~~~~~~~~~~
- Support for using AES as the DNS COOKIE algorithm (``cookie-algorithm
aes;``) has been deprecated and will be removed in a future release.
Please use the current default, SipHash-2-4, instead. :gl:`#4421`
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

@ -423,9 +423,9 @@ dns_catz_zones_merge(dns_catz_zone_t *target, dns_catz_zone_t *newzone) {
dns_name_format(&target->name, czname, DNS_NAME_FORMATSIZE);
isc_ht_init(&toadd, target->catzs->mctx, 16);
isc_ht_init(&toadd, target->catzs->mctx, 16, ISC_HT_CASE_SENSITIVE);
isc_ht_init(&tomod, target->catzs->mctx, 16);
isc_ht_init(&tomod, target->catzs->mctx, 16, ISC_HT_CASE_SENSITIVE);
isc_ht_iter_create(newzone->entries, &iter1);
@ -610,7 +610,7 @@ dns_catz_new_zones(dns_catz_zones_t **catzsp, dns_catz_zonemodmethods_t *zmm,
isc_refcount_init(&new_zones->refs, 1);
isc_ht_init(&new_zones->zones, mctx, 4);
isc_ht_init(&new_zones->zones, mctx, 4, ISC_HT_CASE_SENSITIVE);
isc_mem_attach(mctx, &new_zones->mctx);
new_zones->zmm = zmm;
@ -662,7 +662,7 @@ dns_catz_new_zone(dns_catz_zones_t *catzs, dns_catz_zone_t **zonep,
dns_name_init(&new_zone->name, NULL);
dns_name_dup(name, catzs->mctx, &new_zone->name);
isc_ht_init(&new_zone->entries, catzs->mctx, 16);
isc_ht_init(&new_zone->entries, catzs->mctx, 16, ISC_HT_CASE_SENSITIVE);
new_zone->updatetimer = NULL;
result = isc_timer_create(catzs->timermgr, isc_timertype_inactive, NULL,

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);
@ -780,6 +781,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;
@ -810,7 +818,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);
}
@ -831,7 +839,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);
}
@ -2337,7 +2345,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;
@ -2362,10 +2371,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

@ -801,44 +801,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_movename(dns_message_t *msg, dns_name_t *name,
dns_section_t fromsection, dns_section_t tosection);
/*%<
* Move a name from one section to another.
*
* Requires:
*
*\li 'msg' be valid.
*
*\li 'name' must be a name already in 'fromsection'.
*
*\li 'fromsection' must be a valid section.
*
*\li 'tosection' must be a valid section.
*/
void
dns_message_addname(dns_message_t *msg, dns_name_t *name,
dns_section_t section);

View file

@ -69,6 +69,7 @@
#include <stdbool.h>
#include <stdio.h>
#include <isc/ht.h>
#include <isc/lang.h>
#include <isc/magic.h>
#include <isc/region.h> /* Required for storage size of dns_label_t. */
@ -112,6 +113,7 @@ struct dns_name {
isc_buffer_t *buffer;
ISC_LINK(dns_name_t) link;
ISC_LIST(dns_rdataset_t) list;
isc_ht_t *ht;
};
#define DNS_NAME_MAGIC ISC_MAGIC('D', 'N', 'S', 'n')
@ -167,30 +169,24 @@ LIBDNS_EXTERNAL_DATA extern const dns_name_t *dns_wildcardname;
* unsigned char offsets[] = { 0, 6 };
* dns_name_t value = DNS_NAME_INITABSOLUTE(data, offsets);
*/
#define DNS_NAME_INITNONABSOLUTE(A, B) \
{ \
DNS_NAME_MAGIC, A, (sizeof(A) - 1), sizeof(B), \
DNS_NAMEATTR_READONLY, B, NULL, \
{ (void *)-1, (void *)-1 }, { \
NULL, NULL \
} \
#define DNS_NAME_INITNONABSOLUTE(A, B) \
{ \
DNS_NAME_MAGIC, A, (sizeof(A) - 1), sizeof(B), \
DNS_NAMEATTR_READONLY, B, NULL, \
{ (void *)-1, (void *)-1 }, { NULL, NULL }, NULL \
}
#define DNS_NAME_INITABSOLUTE(A, B) \
{ \
DNS_NAME_MAGIC, A, sizeof(A), sizeof(B), \
DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE, B, \
NULL, { (void *)-1, (void *)-1 }, { \
NULL, NULL \
} \
#define DNS_NAME_INITABSOLUTE(A, B) \
{ \
DNS_NAME_MAGIC, A, sizeof(A), sizeof(B), \
DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE, B, \
NULL, { (void *)-1, (void *)-1 }, { NULL, NULL }, NULL \
}
#define DNS_NAME_INITEMPTY \
{ \
DNS_NAME_MAGIC, NULL, 0, 0, 0, NULL, NULL, \
{ (void *)-1, (void *)-1 }, { \
NULL, NULL \
} \
#define DNS_NAME_INITEMPTY \
{ \
DNS_NAME_MAGIC, NULL, 0, 0, 0, NULL, NULL, \
{ (void *)-1, (void *)-1 }, { NULL, NULL }, NULL \
}
/*%
@ -1357,6 +1353,7 @@ ISC_LANG_ENDDECLS
_n->buffer = NULL; \
ISC_LINK_INIT(_n, link); \
ISC_LIST_INIT(_n->list); \
_n->ht = NULL; \
} while (0)
#define DNS_NAME_RESET(n) \

View file

@ -140,6 +140,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

@ -149,6 +149,7 @@ struct dns_validator {
unsigned int depth;
unsigned int authcount;
unsigned int authfail;
bool failed;
isc_stdtime_t start;
};

View file

@ -471,6 +471,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

@ -13,4 +13,4 @@
# Whenever releasing a new major release of BIND9, set this value
# back to 1.0 when releasing the first alpha. Map files are *never*
# compatible across major releases.
MAPAPI=3.0
MAPAPI=4.0

View file

@ -22,6 +22,8 @@
#include <stdbool.h>
#include <isc/buffer.h>
#include <isc/hash.h>
#include <isc/ht.h>
#include <isc/mem.h>
#include <isc/print.h>
#include <isc/string.h> /* Required for HP/UX (and others?) */
@ -507,9 +509,11 @@ msgresetsigs(dns_message_t *msg, bool replying) {
} else {
dns_rdataset_disassociate(msg->tsig);
isc_mempool_put(msg->rdspool, msg->tsig);
msg->tsig = NULL;
if (msg->querytsig != NULL) {
dns_rdataset_disassociate(msg->querytsig);
isc_mempool_put(msg->rdspool, msg->querytsig);
msg->querytsig = NULL;
}
}
dns_message_puttempname(msg, &msg->tsigname);
@ -798,6 +802,18 @@ dns_message_detach(dns_message_t **messagep) {
}
}
static isc_result_t
name_hash_add(isc_ht_t *ht, dns_name_t *name, dns_name_t **foundp) {
isc_result_t result = isc_ht_find(ht, name->ndata, name->length,
(void **)foundp);
if (result == ISC_R_SUCCESS) {
return (ISC_R_EXISTS);
}
result = isc_ht_add(ht, name->ndata, name->length, (void *)name);
INSIST(result == ISC_R_SUCCESS);
return (ISC_R_SUCCESS);
}
static isc_result_t
findname(dns_name_t **foundname, const dns_name_t *target,
dns_namelist_t *section) {
@ -817,29 +833,26 @@ 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 **rdataset) {
dns_rdataset_t *curr;
typedef struct __attribute__((__packed__)) rds_key {
dns_rdataclass_t rdclass;
dns_rdatatype_t type;
dns_rdatatype_t covers;
} rds_key_t;
REQUIRE(name != NULL);
REQUIRE(rdataset == NULL || *rdataset == NULL);
for (curr = ISC_LIST_TAIL(name->list); curr != NULL;
curr = ISC_LIST_PREV(curr, link))
{
if (curr->rdclass == rdclass && curr->type == type &&
curr->covers == covers)
{
if (rdataset != NULL) {
*rdataset = curr;
}
return (ISC_R_SUCCESS);
}
static isc_result_t
rds_hash_add(isc_ht_t *ht, dns_rdataset_t *rds, dns_rdataset_t **foundp) {
rds_key_t key = { .rdclass = rds->rdclass,
.type = rds->type,
.covers = rds->covers };
isc_result_t result = isc_ht_find(ht, (const unsigned char *)&key,
sizeof(key), (void **)foundp);
if (result == ISC_R_SUCCESS) {
return (ISC_R_EXISTS);
}
return (ISC_R_NOTFOUND);
result = isc_ht_add(ht, (const unsigned char *)&key, sizeof(key),
(void *)rds);
INSIST(result == ISC_R_SUCCESS);
return (ISC_R_SUCCESS);
}
isc_result_t
@ -966,6 +979,18 @@ 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;
for (name = ISC_LIST_HEAD(*section); name != NULL;
name = ISC_LIST_NEXT(name, link))
{
if (name->ht != NULL) {
isc_ht_destroy(&name->ht);
}
}
}
static isc_result_t
getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
unsigned int options) {
@ -975,13 +1000,19 @@ getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
dns_name_t *name2 = 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_ht = false;
isc_ht_t *name_map = NULL;
if (msg->counts[DNS_SECTION_QUESTION] > 1) {
isc_ht_init(&name_map, msg->mctx, 1, ISC_HT_CASE_INSENSITIVE);
}
for (count = 0; count < msg->counts[DNS_SECTION_QUESTION]; count++) {
name = NULL;
@ -1002,13 +1033,19 @@ 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 = name_hash_add(name_map, name, &name2);
/*
* If it is the first name in the section, accept it.
@ -1020,19 +1057,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;
break;
default:
UNREACHABLE();
}
free_name = false;
/*
* Get type and class.
*/
@ -1062,14 +1105,6 @@ 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.
*/
@ -1083,6 +1118,7 @@ getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
result = ISC_R_NOMEMORY;
goto cleanup;
}
dns_rdataset_init(rdataset);
/*
* Convert rdatalist to rdataset, and attach the latter to
@ -1091,32 +1127,71 @@ getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
rdatalist->type = rdtype;
rdatalist->rdclass = rdclass;
dns_rdataset_init(rdataset);
result = dns_rdatalist_tordataset(rdatalist, rdataset);
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
RUNTIME_CHECK(result == ISC_R_SUCCESS);
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->ht == NULL) {
isc_ht_init(&name->ht, msg->mctx, 1,
ISC_HT_CASE_SENSITIVE);
free_ht = true;
INSIST(ISC_LIST_HEAD(name->list) ==
ISC_LIST_TAIL(name->list));
dns_rdataset_t *old_rdataset =
ISC_LIST_HEAD(name->list);
result = rds_hash_add(name->ht, old_rdataset, NULL);
INSIST(result == ISC_R_SUCCESS);
}
result = rds_hash_add(name->ht, 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) {
INSIST(!dns_rdataset_isassociated(rdataset));
if (dns_rdataset_isassociated(rdataset)) {
dns_rdataset_disassociate(rdataset);
}
isc_mempool_put(msg->rdspool, rdataset);
}
if (free_name) {
dns_message_puttempname(msg, &name);
}
if (free_ht) {
cleanup_name_hashmaps(section);
}
if (name_map != NULL) {
isc_ht_destroy(&name_map);
}
return (result);
}
@ -1196,17 +1271,24 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
dns_name_t *name = NULL;
dns_name_t *name2 = 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_ht = false;
bool preserve_order = ((options & DNS_MESSAGEPARSE_PRESERVEORDER) != 0);
bool best_effort = ((options & DNS_MESSAGEPARSE_BESTEFFORT) != 0);
bool isedns, issigzero, istsig;
isc_ht_t *name_map = NULL;
if (msg->counts[sectionid] > 1) {
isc_ht_init(&name_map, msg->mctx, 1, ISC_HT_CASE_INSENSITIVE);
}
for (count = 0; count < msg->counts[sectionid]; count++) {
int recstart = source->current;
@ -1214,10 +1296,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;
result = dns_message_gettempname(msg, &name);
@ -1257,8 +1339,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;
}
@ -1365,10 +1447,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))
{
@ -1456,34 +1534,71 @@ 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 = name_hash_add(name_map, name, &name2);
/*
* If it is a new name, append to the section.
*/
if (result == ISC_R_SUCCESS) {
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 = name2;
} else {
ISC_LIST_APPEND(*section, name, link);
name2 = NULL;
break;
default:
UNREACHABLE();
}
free_name = false;
}
rdatalist = newrdatalist(msg);
if (rdatalist == NULL) {
result = ISC_R_NOMEMORY;
goto cleanup;
}
dns_message_gettemprdataset(msg, &rdataset);
if (rdataset == NULL) {
result = ISC_R_NOMEMORY;
goto cleanup;
}
rdatalist->type = rdtype;
rdatalist->covers = covers;
rdatalist->rdclass = rdclass;
rdatalist->ttl = ttl;
RUNTIME_CHECK(dns_rdatalist_tordataset(rdatalist, rdataset) ==
ISC_R_SUCCESS);
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
@ -1493,63 +1608,71 @@ 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->ht == NULL) {
isc_ht_init(&name->ht, msg->mctx, 1,
ISC_HT_CASE_SENSITIVE);
free_ht = true;
INSIST(ISC_LIST_HEAD(name->list) ==
ISC_LIST_TAIL(name->list));
dns_rdataset_t *old_rdataset =
ISC_LIST_HEAD(name->list);
result = rds_hash_add(name->ht, old_rdataset,
NULL);
INSIST(result == ISC_R_SUCCESS);
}
found_rdataset = NULL;
result = rds_hash_add(name->ht, rdataset,
&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_rdataset_disassociate(rdataset);
isc_mempool_put(msg->rdspool, 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);
if (rdataset == NULL) {
result = ISC_R_NOMEMORY;
goto cleanup;
}
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);
RUNTIME_CHECK(
dns_rdatalist_tordataset(rdatalist, rdataset) ==
ISC_R_SUCCESS);
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();
}
}
@ -1584,8 +1707,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);
@ -1596,8 +1717,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;
@ -1607,22 +1726,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 |= DNS_NAMEATTR_NOCOMPRESS;
rdataset = NULL;
free_rdataset = false;
free_name = false;
}
rdataset = NULL;
if (seen_problem) {
if (free_name) {
dns_message_puttempname(msg, &name);
}
if (free_rdataset) {
isc_mempool_put(msg->rdspool, rdataset);
}
free_name = free_rdataset = false;
free_name = false;
}
INSIST(!free_name);
INSIST(!free_rdataset);
}
/*
@ -1640,16 +1754,24 @@ 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_rdataset_disassociate(rdataset);
isc_mempool_put(msg->rdspool, rdataset);
}
if (free_name) {
dns_message_puttempname(msg, &name);
}
if (free_rdataset) {
isc_mempool_put(msg->rdspool, rdataset);
if (free_ht) {
cleanup_name_hashmaps(section);
}
if (name_map != NULL) {
isc_ht_destroy(&name_map);
}
return (result);
@ -2446,7 +2568,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;
/*
@ -2493,22 +2615,6 @@ dns_message_findname(dns_message_t *msg, dns_section_t section,
return (result);
}
void
dns_message_movename(dns_message_t *msg, dns_name_t *name,
dns_section_t fromsection, dns_section_t tosection) {
REQUIRE(msg != NULL);
REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
REQUIRE(name != NULL);
REQUIRE(VALID_NAMED_SECTION(fromsection));
REQUIRE(VALID_NAMED_SECTION(tosection));
/*
* Unlink the name from the old section
*/
ISC_LIST_UNLINK(msg->sections[fromsection], name, link);
ISC_LIST_APPEND(msg->sections[tosection], name, link);
}
void
dns_message_addname(dns_message_t *msg, dns_name_t *name,
dns_section_t section) {
@ -2600,6 +2706,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->ht != NULL) {
isc_ht_destroy(&item->ht);
}
/*
* we need to check this in case dns_name_dup() was used.
*/

View file

@ -188,6 +188,7 @@ dns_name_invalidate(dns_name_t *name) {
name->offsets = NULL;
name->buffer = NULL;
ISC_LINK_INIT(name, link);
INSIST(name->ht == NULL);
}
bool

View file

@ -2307,6 +2307,7 @@ create_node(isc_mem_t *mctx, const dns_name_t *name, dns_rbtnode_t **nodep) {
HASHVAL(node) = 0;
ISC_LINK_INIT(node, deadlink);
ISC_LINK_INIT(node, prunelink);
LOCKNUM(node) = 0;
WILD(node) = 0;

View file

@ -521,6 +521,10 @@ struct dns_rbtdb {
*/
rbtnodelist_t *deadnodes;
/* List of nodes from which recursive tree pruning can be started from.
* Locked by tree_lock. */
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
@ -1091,6 +1095,7 @@ free_rbtdb(dns_rbtdb_t *rbtdb, bool log, isc_event_t *event) {
unsigned int i;
isc_result_t result;
char buf[DNS_NAME_FORMATSIZE];
dns_rbtnode_t *node = NULL;
dns_rbt_t **treep;
isc_time_t start;
@ -1116,8 +1121,6 @@ free_rbtdb(dns_rbtdb_t *rbtdb, bool log, isc_event_t *event) {
* the overhead of unlinking all nodes here should be negligible.
*/
for (i = 0; i < rbtdb->node_lock_count; i++) {
dns_rbtnode_t *node;
node = ISC_LIST_HEAD(rbtdb->deadnodes[i]);
while (node != NULL) {
ISC_LIST_UNLINK(rbtdb->deadnodes[i], node, deadlink);
@ -1125,6 +1128,12 @@ free_rbtdb(dns_rbtdb_t *rbtdb, bool log, isc_event_t *event) {
}
}
node = ISC_LIST_HEAD(rbtdb->prunenodes);
while (node != NULL) {
ISC_LIST_UNLINK(rbtdb->prunenodes, node, prunelink);
node = ISC_LIST_HEAD(rbtdb->prunenodes);
}
if (event == NULL) {
rbtdb->quantum = (rbtdb->task != NULL) ? 100 : 0;
}
@ -1959,19 +1968,32 @@ 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) {
isc_event_t *ev;
dns_db_t *db;
bool pruning_queued = (ISC_LIST_HEAD(rbtdb->prunenodes) != NULL);
INSIST(locktype == isc_rwlocktype_write);
ev = isc_event_allocate(rbtdb->common.mctx, NULL, DNS_EVENT_RBTPRUNE,
prune_tree, node, sizeof(isc_event_t));
new_reference(rbtdb, node, locktype);
db = NULL;
attach((dns_db_t *)rbtdb, &db);
ev->ev_sender = db;
isc_task_send(rbtdb->task, &ev);
INSIST(!ISC_LINK_LINKED(node, prunelink));
ISC_LIST_APPEND(rbtdb->prunenodes, node, prunelink);
if (!pruning_queued) {
isc_event_t *ev = NULL;
dns_db_t *db = NULL;
attach((dns_db_t *)rbtdb, &db);
ev = isc_event_allocate(rbtdb->common.mctx, NULL,
DNS_EVENT_RBTPRUNE, prune_tree, db,
sizeof(isc_event_t));
isc_task_send(rbtdb->task, &ev);
}
}
/*%
@ -2246,17 +2268,26 @@ 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(isc_task_t *task, isc_event_t *event) {
dns_rbtdb_t *rbtdb = event->ev_sender;
dns_rbtnode_t *node = event->ev_arg;
dns_rbtnode_t *parent;
dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)event->ev_arg;
dns_rbtnode_t *node = NULL;
dns_rbtnode_t *parent = NULL;
unsigned int locknum;
UNUSED(task);
@ -2264,44 +2295,60 @@ prune_tree(isc_task_t *task, isc_event_t *event) {
isc_event_free(&event);
RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
locknum = node->locknum;
NODE_LOCK(&rbtdb->node_locks[locknum].lock, isc_rwlocktype_write);
do {
parent = node->parent;
decrement_reference(rbtdb, node, 0, isc_rwlocktype_write,
isc_rwlocktype_write, true);
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,
isc_rwlocktype_write);
locknum = parent->locknum;
NODE_LOCK(&rbtdb->node_locks[locknum].lock,
isc_rwlocktype_write);
while ((node = ISC_LIST_HEAD(rbtdb->prunenodes)) != NULL) {
locknum = node->locknum;
NODE_LOCK(&rbtdb->node_locks[locknum].lock,
isc_rwlocktype_write);
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;
decrement_reference(rbtdb, node, 0,
isc_rwlocktype_write,
isc_rwlocktype_write, true);
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,
isc_rwlocktype_write);
locknum = parent->locknum;
NODE_LOCK(
&rbtdb->node_locks[locknum].lock,
isc_rwlocktype_write);
}
/*
* 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);
}
new_reference(rbtdb, parent,
isc_rwlocktype_write);
} else {
parent = NULL;
}
new_reference(rbtdb, parent, isc_rwlocktype_write);
} else {
parent = NULL;
}
node = parent;
} while (node != NULL);
NODE_UNLOCK(&rbtdb->node_locks[locknum].lock, isc_rwlocktype_write);
node = parent;
} while (node != NULL);
NODE_UNLOCK(&rbtdb->node_locks[locknum].lock,
isc_rwlocktype_write);
}
RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
detach((dns_db_t **)&rbtdb);
@ -8785,6 +8832,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

@ -10634,8 +10634,8 @@ dns_resolver_create(dns_view_t *view, isc_taskmgr_t *taskmgr,
* Since we have a pool of tasks we bind them to task queues
* to spread the load evenly
*/
result = isc_task_create_bound(taskmgr, 0,
&res->buckets[i].task, i);
result = isc_task_create_bound(
taskmgr, 0, &res->buckets[i].task, ISC_NM_TASK_SLOW(i));
if (result != ISC_R_SUCCESS) {
isc_mutex_destroy(&res->buckets[i].lock);
goto cleanup_buckets;

View file

@ -1544,7 +1544,7 @@ dns_rpz_new_zone(dns_rpz_zones_t *rpzs, dns_rpz_zone_t **rpzp) {
* simplifies update_from_db
*/
isc_ht_init(&zone->nodes, rpzs->mctx, 1);
isc_ht_init(&zone->nodes, rpzs->mctx, 1, ISC_HT_CASE_SENSITIVE);
dns_name_init(&zone->origin, NULL);
dns_name_init(&zone->client_ip, NULL);
@ -1722,7 +1722,8 @@ setup_update(dns_rpz_zone_t *rpz) {
ISC_LOG_DEBUG(1), "rpz: %s: using hashtable size %d",
domain, hashsize);
isc_ht_init(&rpz->newnodes, rpz->rpzs->mctx, hashsize);
isc_ht_init(&rpz->newnodes, rpz->rpzs->mctx, hashsize,
ISC_HT_CASE_SENSITIVE);
result = dns_db_createiterator(rpz->updb, DNS_DB_NONSEC3, &rpz->updbit);
if (result != ISC_R_SUCCESS) {

View file

@ -1744,8 +1744,9 @@ isc_result_t
dns_tsigkey_find(dns_tsigkey_t **tsigkey, const dns_name_t *name,
const dns_name_t *algorithm, dns_tsig_keyring_t *ring) {
dns_tsigkey_t *key;
isc_stdtime_t now;
isc_result_t result;
isc_rwlocktype_t locktype = isc_rwlocktype_read;
isc_stdtime_t now;
REQUIRE(tsigkey != NULL);
REQUIRE(*tsigkey == NULL);
@ -1757,25 +1758,30 @@ dns_tsigkey_find(dns_tsigkey_t **tsigkey, const dns_name_t *name,
RWUNLOCK(&ring->lock, isc_rwlocktype_write);
isc_stdtime_get(&now);
RWLOCK(&ring->lock, isc_rwlocktype_read);
again:
RWLOCK(&ring->lock, locktype);
key = NULL;
result = dns_rbt_findname(ring->keys, name, 0, NULL, (void *)&key);
if (result == DNS_R_PARTIALMATCH || result == ISC_R_NOTFOUND) {
RWUNLOCK(&ring->lock, isc_rwlocktype_read);
RWUNLOCK(&ring->lock, locktype);
return (ISC_R_NOTFOUND);
}
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;
goto again;
}
remove_fromring(key);
RWUNLOCK(&ring->lock, isc_rwlocktype_write);
RWUNLOCK(&ring->lock, locktype);
return (ISC_R_NOTFOUND);
}
#if 0
@ -1790,7 +1796,7 @@ dns_tsigkey_find(dns_tsigkey_t **tsigkey, const dns_name_t *name,
}
#endif /* if 0 */
isc_refcount_increment(&key->refs);
RWUNLOCK(&ring->lock, isc_rwlocktype_read);
RWUNLOCK(&ring->lock, locktype);
adjust_lru(key);
*tsigkey = key;
return (ISC_R_SUCCESS);

View file

@ -1104,8 +1104,8 @@ create_validator(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type,
* 'rdataset'. If found, build a dst_key_t for it and point val->key at
* it.
*
* If val->key is already non-NULL, locate it in the rdataset and then
* search past it for the *next* key that could have signed 'siginfo', then
* If val->key is already non-NULL, start searching from the next position in
* 'rdataset' to find the *next* key that could have signed 'siginfo', then
* set val->key to that.
*
* Returns ISC_R_SUCCESS if a possible matching key has been found,
@ -1118,59 +1118,59 @@ select_signing_key(dns_validator_t *val, dns_rdataset_t *rdataset) {
isc_buffer_t b;
dns_rdata_t rdata = DNS_RDATA_INIT;
dst_key_t *oldkey = val->key;
bool foundold;
bool no_rdata = false;
if (oldkey == NULL) {
foundold = true;
result = dns_rdataset_first(rdataset);
} else {
foundold = false;
dst_key_free(&oldkey);
val->key = NULL;
result = dns_rdataset_next(rdataset);
}
if (result != ISC_R_SUCCESS) {
goto done;
}
result = dns_rdataset_first(rdataset);
if (result != ISC_R_SUCCESS) {
goto failure;
}
do {
dns_rdataset_current(rdataset, &rdata);
isc_buffer_init(&b, rdata.data, rdata.length);
isc_buffer_add(&b, rdata.length);
INSIST(val->key == NULL);
result = dst_key_fromdns(&siginfo->signer, rdata.rdclass, &b,
val->view->mctx, &val->key);
result = dst_key_fromdns_ex(&siginfo->signer, rdata.rdclass, &b,
val->view->mctx, no_rdata,
&val->key);
if (result == ISC_R_SUCCESS) {
if (siginfo->algorithm ==
(dns_secalg_t)dst_key_alg(val->key) &&
siginfo->keyid ==
(dns_keytag_t)dst_key_id(val->key) &&
(dst_key_flags(val->key) & DNS_KEYFLAG_REVOKE) ==
0 &&
dst_key_iszonekey(val->key))
{
if (foundold) {
/*
* This is the key we're looking for.
*/
return (ISC_R_SUCCESS);
} else if (dst_key_compare(oldkey, val->key)) {
foundold = true;
dst_key_free(&oldkey);
if (no_rdata) {
/* Retry with full key */
dns_rdata_reset(&rdata);
dst_key_free(&val->key);
no_rdata = false;
continue;
}
/* This is the key we're looking for. */
goto done;
}
dst_key_free(&val->key);
}
dns_rdata_reset(&rdata);
result = dns_rdataset_next(rdataset);
no_rdata = true;
} while (result == ISC_R_SUCCESS);
done:
if (result == ISC_R_NOMORE) {
result = ISC_R_NOTFOUND;
}
failure:
if (oldkey != NULL) {
dst_key_free(&oldkey);
}
return (result);
}
@ -1589,20 +1589,9 @@ validate_answer(dns_validator_t *val, bool resume) {
continue;
}
do {
isc_result_t tresult;
vresult = verify(val, val->key, &rdata,
val->siginfo->keyid);
if (vresult == ISC_R_SUCCESS) {
break;
}
tresult = select_signing_key(val, val->keyset);
if (tresult != ISC_R_SUCCESS) {
break;
}
} while (1);
vresult = verify(val, val->key, &rdata, val->siginfo->keyid);
if (vresult != ISC_R_SUCCESS) {
val->failed = true;
validator_log(val, ISC_LOG_DEBUG(3),
"failed to verify rdataset");
} else {
@ -1639,9 +1628,13 @@ validate_answer(dns_validator_t *val, bool resume) {
} else {
validator_log(val, ISC_LOG_DEBUG(3),
"verify failure: %s",
isc_result_totext(result));
isc_result_totext(vresult));
resume = false;
}
if (val->failed) {
result = ISC_R_NOMORE;
break;
}
}
if (result != ISC_R_NOMORE) {
validator_log(val, ISC_LOG_DEBUG(3),

View file

@ -535,7 +535,6 @@ dns_message_clonebuffer
dns_message_create
dns_message_currentname
dns_message_detach
dns_message_find
dns_message_findname
dns_message_findtype
dns_message_firstname
@ -554,7 +553,6 @@ dns_message_gettsigkey
dns_message_headertotext
dns_message_logfmtpacket
dns_message_logpacket
dns_message_movename
dns_message_nextname
dns_message_parse
dns_message_peekheader
@ -1467,6 +1465,7 @@ dst_key_format
dst_key_free
dst_key_frombuffer
dst_key_fromdns
dst_key_fromdns_ex
dst_key_fromfile
dst_key_fromgssapi
dst_key_fromlabel

View file

@ -27,51 +27,274 @@ typedef struct isc_ht_node isc_ht_node_t;
#define ISC_HT_MAGIC ISC_MAGIC('H', 'T', 'a', 'b')
#define ISC_HT_VALID(ht) ISC_MAGIC_VALID(ht, ISC_HT_MAGIC)
#define HT_NO_BITS 0
#define HT_MIN_BITS 1
#define HT_MAX_BITS 32
#define HT_OVERCOMMIT 3
#define HT_NEXTTABLE(idx) ((idx == 0) ? 1 : 0)
#define TRY_NEXTTABLE(idx, ht) (idx == ht->hindex && rehashing_in_progress(ht))
#define GOLDEN_RATIO_32 0x61C88647
#define HASHSIZE(bits) (UINT64_C(1) << (bits))
struct isc_ht_node {
void *value;
isc_ht_node_t *next;
uint32_t hashval;
size_t keysize;
unsigned char key[FLEXIBLE_ARRAY_MEMBER];
unsigned char key[];
};
struct isc_ht {
unsigned int magic;
isc_mem_t *mctx;
size_t size;
size_t mask;
unsigned int count;
isc_ht_node_t **table;
size_t count;
bool case_sensitive;
size_t size[2];
uint8_t hashbits[2];
isc_ht_node_t **table[2];
uint8_t hindex;
uint32_t hiter; /* rehashing iterator */
};
struct isc_ht_iter {
isc_ht_t *ht;
size_t i;
uint8_t hindex;
isc_ht_node_t *cur;
};
static isc_ht_node_t *
isc__ht_find(const isc_ht_t *ht, const unsigned char *key,
const uint32_t keysize, const uint32_t hashval, const uint8_t idx);
static void
isc__ht_add(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize,
const uint32_t hashval, const uint8_t idx, void *value);
static isc_result_t
isc__ht_delete(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize,
const uint32_t hashval, const uint8_t idx);
static uint32_t
rehash_bits(isc_ht_t *ht, size_t newcount);
static void
hashtable_new(isc_ht_t *ht, const uint8_t idx, const uint8_t bits);
static void
hashtable_free(isc_ht_t *ht, const uint8_t idx);
static void
hashtable_rehash(isc_ht_t *ht, uint32_t newbits);
static void
hashtable_rehash_one(isc_ht_t *ht);
static void
maybe_rehash(isc_ht_t *ht, size_t newcount);
static isc_result_t
isc__ht_iter_next(isc_ht_iter_t *it);
static uint8_t maptolower[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73,
0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b,
0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b,
0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3,
0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb,
0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3,
0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb,
0xfc, 0xfd, 0xfe, 0xff
};
static int
memcasecmp(const void *vs1, const void *vs2, size_t len) {
uint8_t const *s1 = vs1;
uint8_t const *s2 = vs2;
for (size_t i = 0; i < len; i++) {
uint8_t u1 = s1[i];
uint8_t u2 = s2[i];
int U1 = maptolower[u1];
int U2 = maptolower[u2];
int diff = U1 - U2;
if (diff) {
return diff;
}
}
return 0;
}
static bool
isc__ht_node_match(isc_ht_node_t *node, const uint32_t hashval,
const uint8_t *key, uint32_t keysize, bool case_sensitive) {
return (node->hashval == hashval && node->keysize == keysize &&
(case_sensitive ? (memcmp(node->key, key, keysize) == 0)
: (memcasecmp(node->key, key, keysize) == 0)));
}
static uint32_t
hash_32(uint32_t val, unsigned int bits) {
REQUIRE(bits <= HT_MAX_BITS);
/* High bits are more random. */
return (val * GOLDEN_RATIO_32 >> (32 - bits));
}
static bool
rehashing_in_progress(const isc_ht_t *ht) {
return (ht->table[HT_NEXTTABLE(ht->hindex)] != NULL);
}
static bool
hashtable_is_overcommited(isc_ht_t *ht) {
return (ht->count >= (ht->size[ht->hindex] * HT_OVERCOMMIT));
}
static uint32_t
rehash_bits(isc_ht_t *ht, size_t newcount) {
uint32_t newbits = ht->hashbits[ht->hindex];
while (newcount >= HASHSIZE(newbits) && newbits <= HT_MAX_BITS) {
newbits += 1;
}
return (newbits);
}
/*
* Rebuild the hashtable to reduce the load factor
*/
static void
hashtable_rehash(isc_ht_t *ht, uint32_t newbits) {
uint8_t oldindex = ht->hindex;
uint32_t oldbits = ht->hashbits[oldindex];
uint8_t newindex = HT_NEXTTABLE(oldindex);
REQUIRE(ht->hashbits[oldindex] >= HT_MIN_BITS);
REQUIRE(ht->hashbits[oldindex] <= HT_MAX_BITS);
REQUIRE(ht->table[oldindex] != NULL);
REQUIRE(newbits <= HT_MAX_BITS);
REQUIRE(ht->hashbits[newindex] == HT_NO_BITS);
REQUIRE(ht->table[newindex] == NULL);
REQUIRE(newbits > oldbits);
hashtable_new(ht, newindex, newbits);
ht->hindex = newindex;
hashtable_rehash_one(ht);
}
static void
hashtable_rehash_one(isc_ht_t *ht) {
isc_ht_node_t **newtable = ht->table[ht->hindex];
uint32_t oldsize = ht->size[HT_NEXTTABLE(ht->hindex)];
isc_ht_node_t **oldtable = ht->table[HT_NEXTTABLE(ht->hindex)];
isc_ht_node_t *node = NULL;
isc_ht_node_t *nextnode;
/* Find first non-empty node */
while (ht->hiter < oldsize && oldtable[ht->hiter] == NULL) {
ht->hiter++;
}
/* Rehashing complete */
if (ht->hiter == oldsize) {
hashtable_free(ht, HT_NEXTTABLE(ht->hindex));
ht->hiter = 0;
return;
}
/* Move the first non-empty node from old hashtable to new hashtable */
for (node = oldtable[ht->hiter]; node != NULL; node = nextnode) {
uint32_t hash = hash_32(node->hashval,
ht->hashbits[ht->hindex]);
nextnode = node->next;
node->next = newtable[hash];
newtable[hash] = node;
}
oldtable[ht->hiter] = NULL;
ht->hiter++;
}
static void
maybe_rehash(isc_ht_t *ht, size_t newcount) {
uint32_t newbits = rehash_bits(ht, newcount);
if (ht->hashbits[ht->hindex] < newbits && newbits <= HT_MAX_BITS) {
hashtable_rehash(ht, newbits);
}
}
static void
hashtable_new(isc_ht_t *ht, const uint8_t idx, const uint8_t bits) {
size_t size;
REQUIRE(ht->hashbits[idx] == HT_NO_BITS);
REQUIRE(ht->table[idx] == NULL);
REQUIRE(bits >= HT_MIN_BITS);
REQUIRE(bits <= HT_MAX_BITS);
ht->hashbits[idx] = bits;
ht->size[idx] = HASHSIZE(ht->hashbits[idx]);
size = ht->size[idx] * sizeof(isc_ht_node_t *);
ht->table[idx] = isc_mem_get(ht->mctx, size);
memset(ht->table[idx], 0, size);
}
static void
hashtable_free(isc_ht_t *ht, const uint8_t idx) {
size_t size = ht->size[idx] * sizeof(isc_ht_node_t *);
for (size_t i = 0; i < ht->size[idx]; i++) {
isc_ht_node_t *node = ht->table[idx][i];
while (node != NULL) {
isc_ht_node_t *next = node->next;
ht->count--;
isc_mem_put(ht->mctx, node,
sizeof(*node) + node->keysize);
node = next;
}
}
isc_mem_put(ht->mctx, ht->table[idx], size);
ht->hashbits[idx] = HT_NO_BITS;
ht->table[idx] = NULL;
}
void
isc_ht_init(isc_ht_t **htp, isc_mem_t *mctx, uint8_t bits) {
isc_ht_init(isc_ht_t **htp, isc_mem_t *mctx, uint8_t bits,
unsigned int options) {
isc_ht_t *ht = NULL;
size_t i;
bool case_sensitive = ((options & ISC_HT_CASE_INSENSITIVE) == 0);
REQUIRE(htp != NULL && *htp == NULL);
REQUIRE(mctx != NULL);
REQUIRE(bits >= 1 && bits <= (sizeof(size_t) * 8 - 1));
REQUIRE(bits >= 1 && bits <= HT_MAX_BITS);
ht = isc_mem_get(mctx, sizeof(struct isc_ht));
ht = isc_mem_get(mctx, sizeof(*ht));
*ht = (isc_ht_t){
.case_sensitive = case_sensitive,
};
ht->mctx = NULL;
isc_mem_attach(mctx, &ht->mctx);
ht->size = ((size_t)1 << bits);
ht->mask = ((size_t)1 << bits) - 1;
ht->count = 0;
ht->table = isc_mem_get(ht->mctx, ht->size * sizeof(isc_ht_node_t *));
for (i = 0; i < ht->size; i++) {
ht->table[i] = NULL;
}
hashtable_new(ht, 0, bits);
ht->magic = ISC_HT_MAGIC;
@ -81,126 +304,184 @@ isc_ht_init(isc_ht_t **htp, isc_mem_t *mctx, uint8_t bits) {
void
isc_ht_destroy(isc_ht_t **htp) {
isc_ht_t *ht;
size_t i;
REQUIRE(htp != NULL);
REQUIRE(ISC_HT_VALID(*htp));
ht = *htp;
*htp = NULL;
REQUIRE(ISC_HT_VALID(ht));
ht->magic = 0;
for (i = 0; i < ht->size; i++) {
isc_ht_node_t *node = ht->table[i];
while (node != NULL) {
isc_ht_node_t *next = node->next;
ht->count--;
isc_mem_put(ht->mctx, node,
offsetof(isc_ht_node_t, key) +
node->keysize);
node = next;
for (size_t i = 0; i <= 1; i++) {
if (ht->table[i] != NULL) {
hashtable_free(ht, i);
}
}
INSIST(ht->count == 0);
isc_mem_put(ht->mctx, ht->table, ht->size * sizeof(isc_ht_node_t *));
isc_mem_putanddetach(&ht->mctx, ht, sizeof(struct isc_ht));
isc_mem_putanddetach(&ht->mctx, ht, sizeof(*ht));
}
static void
isc__ht_add(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize,
const uint32_t hashval, const uint8_t idx, void *value) {
isc_ht_node_t *node;
uint32_t hash;
hash = hash_32(hashval, ht->hashbits[idx]);
node = isc_mem_get(ht->mctx, sizeof(*node) + keysize);
*node = (isc_ht_node_t){
.keysize = keysize,
.hashval = hashval,
.next = ht->table[idx][hash],
.value = value,
};
memmove(node->key, key, keysize);
ht->count++;
ht->table[idx][hash] = node;
}
isc_result_t
isc_ht_add(isc_ht_t *ht, const unsigned char *key, uint32_t keysize,
isc_ht_add(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize,
void *value) {
isc_ht_node_t *node;
uint32_t hash;
uint32_t hashval;
REQUIRE(ISC_HT_VALID(ht));
REQUIRE(key != NULL && keysize > 0);
hash = isc_hash_function(key, keysize, true);
node = ht->table[hash & ht->mask];
while (node != NULL) {
if (keysize == node->keysize &&
memcmp(key, node->key, keysize) == 0)
{
return (ISC_R_EXISTS);
}
node = node->next;
if (rehashing_in_progress(ht)) {
/* Rehash in progress */
hashtable_rehash_one(ht);
} else if (hashtable_is_overcommited(ht)) {
/* Rehash requested */
maybe_rehash(ht, ht->count);
}
node = isc_mem_get(ht->mctx, offsetof(isc_ht_node_t, key) + keysize);
hashval = isc_hash32(key, keysize, ht->case_sensitive);
memmove(node->key, key, keysize);
node->keysize = keysize;
node->next = ht->table[hash & ht->mask];
node->value = value;
if (isc__ht_find(ht, key, keysize, hashval, ht->hindex) != NULL) {
return (ISC_R_EXISTS);
}
isc__ht_add(ht, key, keysize, hashval, ht->hindex, value);
ht->count++;
ht->table[hash & ht->mask] = node;
return (ISC_R_SUCCESS);
}
isc_result_t
isc_ht_find(const isc_ht_t *ht, const unsigned char *key, uint32_t keysize,
void **valuep) {
isc_ht_node_t *node;
static isc_ht_node_t *
isc__ht_find(const isc_ht_t *ht, const unsigned char *key,
const uint32_t keysize, const uint32_t hashval,
const uint8_t idx) {
uint32_t hash;
uint8_t findex = idx;
nexttable:
hash = hash_32(hashval, ht->hashbits[findex]);
for (isc_ht_node_t *node = ht->table[findex][hash]; node != NULL;
node = node->next)
{
if (isc__ht_node_match(node, hashval, key, keysize,
ht->case_sensitive))
{
return (node);
}
}
if (TRY_NEXTTABLE(findex, ht)) {
/*
* Rehashing in progress, check the other table
*/
findex = HT_NEXTTABLE(findex);
goto nexttable;
}
return (NULL);
}
isc_result_t
isc_ht_find(const isc_ht_t *ht, const unsigned char *key,
const uint32_t keysize, void **valuep) {
uint32_t hashval;
isc_ht_node_t *node;
REQUIRE(ISC_HT_VALID(ht));
REQUIRE(key != NULL && keysize > 0);
REQUIRE(valuep == NULL || *valuep == NULL);
hash = isc_hash_function(key, keysize, true);
node = ht->table[hash & ht->mask];
while (node != NULL) {
if (keysize == node->keysize &&
memcmp(key, node->key, keysize) == 0)
hashval = isc_hash32(key, keysize, ht->case_sensitive);
node = isc__ht_find(ht, key, keysize, hashval, ht->hindex);
if (node == NULL) {
return (ISC_R_NOTFOUND);
}
if (valuep != NULL) {
*valuep = node->value;
}
return (ISC_R_SUCCESS);
}
static isc_result_t
isc__ht_delete(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize,
const uint32_t hashval, const uint8_t idx) {
isc_ht_node_t *prev = NULL;
uint32_t hash;
hash = hash_32(hashval, ht->hashbits[idx]);
for (isc_ht_node_t *node = ht->table[idx][hash]; node != NULL;
prev = node, node = node->next)
{
if (isc__ht_node_match(node, hashval, key, keysize,
ht->case_sensitive))
{
if (valuep != NULL) {
*valuep = node->value;
if (prev == NULL) {
ht->table[idx][hash] = node->next;
} else {
prev->next = node->next;
}
isc_mem_put(ht->mctx, node,
sizeof(*node) + node->keysize);
ht->count--;
return (ISC_R_SUCCESS);
}
node = node->next;
}
return (ISC_R_NOTFOUND);
}
isc_result_t
isc_ht_delete(isc_ht_t *ht, const unsigned char *key, uint32_t keysize) {
isc_ht_node_t *node, *prev;
uint32_t hash;
isc_ht_delete(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize) {
uint32_t hashval;
uint8_t hindex;
isc_result_t result;
REQUIRE(ISC_HT_VALID(ht));
REQUIRE(key != NULL && keysize > 0);
prev = NULL;
hash = isc_hash_function(key, keysize, true);
node = ht->table[hash & ht->mask];
while (node != NULL) {
if (keysize == node->keysize &&
memcmp(key, node->key, keysize) == 0)
{
if (prev == NULL) {
ht->table[hash & ht->mask] = node->next;
} else {
prev->next = node->next;
}
isc_mem_put(ht->mctx, node,
offsetof(isc_ht_node_t, key) +
node->keysize);
ht->count--;
return (ISC_R_SUCCESS);
}
prev = node;
node = node->next;
if (rehashing_in_progress(ht)) {
/* Rehash in progress */
hashtable_rehash_one(ht);
}
return (ISC_R_NOTFOUND);
hindex = ht->hindex;
hashval = isc_hash32(key, keysize, ht->case_sensitive);
nexttable:
result = isc__ht_delete(ht, key, keysize, hashval, hindex);
if (result == ISC_R_NOTFOUND && TRY_NEXTTABLE(hindex, ht)) {
/*
* Rehashing in progress, check the other table
*/
hindex = HT_NEXTTABLE(hindex);
goto nexttable;
}
return (result);
}
void
@ -211,10 +492,10 @@ isc_ht_iter_create(isc_ht_t *ht, isc_ht_iter_t **itp) {
REQUIRE(itp != NULL && *itp == NULL);
it = isc_mem_get(ht->mctx, sizeof(isc_ht_iter_t));
it->ht = ht;
it->i = 0;
it->cur = NULL;
*it = (isc_ht_iter_t){
.ht = ht,
.hindex = ht->hindex,
};
*itp = it;
}
@ -229,25 +510,46 @@ isc_ht_iter_destroy(isc_ht_iter_t **itp) {
it = *itp;
*itp = NULL;
ht = it->ht;
isc_mem_put(ht->mctx, it, sizeof(isc_ht_iter_t));
isc_mem_put(ht->mctx, it, sizeof(*it));
}
isc_result_t
isc_ht_iter_first(isc_ht_iter_t *it) {
isc_ht_t *ht;
REQUIRE(it != NULL);
ht = it->ht;
it->hindex = ht->hindex;
it->i = 0;
while (it->i < it->ht->size && it->ht->table[it->i] == NULL) {
return (isc__ht_iter_next(it));
}
static isc_result_t
isc__ht_iter_next(isc_ht_iter_t *it) {
isc_ht_t *ht = it->ht;
while (it->i < ht->size[it->hindex] &&
ht->table[it->hindex][it->i] == NULL)
{
it->i++;
}
if (it->i == it->ht->size) {
return (ISC_R_NOMORE);
if (it->i < ht->size[it->hindex]) {
it->cur = ht->table[it->hindex][it->i];
return (ISC_R_SUCCESS);
}
it->cur = it->ht->table[it->i];
if (TRY_NEXTTABLE(it->hindex, ht)) {
it->hindex = HT_NEXTTABLE(it->hindex);
it->i = 0;
return (isc__ht_iter_next(it));
}
return (ISC_R_SUCCESS);
return (ISC_R_NOMORE);
}
isc_result_t
@ -256,60 +558,36 @@ isc_ht_iter_next(isc_ht_iter_t *it) {
REQUIRE(it->cur != NULL);
it->cur = it->cur->next;
if (it->cur == NULL) {
do {
it->i++;
} while (it->i < it->ht->size && it->ht->table[it->i] == NULL);
if (it->i >= it->ht->size) {
return (ISC_R_NOMORE);
}
it->cur = it->ht->table[it->i];
if (it->cur != NULL) {
return (ISC_R_SUCCESS);
}
return (ISC_R_SUCCESS);
it->i++;
return (isc__ht_iter_next(it));
}
isc_result_t
isc_ht_iter_delcurrent_next(isc_ht_iter_t *it) {
isc_result_t result = ISC_R_SUCCESS;
isc_ht_node_t *to_delete = NULL;
isc_ht_node_t *prev = NULL;
isc_ht_node_t *node = NULL;
uint32_t hash;
isc_ht_node_t *dnode = NULL;
uint8_t dindex;
isc_ht_t *ht;
isc_result_t dresult;
REQUIRE(it != NULL);
REQUIRE(it->cur != NULL);
to_delete = it->cur;
ht = it->ht;
dnode = it->cur;
dindex = it->hindex;
it->cur = it->cur->next;
if (it->cur == NULL) {
do {
it->i++;
} while (it->i < ht->size && ht->table[it->i] == NULL);
if (it->i >= ht->size) {
result = ISC_R_NOMORE;
} else {
it->cur = ht->table[it->i];
}
}
result = isc_ht_iter_next(it);
hash = isc_hash_function(to_delete->key, to_delete->keysize, true);
node = ht->table[hash & ht->mask];
while (node != to_delete) {
prev = node;
node = node->next;
INSIST(node != NULL);
}
if (prev == NULL) {
ht->table[hash & ht->mask] = node->next;
} else {
prev->next = node->next;
}
isc_mem_put(ht->mctx, node,
offsetof(isc_ht_node_t, key) + node->keysize);
ht->count--;
dresult = isc__ht_delete(ht, dnode->key, dnode->keysize, dnode->hashval,
dindex);
INSIST(dresult == ISC_R_SUCCESS);
return (result);
}
@ -334,8 +612,8 @@ isc_ht_iter_currentkey(isc_ht_iter_t *it, unsigned char **key,
*keysize = it->cur->keysize;
}
unsigned int
isc_ht_count(isc_ht_t *ht) {
size_t
isc_ht_count(const isc_ht_t *ht) {
REQUIRE(ISC_HT_VALID(ht));
return (ht->count);

View file

@ -13,8 +13,7 @@
/* ! \file */
#ifndef ISC_HT_H
#define ISC_HT_H 1
#pragma once
#include <inttypes.h>
#include <string.h>
@ -25,9 +24,15 @@
typedef struct isc_ht isc_ht_t;
typedef struct isc_ht_iter isc_ht_iter_t;
enum { ISC_HT_CASE_SENSITIVE = 0x00, ISC_HT_CASE_INSENSITIVE = 0x01 };
/*%
* Initialize hashtable at *htp, using memory context and size of (1<<bits)
*
* If 'options' contains ISC_HT_CASE_INSENSITIVE, then upper- and lower-case
* letters in key values will generate the same hash values; this can be used
* when the key for a hash table is a DNS name.
*
* Requires:
*\li 'htp' is not NULL and '*htp' is NULL.
*\li 'mctx' is a valid memory context.
@ -35,7 +40,8 @@ typedef struct isc_ht_iter isc_ht_iter_t;
*
*/
void
isc_ht_init(isc_ht_t **htp, isc_mem_t *mctx, uint8_t bits);
isc_ht_init(isc_ht_t **htp, isc_mem_t *mctx, uint8_t bits,
unsigned int options);
/*%
* Destroy hashtable, freeing everything
@ -52,6 +58,7 @@ isc_ht_destroy(isc_ht_t **htp);
*
* Requires:
*\li 'ht' is a valid hashtable
*\li write-lock
*
* Returns:
*\li #ISC_R_NOMEMORY -- not enough memory to create pool
@ -59,7 +66,7 @@ isc_ht_destroy(isc_ht_t **htp);
*\li #ISC_R_SUCCESS -- all is well.
*/
isc_result_t
isc_ht_add(isc_ht_t *ht, const unsigned char *key, uint32_t keysize,
isc_ht_add(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize,
void *value);
/*%
@ -70,27 +77,29 @@ isc_ht_add(isc_ht_t *ht, const unsigned char *key, uint32_t keysize,
*
* Requires:
* \li 'ht' is a valid hashtable
* \li read-lock
*
* Returns:
* \li #ISC_R_SUCCESS -- success
* \li #ISC_R_NOTFOUND -- key not found
*/
isc_result_t
isc_ht_find(const isc_ht_t *ht, const unsigned char *key, uint32_t keysize,
void **valuep);
isc_ht_find(const isc_ht_t *ht, const unsigned char *key,
const uint32_t keysize, void **valuep);
/*%
* Delete node from hashtable
*
* Requires:
*\li ht is a valid hashtable
*\li write-lock
*
* Returns:
*\li #ISC_R_NOTFOUND -- key not found
*\li #ISC_R_SUCCESS -- all is well
*/
isc_result_t
isc_ht_delete(isc_ht_t *ht, const unsigned char *key, uint32_t keysize);
isc_ht_delete(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize);
/*%
* Create an iterator for the hashtable; point '*itp' to it.
@ -178,6 +187,5 @@ isc_ht_iter_currentkey(isc_ht_iter_t *it, unsigned char **key, size_t *keysize);
* Requires:
*\li 'ht' is a valid hashtable
*/
unsigned int
isc_ht_count(isc_ht_t *ht);
#endif /* ifndef ISC_HT_H */
size_t
isc_ht_count(const isc_ht_t *ht);

View file

@ -479,6 +479,9 @@ isc_nm_tcpdnsconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
* 'cb'.
*/
#define ISC_NM_TASK_SLOW_OFFSET -2
#define ISC_NM_TASK_SLOW(i) (ISC_NM_TASK_SLOW_OFFSET - 1 - i)
void
isc_nm_task_enqueue(isc_nm_t *mgr, isc_task_t *task, int threadid);
/*%<

View file

@ -655,6 +655,7 @@ struct isc_nm {
isc_refcount_t references;
isc_mem_t *mctx;
int nworkers;
int nlisteners;
isc_mutex_t lock;
isc_condition_t wkstatecond;
isc_condition_t wkpausecond;

View file

@ -231,12 +231,12 @@ isc__nm_winsock_destroy(void) {
#endif /* WIN32 */
static void
isc__nm_threadpool_initialize(uint32_t workers) {
isc__nm_threadpool_initialize(uint32_t nworkers) {
char buf[11];
int r = uv_os_getenv("UV_THREADPOOL_SIZE", buf,
&(size_t){ sizeof(buf) });
if (r == UV_ENOENT) {
snprintf(buf, sizeof(buf), "%" PRIu32, workers);
snprintf(buf, sizeof(buf), "%" PRIu32, nworkers);
uv_os_setenv("UV_THREADPOOL_SIZE", buf);
}
}
@ -254,11 +254,11 @@ isc__nm_threadpool_initialize(uint32_t workers) {
#endif
void
isc__netmgr_create(isc_mem_t *mctx, uint32_t workers, isc_nm_t **netmgrp) {
isc__netmgr_create(isc_mem_t *mctx, uint32_t nworkers, isc_nm_t **netmgrp) {
isc_nm_t *mgr = NULL;
char name[32];
REQUIRE(workers > 0);
REQUIRE(nworkers > 0);
#ifdef MAXIMAL_UV_VERSION
if (uv_version() > MAXIMAL_UV_VERSION) {
@ -282,10 +282,13 @@ isc__netmgr_create(isc_mem_t *mctx, uint32_t workers, isc_nm_t **netmgrp) {
isc__nm_winsock_initialize();
#endif /* WIN32 */
isc__nm_threadpool_initialize(workers);
isc__nm_threadpool_initialize(nworkers);
mgr = isc_mem_get(mctx, sizeof(*mgr));
*mgr = (isc_nm_t){ .nworkers = workers };
*mgr = (isc_nm_t){
.nworkers = nworkers * 2,
.nlisteners = nworkers,
};
isc_mem_attach(mctx, &mgr->mctx);
isc_mutex_init(&mgr->lock);
@ -316,11 +319,12 @@ isc__netmgr_create(isc_mem_t *mctx, uint32_t workers, isc_nm_t **netmgrp) {
atomic_init(&mgr->keepalive, 30000);
atomic_init(&mgr->advertised, 30000);
isc_barrier_init(&mgr->pausing, workers);
isc_barrier_init(&mgr->resuming, workers);
isc_barrier_init(&mgr->pausing, mgr->nworkers);
isc_barrier_init(&mgr->resuming, mgr->nworkers);
mgr->workers = isc_mem_get(mctx, workers * sizeof(isc__networker_t));
for (size_t i = 0; i < workers; i++) {
mgr->workers = isc_mem_get(mctx,
mgr->nworkers * sizeof(isc__networker_t));
for (int i = 0; i < mgr->nworkers; i++) {
isc__networker_t *worker = &mgr->workers[i];
int r;
@ -354,7 +358,7 @@ isc__netmgr_create(isc_mem_t *mctx, uint32_t workers, isc_nm_t **netmgrp) {
mgr->workers_running++;
isc_thread_create(nm_thread, &mgr->workers[i], &worker->thread);
snprintf(name, sizeof(name), "isc-net-%04zu", i);
snprintf(name, sizeof(name), "isc-net-%04d", i);
isc_thread_setname(worker->thread, name);
}
@ -840,9 +844,15 @@ isc_nm_task_enqueue(isc_nm_t *nm, isc_task_t *task, int threadid) {
isc__networker_t *worker = NULL;
if (threadid == -1) {
tid = (int)isc_random_uniform(nm->nworkers);
tid = (int)isc_random_uniform(nm->nlisteners);
} else if (threadid == ISC_NM_TASK_SLOW_OFFSET) {
tid = nm->nlisteners +
(int)isc_random_uniform(nm->nworkers - nm->nlisteners);
} else if (threadid < ISC_NM_TASK_SLOW_OFFSET) {
tid = nm->nlisteners + (ISC_NM_TASK_SLOW(threadid) %
(nm->nworkers - nm->nlisteners));
} else {
tid = threadid % nm->nworkers;
tid = threadid % nm->nlisteners;
}
worker = &nm->workers[tid];

View file

@ -323,7 +323,7 @@ isc_nm_tcpconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
isc__nm_connectcb(sock, req, result, false);
} else {
isc__nmsocket_clearcb(sock);
sock->tid = isc_random_uniform(mgr->nworkers);
sock->tid = isc_random_uniform(mgr->nlisteners);
isc__nm_connectcb(sock, req, result, true);
}
atomic_store(&sock->closed, true);
@ -341,7 +341,7 @@ isc_nm_tcpconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
isc__nm_put_netievent_tcpconnect(mgr, ievent);
} else {
atomic_init(&sock->active, false);
sock->tid = isc_random_uniform(mgr->nworkers);
sock->tid = isc_random_uniform(mgr->nlisteners);
isc__nm_enqueue_ievent(&mgr->workers[sock->tid],
(isc__netievent_t *)ievent);
}
@ -445,7 +445,7 @@ isc_nm_listentcp(isc_nm_t *mgr, isc_sockaddr_t *iface,
#if defined(WIN32)
sock->nchildren = 1;
#else
sock->nchildren = mgr->nworkers;
sock->nchildren = mgr->nlisteners;
#endif
children_size = sock->nchildren * sizeof(sock->children[0]);
sock->children = isc_mem_get(mgr->mctx, children_size);

View file

@ -303,7 +303,7 @@ isc_nm_tcpdnsconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
isc__nm_put_netievent_tcpdnsconnect(mgr, ievent);
} else {
atomic_init(&sock->active, false);
sock->tid = isc_random_uniform(mgr->nworkers);
sock->tid = isc_random_uniform(mgr->nlisteners);
isc__nm_enqueue_ievent(&mgr->workers[sock->tid],
(isc__netievent_t *)ievent);
}
@ -410,7 +410,7 @@ isc_nm_listentcpdns(isc_nm_t *mgr, isc_sockaddr_t *iface,
#if defined(WIN32)
sock->nchildren = 1;
#else
sock->nchildren = mgr->nworkers;
sock->nchildren = mgr->nlisteners;
#endif
children_size = sock->nchildren * sizeof(sock->children[0]);
sock->children = isc_mem_get(mgr->mctx, children_size);

View file

@ -136,7 +136,7 @@ isc_nm_listenudp(isc_nm_t *mgr, isc_sockaddr_t *iface, isc_nm_recv_cb_t cb,
uv_os_sock_t fd = -1;
/*
* We are creating mgr->nworkers duplicated sockets, one
* We are creating mgr->nlisteners duplicated sockets, one
* socket for each worker thread.
*/
sock = isc_mem_get(mgr->mctx, sizeof(isc_nmsocket_t));
@ -146,7 +146,7 @@ isc_nm_listenudp(isc_nm_t *mgr, isc_sockaddr_t *iface, isc_nm_recv_cb_t cb,
#if defined(WIN32)
sock->nchildren = 1;
#else
sock->nchildren = mgr->nworkers;
sock->nchildren = mgr->nlisteners;
#endif
children_size = sock->nchildren * sizeof(sock->children[0]);
@ -847,7 +847,7 @@ isc_nm_udpconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
isc__nm_put_netievent_udpconnect(mgr, event);
} else {
atomic_init(&sock->active, false);
sock->tid = isc_random_uniform(mgr->nworkers);
sock->tid = isc_random_uniform(mgr->nlisteners);
isc__nm_enqueue_ievent(&mgr->workers[sock->tid],
(isc__netievent_t *)event);
}

View file

@ -61,7 +61,7 @@ test_ht_full(int bits, uintptr_t count) {
isc_result_t result;
uintptr_t i;
isc_ht_init(&ht, test_mctx, bits);
isc_ht_init(&ht, test_mctx, bits, ISC_HT_CASE_SENSITIVE);
assert_non_null(ht);
for (i = 1; i < count; i++) {
@ -206,7 +206,7 @@ test_ht_iterator() {
unsigned char key[16];
size_t tksize;
isc_ht_init(&ht, test_mctx, 16);
isc_ht_init(&ht, test_mctx, 16, ISC_HT_CASE_SENSITIVE);
assert_non_null(ht);
for (i = 1; i <= count; i++) {
/*
@ -333,9 +333,62 @@ isc_ht_iterator_test(void **state) {
test_ht_iterator();
}
static void
isc_ht_case(void **state) {
UNUSED(state);
isc_ht_t *ht = NULL;
void *f = NULL;
isc_result_t result = ISC_R_UNSET;
unsigned char lower[16] = { "test case" };
unsigned char same[16] = { "test case" };
unsigned char upper[16] = { "TEST CASE" };
unsigned char mixed[16] = { "tEsT CaSe" };
isc_ht_init(&ht, test_mctx, 8, ISC_HT_CASE_SENSITIVE);
assert_non_null(ht);
result = isc_ht_add(ht, lower, 16, (void *)lower);
assert_int_equal(result, ISC_R_SUCCESS);
result = isc_ht_add(ht, same, 16, (void *)same);
assert_int_equal(result, ISC_R_EXISTS);
result = isc_ht_add(ht, upper, 16, (void *)upper);
assert_int_equal(result, ISC_R_SUCCESS);
result = isc_ht_find(ht, mixed, 16, &f);
assert_int_equal(result, ISC_R_NOTFOUND);
assert_null(f);
isc_ht_destroy(&ht);
assert_null(ht);
isc_ht_init(&ht, test_mctx, 8, ISC_HT_CASE_INSENSITIVE);
assert_non_null(ht);
result = isc_ht_add(ht, lower, 16, (void *)lower);
assert_int_equal(result, ISC_R_SUCCESS);
result = isc_ht_add(ht, same, 16, (void *)same);
assert_int_equal(result, ISC_R_EXISTS);
result = isc_ht_add(ht, upper, 16, (void *)upper);
assert_int_equal(result, ISC_R_EXISTS);
result = isc_ht_find(ht, mixed, 16, &f);
assert_int_equal(result, ISC_R_SUCCESS);
assert_ptr_equal(f, &lower);
isc_ht_destroy(&ht);
assert_null(ht);
}
int
main(void) {
const struct CMUnitTest tests[] = {
cmocka_unit_test(isc_ht_case),
cmocka_unit_test(isc_ht_20),
cmocka_unit_test(isc_ht_8),
cmocka_unit_test(isc_ht_1),

View file

@ -455,10 +455,10 @@ static void
query_addnxrrsetnsec(query_ctx_t *qctx);
static isc_result_t
query_nxdomain(query_ctx_t *qctx, bool empty_wild);
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);
@ -6095,6 +6095,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;
@ -7345,8 +7352,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");
@ -7416,16 +7422,16 @@ root_key_sentinel:
return (query_nodata(qctx, DNS_R_NXRRSET));
case DNS_R_EMPTYWILD:
return (query_nxdomain(qctx, true));
return (query_nxdomain(qctx, DNS_R_EMPTYWILD));
case DNS_R_NXDOMAIN:
return (query_nxdomain(qctx, false));
return (query_nxdomain(qctx, DNS_R_NXDOMAIN));
case DNS_R_COVERINGNSEC:
return (query_coveringnsec(qctx));
case DNS_R_NCACHENXDOMAIN:
result = query_redirect(qctx);
result = query_redirect(qctx, result);
if (result != ISC_R_COMPLETE) {
return (result);
}
@ -9243,10 +9249,10 @@ query_addnxrrsetnsec(query_ctx_t *qctx) {
* Handle NXDOMAIN and empty wildcard responses.
*/
static isc_result_t
query_nxdomain(query_ctx_t *qctx, bool empty_wild) {
query_nxdomain(query_ctx_t *qctx, isc_result_t result) {
dns_section_t section;
uint32_t ttl;
isc_result_t result;
bool empty_wild = (result == DNS_R_EMPTYWILD);
CCTRACE(ISC_LOG_DEBUG(3), "query_nxdomain");
@ -9255,7 +9261,7 @@ query_nxdomain(query_ctx_t *qctx, bool empty_wild) {
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);
}
@ -9343,7 +9349,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");
@ -9384,7 +9390,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_copynf(qctx->fname,
qctx->client->query.redirect.fname);
qctx->client->query.redirect.authoritative =
@ -10005,7 +10011,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;