bind/bind-9.16-CVE-2023-4408.patch
Petr Menšík deeca182e3 Prevent increased CPU load on large DNS messages
6315.	[security]	Speed up parsing of DNS messages with many different
			names. (CVE-2023-4408) [GL #4234]
6321.	[security]	Change 6315 inadvertently introduced regressions that
			could cause named to crash. [GL #4234]
6343.	[bug]		Fix case insensitive setting for isc_ht hashtable.

Resolves: RHEL-25342
; Resolves: CVE-2023-4408
2024-03-26 12:05:22 +01:00

1736 lines
49 KiB
Diff

From 8664429a745e73854486e1222d1f3a2a86bd3f7f Mon Sep 17 00:00:00 2001
From: Petr Mensik <pemensik@redhat.com>
Date: Mon, 12 Feb 2024 19:34:45 +0100
Subject: [PATCH] Fixes of CVE-2023-4408
6315. [security] Speed up parsing of DNS messages with many different
names. (CVE-2023-4408) [GL #4234]
6321. [security] Change 6315 inadvertently introduced regressions that
could cause named to crash. [GL #4234]
6343. [bug] Fix case insensitive setting for isc_ht hashtable.
[GL #4568]
---
bin/plugins/filter-aaaa.c | 2 +-
lib/dns/catz.c | 8 +-
lib/dns/include/dns/message.h | 38 ---
lib/dns/include/dns/name.h | 37 ++-
lib/dns/message.c | 387 +++++++++++++++---------
lib/dns/name.c | 1 +
lib/dns/rpz.c | 4 +-
lib/dns/win32/libdns.def.in | 2 -
lib/isc/ht.c | 550 +++++++++++++++++++++++++---------
lib/isc/include/isc/ht.h | 28 +-
lib/isc/tests/ht_test.c | 4 +-
11 files changed, 710 insertions(+), 351 deletions(-)
diff --git a/bin/plugins/filter-aaaa.c b/bin/plugins/filter-aaaa.c
index 1db3ca2..f0327b6 100644
--- a/bin/plugins/filter-aaaa.c
+++ b/bin/plugins/filter-aaaa.c
@@ -355,7 +355,7 @@ plugin_register(const char *parameters, const void *cfg, const char *cfg_file,
}
isc_mempool_create(mctx, sizeof(filter_data_t), &inst->datapool);
- CHECK(isc_ht_init(&inst->ht, mctx, 16));
+ CHECK(isc_ht_init(&inst->ht, mctx, 16, ISC_HT_CASE_SENSITIVE));
isc_mutex_init(&inst->hlock);
/*
diff --git a/lib/dns/catz.c b/lib/dns/catz.c
index 691c4aa..e7fac02 100644
--- a/lib/dns/catz.c
+++ b/lib/dns/catz.c
@@ -404,12 +404,12 @@ dns_catz_zones_merge(dns_catz_zone_t *target, dns_catz_zone_t *newzone) {
dns_name_format(&target->name, czname, DNS_NAME_FORMATSIZE);
- result = isc_ht_init(&toadd, target->catzs->mctx, 16);
+ result = isc_ht_init(&toadd, target->catzs->mctx, 16, ISC_HT_CASE_SENSITIVE);
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
- result = isc_ht_init(&tomod, target->catzs->mctx, 16);
+ result = isc_ht_init(&tomod, target->catzs->mctx, 16, ISC_HT_CASE_SENSITIVE);
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
@@ -623,7 +623,7 @@ dns_catz_new_zones(dns_catz_zones_t **catzsp, dns_catz_zonemodmethods_t *zmm,
isc_refcount_init(&new_zones->refs, 1);
- result = isc_ht_init(&new_zones->zones, mctx, 4);
+ result = isc_ht_init(&new_zones->zones, mctx, 4, ISC_HT_CASE_SENSITIVE);
if (result != ISC_R_SUCCESS) {
goto cleanup_refcount;
}
@@ -679,7 +679,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);
- result = isc_ht_init(&new_zone->entries, catzs->mctx, 4);
+ result = isc_ht_init(&new_zone->entries, catzs->mctx, 4, ISC_HT_CASE_SENSITIVE);
if (result != ISC_R_SUCCESS) {
goto cleanup_name;
}
diff --git a/lib/dns/include/dns/message.h b/lib/dns/include/dns/message.h
index 1f406e5..68c13ee 100644
--- a/lib/dns/include/dns/message.h
+++ b/lib/dns/include/dns/message.h
@@ -799,44 +799,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);
diff --git a/lib/dns/include/dns/name.h b/lib/dns/include/dns/name.h
index 75bc020..4058b56 100644
--- a/lib/dns/include/dns/name.h
+++ b/lib/dns/include/dns/name.h
@@ -67,6 +67,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. */
@@ -110,6 +111,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')
@@ -165,30 +167,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 \
}
/*%
@@ -1355,6 +1351,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) \
diff --git a/lib/dns/message.c b/lib/dns/message.c
index e4f4338..1993b2e 100644
--- a/lib/dns/message.c
+++ b/lib/dns/message.c
@@ -20,6 +20,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?) */
@@ -503,9 +505,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);
@@ -794,6 +798,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) {
@@ -813,28 +829,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;
-
- 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);
- }
- }
+typedef struct __attribute__((__packed__)) rds_key {
+ dns_rdataclass_t rdclass;
+ dns_rdatatype_t type;
+ dns_rdatatype_t covers;
+} rds_key_t;
- return (ISC_R_NOTFOUND);
+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);
+ }
+ 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
@@ -962,6 +976,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) {
@@ -971,13 +997,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;
@@ -998,13 +1030,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.
@@ -1016,19 +1054,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:
+ ISC_UNREACHABLE();
}
+ free_name = false;
+
/*
* Get type and class.
*/
@@ -1058,14 +1102,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.
*/
@@ -1079,6 +1115,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
@@ -1087,32 +1124,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);
}
@@ -1192,17 +1268,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;
@@ -1210,10 +1293,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);
@@ -1253,8 +1336,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;
}
@@ -1360,10 +1443,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)) {
if (rdatalen != 0) {
@@ -1446,33 +1525,70 @@ 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:
+ ISC_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 ||
+ 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
@@ -1482,63 +1598,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 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);
- INSIST(first != NULL);
- if (dns_rdata_compare(rdata, first) != 0) {
- DO_ERROR(DNS_R_FORMERR);
- }
+ if (ISC_LIST_EMPTY(name->list)) {
+ result = ISC_R_SUCCESS;
+ goto skip_rds_check;
}
- }
- if (result == ISC_R_NOTFOUND) {
- rdataset = isc_mempool_get(msg->rdspool);
- if (rdataset == NULL) {
- result = ISC_R_NOMEMORY;
- goto cleanup;
- }
- free_rdataset = true;
+ if (name->ht == NULL) {
+ isc_ht_init(&name->ht, msg->mctx, 1,
+ ISC_HT_CASE_SENSITIVE);
+ free_ht = true;
- rdatalist = newrdatalist(msg);
- if (rdatalist == NULL) {
- result = ISC_R_NOMEMORY;
- goto cleanup;
+ 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);
- rdatalist->type = rdtype;
- rdatalist->covers = covers;
- rdatalist->rdclass = rdclass;
- rdatalist->ttl = ttl;
+ /*
+ * 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;
- dns_rdataset_init(rdataset);
- RUNTIME_CHECK(
- dns_rdatalist_tordataset(rdatalist, rdataset) ==
- ISC_R_SUCCESS);
- dns_rdataset_setownercase(rdataset, name);
+ if (!dns_rdatatype_issingleton(rdtype)) {
+ break;
+ }
- if (!isedns && !istsig && !issigzero) {
+ dns_rdatalist_fromrdataset(rdataset,
+ &rdatalist);
+ dns_rdata_t *first =
+ ISC_LIST_HEAD(rdatalist->rdata);
+ INSIST(first != NULL);
+ if (dns_rdata_compare(rdata, first) != 0) {
+ DO_ERROR(DNS_R_FORMERR);
+ }
+ break;
+ case ISC_R_SUCCESS:
ISC_LIST_APPEND(name->list, rdataset, link);
- free_rdataset = false;
+ break;
+ default:
+ ISC_UNREACHABLE();
}
}
@@ -1573,8 +1697,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);
@@ -1585,8 +1707,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;
@@ -1596,22 +1716,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);
}
/*
@@ -1629,16 +1744,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);
@@ -2432,7 +2555,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;
/*
@@ -2479,22 +2602,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) {
@@ -2586,6 +2693,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.
*/
diff --git a/lib/dns/name.c b/lib/dns/name.c
index 6ca311d..cc1b872 100644
--- a/lib/dns/name.c
+++ b/lib/dns/name.c
@@ -186,6 +186,7 @@ dns_name_invalidate(dns_name_t *name) {
name->offsets = NULL;
name->buffer = NULL;
ISC_LINK_INIT(name, link);
+ INSIST(name->ht == NULL);
}
bool
diff --git a/lib/dns/rpz.c b/lib/dns/rpz.c
index d3baa71..fa93358 100644
--- a/lib/dns/rpz.c
+++ b/lib/dns/rpz.c
@@ -1538,7 +1538,7 @@ dns_rpz_new_zone(dns_rpz_zones_t *rpzs, dns_rpz_zone_t **rpzp) {
* simplifies update_from_db
*/
- result = isc_ht_init(&zone->nodes, rpzs->mctx, 1);
+ result = isc_ht_init(&zone->nodes, rpzs->mctx, 1, ISC_HT_CASE_SENSITIVE);
if (result != ISC_R_SUCCESS) {
goto cleanup_ht;
}
@@ -1721,7 +1721,7 @@ setup_update(dns_rpz_zone_t *rpz) {
ISC_LOG_DEBUG(1), "rpz: %s: using hashtable size %d",
domain, hashsize);
- result = isc_ht_init(&rpz->newnodes, rpz->rpzs->mctx, hashsize);
+ result = isc_ht_init(&rpz->newnodes, rpz->rpzs->mctx, hashsize, ISC_HT_CASE_SENSITIVE);
if (result != ISC_R_SUCCESS) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
diff --git a/lib/dns/win32/libdns.def.in b/lib/dns/win32/libdns.def.in
index 31f5111..1c2ea3e 100644
--- a/lib/dns/win32/libdns.def.in
+++ b/lib/dns/win32/libdns.def.in
@@ -534,7 +534,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
@@ -553,7 +552,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
diff --git a/lib/isc/ht.c b/lib/isc/ht.c
index 82f8ac8..f88a32c 100644
--- a/lib/isc/ht.c
+++ b/lib/isc/ht.c
@@ -25,51 +25,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;
+}
+
isc_result_t
-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;
@@ -83,122 +306,183 @@ isc_ht_destroy(isc_ht_t **htp) {
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 (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));
}
-isc_result_t
-isc_ht_add(isc_ht_t *ht, const unsigned char *key, uint32_t keysize,
- void *value) {
+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, const uint32_t keysize,
+ void *value) {
+ 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);
}
+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, uint32_t keysize,
- void **valuep) {
+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;
- uint32_t hash;
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) {
- if (valuep != NULL) {
- *valuep = node->value;
- }
- return (ISC_R_SUCCESS);
- }
- node = node->next;
+ 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);
}
- return (ISC_R_NOTFOUND);
+ if (valuep != NULL) {
+ *valuep = node->value;
+ }
+ return (ISC_R_SUCCESS);
}
-isc_result_t
-isc_ht_delete(isc_ht_t *ht, const unsigned char *key, uint32_t keysize) {
- isc_ht_node_t *node, *prev;
+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;
- REQUIRE(ISC_HT_VALID(ht));
- REQUIRE(key != NULL && keysize > 0);
+ hash = hash_32(hashval, ht->hashbits[idx]);
- 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) {
+ 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 (prev == NULL) {
- ht->table[hash & ht->mask] = node->next;
+ ht->table[idx][hash] = node->next;
} else {
prev->next = node->next;
}
isc_mem_put(ht->mctx, node,
- offsetof(isc_ht_node_t, key) +
- node->keysize);
+ sizeof(*node) + node->keysize);
ht->count--;
return (ISC_R_SUCCESS);
}
-
- prev = node;
- node = node->next;
}
return (ISC_R_NOTFOUND);
}
+isc_result_t
+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);
+
+ if (rehashing_in_progress(ht)) {
+ /* Rehash in progress */
+ hashtable_rehash_one(ht);
+ }
+
+ 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);
+}
+
isc_result_t
isc_ht_iter_create(isc_ht_t *ht, isc_ht_iter_t **itp) {
isc_ht_iter_t *it;
@@ -208,9 +492,10 @@ isc_ht_iter_create(isc_ht_t *ht, isc_ht_iter_t **itp) {
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;
@@ -227,25 +512,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
@@ -254,60 +560,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;
- 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];
- }
- }
+ ht = it->ht;
+ dnode = it->cur;
+ dindex = it->hindex;
- 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);
- }
+ result = isc_ht_iter_next(it);
- 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);
}
@@ -332,8 +614,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);
diff --git a/lib/isc/include/isc/ht.h b/lib/isc/include/isc/ht.h
index 9d5ab82..46cd32d 100644
--- a/lib/isc/include/isc/ht.h
+++ b/lib/isc/include/isc/ht.h
@@ -11,8 +11,7 @@
/* ! \file */
-#ifndef ISC_HT_H
-#define ISC_HT_H 1
+#pragma once
#include <inttypes.h>
#include <string.h>
@@ -23,9 +22,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.
@@ -36,7 +41,8 @@ typedef struct isc_ht_iter isc_ht_iter_t;
*\li #ISC_R_SUCCESS -- all is well.
*/
isc_result_t
-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
@@ -53,6 +59,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
@@ -60,7 +67,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);
/*%
@@ -71,27 +78,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.
@@ -179,6 +188,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);
diff --git a/lib/isc/tests/ht_test.c b/lib/isc/tests/ht_test.c
index 6a8e319..74d95c1 100644
--- a/lib/isc/tests/ht_test.c
+++ b/lib/isc/tests/ht_test.c
@@ -59,7 +59,7 @@ test_ht_full(int bits, uintptr_t count) {
isc_result_t result;
uintptr_t i;
- result = isc_ht_init(&ht, test_mctx, bits);
+ result = isc_ht_init(&ht, test_mctx, bits, ISC_HT_CASE_SENSITIVE);
assert_int_equal(result, ISC_R_SUCCESS);
assert_non_null(ht);
@@ -205,7 +205,7 @@ test_ht_iterator() {
unsigned char key[16];
size_t tksize;
- result = isc_ht_init(&ht, test_mctx, 16);
+ result = isc_ht_init(&ht, test_mctx, 16, ISC_HT_CASE_SENSITIVE);
assert_int_equal(result, ISC_R_SUCCESS);
assert_non_null(ht);
for (i = 1; i <= count; i++) {
--
2.43.0