chg: dev: Add a refcount to the vecheaders

This MR changes the way the ownership of the vecheaders is tracked. Before this MR, the ownership of the vecheader was implicitely tracked through a mix of the refcount on the node owning the header, the external refcount of the same node and the version. This has some adverse consequences in terms of contention, such as that querying A and AAAA glue hits the same refcount.

This MR adds a refcount to the vecheader itself, allowing it to exist independently of the node it is contained in. On its own, this would create a cycle, where the node has a reference to the header, which has a reference to the heap, which in turn has a reference to the node.

To break this cycle, this MR also moves from an "intrusive" heap, to a more traditional one where pointers to the node and vecheader in the heap are stored in a hashmap.

Merge branch 'alessio/vecheader-refs' into 'main'

See merge request isc-projects/bind9!11397
This commit is contained in:
Alessio Podda 2026-03-31 15:17:43 +00:00
commit a743ff1e44
12 changed files with 784 additions and 387 deletions

View file

@ -276,12 +276,13 @@ findnsec3node(dns_db_t *db, const dns_name_t *name, bool create,
}
static isc_result_t
setsigningtime(dns_db_t *db, dns_rdataset_t *rdataset, isc_stdtime_t resign) {
setsigningtime(dns_db_t *db, dns_dbnode_t *node, dns_rdataset_t *rdataset,
isc_stdtime_t resign) {
sampledb_t *sampledb = (sampledb_t *)db;
REQUIRE(VALID_SAMPLEDB(sampledb));
return dns_db_setsigningtime(sampledb->db, rdataset, resign);
return dns_db_setsigningtime(sampledb->db, node, rdataset, resign);
}
static isc_result_t

View file

@ -875,10 +875,11 @@ dns_db_getsize(dns_db_t *db, dns_dbversion_t *version, uint64_t *records,
}
isc_result_t
dns_db_setsigningtime(dns_db_t *db, dns_rdataset_t *rdataset,
isc_stdtime_t resign) {
dns_db_setsigningtime(dns_db_t *db, dns_dbnode_t *node,
dns_rdataset_t *rdataset, isc_stdtime_t resign) {
if (db->methods->setsigningtime != NULL) {
return (db->methods->setsigningtime)(db, rdataset, resign);
return (db->methods->setsigningtime)(db, node, rdataset,
resign);
}
return ISC_R_NOTIMPLEMENTED;
}
@ -1043,7 +1044,8 @@ dns_db_setgluecachestats(dns_db_t *db, isc_stats_t *stats) {
}
isc_result_t
dns_db_addglue(dns_db_t *db, dns_dbversion_t *version, dns_rdataset_t *rdataset,
dns_db_addglue(dns_db_t *db, dns_dbversion_t *version,
const dns_name_t *owner_name, dns_rdataset_t *rdataset,
dns_message_t *msg) {
REQUIRE(DNS_DB_VALID(db));
REQUIRE((db->attributes & DNS_DBATTR_CACHE) == 0);
@ -1052,7 +1054,7 @@ dns_db_addglue(dns_db_t *db, dns_dbversion_t *version, dns_rdataset_t *rdataset,
REQUIRE(rdataset->type == dns_rdatatype_ns);
if (db->methods->addglue != NULL) {
(db->methods->addglue)(db, version, rdataset, msg);
(db->methods->addglue)(db, version, owner_name, rdataset, msg);
return ISC_R_SUCCESS;
}

View file

@ -109,7 +109,7 @@ struct dns_gluelist {
typedef struct dns_glue_additionaldata_ctx {
dns_db_t *db;
dns_dbversion_t *version;
dns_dbnode_t *node;
const dns_name_t *owner_name;
dns_glue_t *glue;
} dns_glue_additionaldata_ctx_t;

View file

@ -264,7 +264,7 @@ update_rdataset(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
if (is_resign) {
isc_stdtime_t resign;
resign = dns_rdataset_minresign(&ardataset);
dns_db_setsigningtime(db, &ardataset, resign);
dns_db_setsigningtime(db, node, &ardataset, resign);
}
cleanup:

View file

@ -139,8 +139,9 @@ typedef struct dns_db_methods {
isc_result_t (*findnsec3node)(dns_db_t *db, const dns_name_t *name,
bool create,
dns_dbnode_t **nodep DNS__DB_FLARG);
isc_result_t (*setsigningtime)(dns_db_t *db, dns_rdataset_t *rdataset,
isc_stdtime_t resign);
isc_result_t (*setsigningtime)(dns_db_t *db, dns_dbnode_t *node,
dns_rdataset_t *rdataset,
isc_stdtime_t resign);
isc_result_t (*getsigningtime)(dns_db_t *db, isc_stdtime_t *resign,
dns_name_t *name,
dns_typepair_t *typepair);
@ -166,7 +167,8 @@ typedef struct dns_db_methods {
isc_result_t (*getservestalerefresh)(dns_db_t *db, uint32_t *interval);
isc_result_t (*setgluecachestats)(dns_db_t *db, isc_stats_t *stats);
void (*addglue)(dns_db_t *db, dns_dbversion_t *version,
dns_rdataset_t *rdataset, dns_message_t *msg);
const dns_name_t *owner_name, dns_rdataset_t *rdataset,
dns_message_t *msg);
void (*setmaxrrperset)(dns_db_t *db, uint32_t value);
void (*setmaxtypepername)(dns_db_t *db, uint32_t value);
isc_result_t (*getzoneversion)(dns_db_t *db, isc_buffer_t *b);
@ -1572,8 +1574,8 @@ dns__db_findnsec3node(dns_db_t *db, const dns_name_t *name, bool create,
*/
isc_result_t
dns_db_setsigningtime(dns_db_t *db, dns_rdataset_t *rdataset,
isc_stdtime_t resign);
dns_db_setsigningtime(dns_db_t *db, dns_dbnode_t *node,
dns_rdataset_t *rdataset, isc_stdtime_t resign);
/*%<
* Sets the re-signing time associated with 'rdataset' to 'resign'.
*
@ -1742,7 +1744,8 @@ dns_db_setgluecachestats(dns_db_t *db, isc_stats_t *stats);
*/
isc_result_t
dns_db_addglue(dns_db_t *db, dns_dbversion_t *version, dns_rdataset_t *rdataset,
dns_db_addglue(dns_db_t *db, dns_dbversion_t *version,
const dns_name_t *owner_name, dns_rdataset_t *rdataset,
dns_message_t *msg);
/*%<
* Add glue records for rdataset to the additional section of message in
@ -1751,6 +1754,7 @@ dns_db_addglue(dns_db_t *db, dns_dbversion_t *version, dns_rdataset_t *rdataset,
* Requires:
* \li 'db' is a database with 'zone' semantics.
* \li 'version' is the DB version.
* \li 'owner_name' name of the rdataset.
* \li 'rdataset' is a valid NS rdataset.
* \li 'msg' is the DNS message to which the glue should be added.
*

View file

@ -208,8 +208,6 @@ struct dns_rdataset {
* methods; see comments in rdatavec.c for details.)
*/
struct {
struct dns_db *db;
dns_dbnode_t *node;
dns_vecheader_t *header;
rdatavec_iter_t iter;
} vec;

View file

@ -81,35 +81,31 @@ struct dns_vecheader {
_Atomic(uint16_t) attributes;
_Atomic(dns_trust_t) trust;
dns_typepair_t typepair;
isc_refcount_t references;
/*%
* Locked by the heap lock. Can't be packed together with other fields
* since it is protected by a different lock.
* Memory context for this header.
*/
unsigned int heap_index;
isc_mem_t *mctx;
/*%
* Locked by the owning node's lock.
*/
uint32_t serial;
dns_ttl_t ttl;
dns_typepair_t typepair;
uint32_t serial;
dns_ttl_t ttl;
/*
* resigning (zone).
*/
isc_stdtime_t resign;
uint16_t resign_lsb : 1;
int64_t resign;
/*%
* Link to the other versions of this rdataset.
*/
ISC_SLINK(dns_vecheader_t) next_header;
/*%
* The database node objects containing this rdataset, if any.
*/
dns_dbnode_t *node;
/*%
* Cached glue records for an rdataset of type NS (zone only).
*/
@ -240,24 +236,10 @@ dns_vecheader_setownercase(dns_vecheader_t *header, const dns_name_t *name);
* \li 'name' is a valid name.
*/
void
dns_vecheader_reset(dns_vecheader_t *h, dns_dbnode_t *node);
/*%<
* Reset an rdatavec header 'h' so it can be used to store data in
* database node 'node'.
*/
dns_vecheader_t *
dns_vecheader_new(isc_mem_t *mctx, dns_dbnode_t *node);
dns_vecheader_new(isc_mem_t *mctx);
/*%<
* Allocate memory for an rdatavec header and initialize it for use
* in database node 'node'.
*/
void
dns_vecheader_destroy(dns_vecheader_t **headerp);
/*%<
* Free all memory associated with '*headerp'.
* Allocate memory for an rdatavec header and initialize it.
*/
dns_vectop_t *
@ -272,3 +254,8 @@ dns_vectop_destroy(isc_mem_t *mctx, dns_vectop_t **topp);
/*%<
* Free all memory associated with '*vectopp'.
*/
/*
* Reference counting for dns_vecheader_t
*/
ISC_REFCOUNT_DECL(dns_vecheader);

File diff suppressed because it is too large Load diff

View file

@ -21,6 +21,7 @@
#include <isc/ascii.h>
#include <isc/atomic.h>
#include <isc/mem.h>
#include <isc/refcount.h>
#include <isc/region.h>
#include <isc/result.h>
#include <isc/string.h>
@ -133,6 +134,8 @@ newvec(dns_rdataset_t *rdataset, isc_mem_t *mctx, isc_region_t *region,
.next_header = ISC_SLINK_INITIALIZER,
.trust = rdataset->trust,
.ttl = rdataset->ttl,
.references = ISC_REFCOUNT_INITIALIZER(1),
.mctx = isc_mem_ref(mctx),
};
region->base = (unsigned char *)header;
@ -356,11 +359,16 @@ dns_rdatavec_fromrdataset(dns_rdataset_t *rdataset, isc_mem_t *mctx,
rdataset->covers);
}
/*
* Reset the vecheader content, but keep the refcount and mctx.
*/
*new = (dns_vecheader_t){
.next_header = ISC_SLINK_INITIALIZER,
.typepair = typepair,
.trust = rdataset->trust,
.ttl = rdataset->ttl,
.references = atomic_load_acquire(&new->references),
.mctx = new->mctx,
};
}
@ -570,23 +578,31 @@ dns_rdatavec_merge(dns_vecheader_t *oheader, dns_vecheader_t *nheader,
CLEANUP(ISC_R_NOSPACE);
}
/* Allocate the target buffer and copy the new vec's header */
/*
* Allocate the target buffer and initialize the header.
* Preserve the case of the old header, but the rest from the
* new header.
*/
unsigned char *tstart = isc_mem_get(mctx, tlength);
dns_vecheader_t *as_header = (dns_vecheader_t *)tstart;
/*
* Preserve the case of the old header, but the rest from the new
* header
*/
memmove(tstart, nheader, header_size(nheader));
memmove(as_header->upper, oheader->upper, sizeof(oheader->upper));
uint16_t case_attrs = DNS_VECHEADER_GETATTR(
uint16_t attrs = DNS_VECHEADER_GETATTR(
oheader,
DNS_VECHEADERATTR_CASESET | DNS_VECHEADERATTR_CASEFULLYLOWER);
DNS_VECHEADER_CLRATTR(as_header,
DNS_VECHEADERATTR_CASESET |
DNS_VECHEADERATTR_CASEFULLYLOWER);
DNS_VECHEADER_SETATTR(as_header, case_attrs);
if (RESIGN(nheader)) {
attrs |= DNS_VECHEADERATTR_RESIGN;
}
*as_header = (dns_vecheader_t){
.typepair = nheader->typepair,
.mctx = isc_mem_ref(mctx),
.serial = nheader->serial,
.ttl = nheader->ttl,
.resign = nheader->resign,
.next_header = ISC_SLINK_INITIALIZER,
};
isc_refcount_init(&as_header->references, 1);
atomic_init(&as_header->attributes, attrs);
atomic_init(&as_header->trust, atomic_load_acquire(&nheader->trust));
memmove(as_header->upper, oheader->upper, sizeof(oheader->upper));
tcurrent = tstart + header_size(nheader);
@ -739,7 +755,21 @@ dns_rdatavec_subtract(dns_vecheader_t *oheader, dns_vecheader_t *sheader,
* Allocate the target buffer and copy the old vec's header.
*/
tstart = isc_mem_get(mctx, tlength);
memmove(tstart, oheader, header_size(oheader));
dns_vecheader_t *as_header = (dns_vecheader_t *)tstart;
uint16_t attrs = RESIGN(oheader) ? DNS_VECHEADERATTR_RESIGN : 0;
*as_header = (dns_vecheader_t){
.typepair = oheader->typepair,
.mctx = isc_mem_ref(mctx),
.serial = oheader->serial,
.ttl = oheader->ttl,
.resign = oheader->resign,
.next_header = ISC_SLINK_INITIALIZER,
};
isc_refcount_init(&as_header->references, 1);
atomic_init(&as_header->attributes, attrs);
atomic_init(&as_header->trust, atomic_load_acquire(&oheader->trust));
memmove(as_header->upper, oheader->upper, sizeof(oheader->upper));
tcurrent = tstart + header_size(oheader);
/*
@ -790,48 +820,18 @@ dns_vecheader_setownercase(dns_vecheader_t *header, const dns_name_t *name) {
DNS_VECHEADER_SETATTR(header, DNS_VECHEADERATTR_CASESET);
}
void
dns_vecheader_reset(dns_vecheader_t *h, dns_dbnode_t *node) {
h->heap_index = 0;
h->node = node;
atomic_init(&h->attributes, 0);
STATIC_ASSERT(sizeof(h->attributes) == 2,
"The .attributes field of dns_vecheader_t needs to be "
"16-bit int type exactly.");
}
dns_vecheader_t *
dns_vecheader_new(isc_mem_t *mctx, dns_dbnode_t *node) {
dns_vecheader_new(isc_mem_t *mctx) {
dns_vecheader_t *h = NULL;
h = isc_mem_get(mctx, sizeof(*h));
*h = (dns_vecheader_t){
.node = node,
.references = ISC_REFCOUNT_INITIALIZER(1),
.mctx = isc_mem_ref(mctx),
};
return h;
}
void
dns_vecheader_destroy(dns_vecheader_t **headerp) {
unsigned int size;
dns_vecheader_t *header = *headerp;
*headerp = NULL;
isc_mem_t *mctx = header->node->mctx;
dns_db_deletedata(header->node, header);
if (EXISTS(header)) {
size = dns_rdatavec_size(header);
} else {
size = sizeof(*header);
}
isc_mem_put(mctx, header, size);
}
/* Iterators for already bound rdatavec */
isc_result_t
@ -913,9 +913,7 @@ vecheader_current(rdatavec_iter_t *iter, dns_rdata_t *rdata) {
static void
rdataset_disassociate(dns_rdataset_t *rdataset DNS__DB_FLARG) {
dns_dbnode_t *node = rdataset->vec.node;
dns__db_detachnode(&node DNS__DB_FLARG_PASS);
dns_vecheader_unref(rdataset->vec.header);
}
static isc_result_t
@ -937,16 +935,14 @@ rdataset_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata) {
static void
rdataset_clone(const dns_rdataset_t *source,
dns_rdataset_t *target DNS__DB_FLARG) {
dns_dbnode_t *node = source->vec.node;
dns_dbnode_t *cloned_node = NULL;
dns__db_attachnode(node, &cloned_node DNS__DB_FLARG_PASS);
INSIST(!ISC_LINK_LINKED(target, link));
*target = *source;
ISC_LINK_INIT(target, link);
target->vec.iter.iter_pos = NULL;
target->vec.iter.iter_count = 0;
dns_vecheader_ref(target->vec.header);
}
static unsigned int
@ -1014,3 +1010,16 @@ dns_vectop_destroy(isc_mem_t *mctx, dns_vectop_t **topp) {
*topp = NULL;
isc_mem_put(mctx, top, sizeof(*top));
}
static void
vecheader_destroy(dns_vecheader_t *header) {
unsigned int size = EXISTS(header) ? dns_rdatavec_size(header)
: sizeof(*header);
isc_mem_putanddetach(&header->mctx, header, size);
}
/*
* Reference counting implementation for dns_vecheader_t
*/
ISC_REFCOUNT_IMPL(dns_vecheader, vecheader_destroy);

View file

@ -2233,8 +2233,8 @@ query_additional(query_ctx_t *qctx, dns_name_t *name,
goto regular;
}
result = dns_db_addglue(qctx->db, dbversion->version, rdataset,
client->message);
result = dns_db_addglue(qctx->db, dbversion->version, name,
rdataset, client->message);
if (result == ISC_R_SUCCESS) {
return;
}

View file

@ -186,16 +186,10 @@ ownercase_test_one(const char *str1, const char *str2) {
.common.methods = &qpdb_zonemethods,
.common.mctx = isc_g_mctx,
};
qpznode_t node = { .methods = &qpznode_methods, .locknum = 0 };
dns_vecheader_t header = {
.node = (dns_dbnode_t *)&node,
};
dns_vecheader_t header = { 0 };
dns_rdataset_t rdataset = {
.magic = DNS_RDATASET_MAGIC,
.vec = { .db = (dns_db_t *)qpdb,
.node = (dns_dbnode_t *)&node,
.header = &header,
},
.vec = { .header = &header },
.methods = &dns_rdatavec_rdatasetmethods,
};
isc_buffer_t b;
@ -359,16 +353,10 @@ ISC_RUN_TEST_IMPL(setownercase) {
.common.methods = &qpdb_zonemethods,
.common.mctx = isc_g_mctx,
};
qpznode_t node = { .methods = &qpznode_methods, .locknum = 0 };
dns_vecheader_t header = {
.node = (dns_dbnode_t *)&node,
};
dns_vecheader_t header = { 0 };
dns_rdataset_t rdataset = {
.magic = DNS_RDATASET_MAGIC,
.vec = { .db = (dns_db_t *)qpdb,
.node = (dns_dbnode_t *)&node,
.header = &header,
},
.vec = { .header = &header },
.methods = &dns_rdatavec_rdatasetmethods,
};
const char *str1 =

View file

@ -282,10 +282,274 @@ cleanup:
}
}
/* Test case where dns_rdatavec_subtract causes assertion failure */
ISC_RUN_TEST_IMPL(rdatavec_subtract_assertion_failure) {
isc_mem_t *mctx = isc_g_mctx;
UNUSED(state);
dns_vecheader_t *original_header = NULL, *subtract_header = NULL,
*result_header = NULL;
dns_rdataset_t original_rdataset, subtract_rdataset;
dns_rdatalist_t *original_rdatalist = NULL, *subtract_rdatalist = NULL;
dns_rdata_t *original_rdata1 = NULL, *original_rdata2 = NULL,
*subtract_rdata = NULL;
unsigned char *original_data1 = NULL, *original_data2 = NULL,
*subtract_data = NULL;
isc_region_t original_region, subtract_region;
isc_result_t result;
/* Allocate temporary structures for original rdatalist with 2 records
*/
original_data1 = isc_mem_get(mctx, 256);
original_data2 = isc_mem_get(mctx, 256);
original_rdatalist = isc_mem_get(mctx, sizeof(*original_rdatalist));
original_rdata1 = isc_mem_get(mctx, sizeof(*original_rdata1));
original_rdata2 = isc_mem_get(mctx, sizeof(*original_rdata2));
/* Allocate temporary structures for subtract rdatalist with 1 record */
subtract_data = isc_mem_get(mctx, 256);
subtract_rdatalist = isc_mem_get(mctx, sizeof(*subtract_rdatalist));
subtract_rdata = isc_mem_get(mctx, sizeof(*subtract_rdata));
/* Initialize original rdataset and rdatalist with 2 A records */
dns_rdataset_init(&original_rdataset);
dns_rdatalist_init(original_rdatalist);
original_rdatalist->type = dns_rdatatype_a;
original_rdatalist->rdclass = dns_rdataclass_in;
original_rdatalist->ttl = 300;
/* Create first rdata: 192.168.1.1 */
dns_rdata_init(original_rdata1);
CHECK(dns_test_rdatafromstring(original_rdata1, dns_rdataclass_in,
dns_rdatatype_a, original_data1, 256,
"192.168.1.1", false));
ISC_LIST_APPEND(original_rdatalist->rdata, original_rdata1, link);
/* Create second rdata: 192.168.1.2 */
dns_rdata_init(original_rdata2);
CHECK(dns_test_rdatafromstring(original_rdata2, dns_rdataclass_in,
dns_rdatatype_a, original_data2, 256,
"192.168.1.2", false));
ISC_LIST_APPEND(original_rdatalist->rdata, original_rdata2, link);
dns_rdatalist_tordataset(original_rdatalist, &original_rdataset);
/* Initialize subtract rdataset and rdatalist with 1 A record */
dns_rdataset_init(&subtract_rdataset);
dns_rdatalist_init(subtract_rdatalist);
subtract_rdatalist->type = dns_rdatatype_a;
subtract_rdatalist->rdclass = dns_rdataclass_in;
subtract_rdatalist->ttl = 300;
/* Create subtract rdata: 192.168.1.1 (same as first record) */
dns_rdata_init(subtract_rdata);
CHECK(dns_test_rdatafromstring(subtract_rdata, dns_rdataclass_in,
dns_rdatatype_a, subtract_data, 256,
"192.168.1.1", false));
ISC_LIST_APPEND(subtract_rdatalist->rdata, subtract_rdata, link);
dns_rdatalist_tordataset(subtract_rdatalist, &subtract_rdataset);
/* Convert to vecheaders (each starts with refcount = 1) */
CHECK(dns_rdatavec_fromrdataset(&original_rdataset, mctx,
&original_region, 0));
original_header = (dns_vecheader_t *)original_region.base;
CHECK(dns_rdatavec_fromrdataset(&subtract_rdataset, mctx,
&subtract_region, 0));
subtract_header = (dns_vecheader_t *)subtract_region.base;
/*
* This should cause assertion failure because dns_rdatavec_subtract()
* copies the original header (including its mctx) with memmove(), then
* tries to call isc_mem_attach() on the already-attached mctx field.
* Since we're subtracting 1 record from 2, it should create a new
* header and hit the problematic code path at rdatavec.c:759
*/
result = dns_rdatavec_subtract(original_header, subtract_header, mctx,
dns_rdataclass_in, dns_rdatatype_a, 0,
&result_header);
/* If we get here without assertion failure, the bug has been fixed */
assert_int_equal(result, ISC_R_SUCCESS);
assert_non_null(result_header);
/* Result should contain only the second record (192.168.1.2) */
unsigned int result_count = dns_rdatavec_count(result_header);
assert_int_equal(result_count, 1);
cleanup:
/* Cleanup rdatasets */
if (DNS_RDATASET_VALID(&original_rdataset)) {
dns_rdataset_disassociate(&original_rdataset);
}
if (DNS_RDATASET_VALID(&subtract_rdataset)) {
dns_rdataset_disassociate(&subtract_rdataset);
}
/* Cleanup vecheaders */
if (original_header != NULL) {
dns_vecheader_unref(original_header);
}
if (subtract_header != NULL) {
dns_vecheader_unref(subtract_header);
}
if (result_header != NULL) {
dns_vecheader_unref(result_header);
}
/* Cleanup temporary structures for original */
if (original_rdata1 != NULL) {
isc_mem_put(mctx, original_rdata1, sizeof(*original_rdata1));
}
if (original_rdata2 != NULL) {
isc_mem_put(mctx, original_rdata2, sizeof(*original_rdata2));
}
if (original_rdatalist != NULL) {
isc_mem_put(mctx, original_rdatalist,
sizeof(*original_rdatalist));
}
if (original_data1 != NULL) {
isc_mem_put(mctx, original_data1, 256);
}
if (original_data2 != NULL) {
isc_mem_put(mctx, original_data2, 256);
}
/* Cleanup temporary structures for subtract */
if (subtract_rdata != NULL) {
isc_mem_put(mctx, subtract_rdata, sizeof(*subtract_rdata));
}
if (subtract_rdatalist != NULL) {
isc_mem_put(mctx, subtract_rdatalist,
sizeof(*subtract_rdatalist));
}
if (subtract_data != NULL) {
isc_mem_put(mctx, subtract_data, 256);
}
}
/* Test refcount functionality with merge and cleanup */
ISC_RUN_TEST_IMPL(rdatavec_refcount_merge) {
isc_mem_t *mctx = isc_g_mctx;
UNUSED(state);
dns_vecheader_t *header1 = NULL, *header2 = NULL, *merged_header = NULL;
dns_rdataset_t rdataset1, rdataset2;
dns_rdatalist_t *rdatalist1 = NULL, *rdatalist2 = NULL;
dns_rdata_t *rdata1 = NULL, *rdata2 = NULL;
unsigned char *data1 = NULL, *data2 = NULL;
isc_region_t region1, region2;
isc_result_t result;
/* Allocate temporary structures for first rdatalist */
data1 = isc_mem_get(mctx, 256);
rdatalist1 = isc_mem_get(mctx, sizeof(*rdatalist1));
rdata1 = isc_mem_get(mctx, sizeof(*rdata1));
/* Allocate temporary structures for second rdatalist */
data2 = isc_mem_get(mctx, 256);
rdatalist2 = isc_mem_get(mctx, sizeof(*rdatalist2));
rdata2 = isc_mem_get(mctx, sizeof(*rdata2));
/* Initialize first rdataset and rdatalist */
dns_rdataset_init(&rdataset1);
dns_rdatalist_init(rdatalist1);
rdatalist1->type = dns_rdatatype_a;
rdatalist1->rdclass = dns_rdataclass_in;
rdatalist1->ttl = 300;
/* Create first rdata */
dns_rdata_init(rdata1);
CHECK(dns_test_rdatafromstring(rdata1, dns_rdataclass_in,
dns_rdatatype_a, data1, 256,
"192.168.1.1", false));
ISC_LIST_APPEND(rdatalist1->rdata, rdata1, link);
dns_rdatalist_tordataset(rdatalist1, &rdataset1);
/* Initialize second rdataset and rdatalist */
dns_rdataset_init(&rdataset2);
dns_rdatalist_init(rdatalist2);
rdatalist2->type = dns_rdatatype_a;
rdatalist2->rdclass = dns_rdataclass_in;
rdatalist2->ttl = 300;
/* Create second rdata */
dns_rdata_init(rdata2);
CHECK(dns_test_rdatafromstring(rdata2, dns_rdataclass_in,
dns_rdatatype_a, data2, 256,
"192.168.1.2", false));
ISC_LIST_APPEND(rdatalist2->rdata, rdata2, link);
dns_rdatalist_tordataset(rdatalist2, &rdataset2);
/* Convert to vecheaders (each starts with refcount = 1) */
CHECK(dns_rdatavec_fromrdataset(&rdataset1, mctx, &region1, 0));
header1 = (dns_vecheader_t *)region1.base;
CHECK(dns_rdatavec_fromrdataset(&rdataset2, mctx, &region2, 0));
header2 = (dns_vecheader_t *)region2.base;
/* Merge headers (this will create a new header with refcount = 1) */
CHECK(dns_rdatavec_merge(header1, header2, mctx, dns_rdataclass_in,
dns_rdatatype_a, 0, 0, &merged_header));
assert_non_null(merged_header);
/* Test: merged header should have expected count */
unsigned int merged_count = dns_rdatavec_count(merged_header);
assert_int_equal(merged_count, 2);
/* Test: merged header should have expected size */
unsigned int merged_size = dns_rdatavec_size(merged_header);
assert_true(merged_size > sizeof(dns_vecheader_t));
cleanup:
/* Cleanup rdatasets */
if (DNS_RDATASET_VALID(&rdataset1)) {
dns_rdataset_disassociate(&rdataset1);
}
if (DNS_RDATASET_VALID(&rdataset2)) {
dns_rdataset_disassociate(&rdataset2);
}
/* Cleanup using refcount - each header should be unreferenced once */
if (header1 != NULL) {
dns_vecheader_unref(header1);
}
if (header2 != NULL) {
dns_vecheader_unref(header2);
}
if (merged_header != NULL) {
dns_vecheader_unref(merged_header);
}
/* Cleanup temporary structures */
if (rdata1 != NULL) {
isc_mem_put(mctx, rdata1, sizeof(*rdata1));
}
if (rdatalist1 != NULL) {
isc_mem_put(mctx, rdatalist1, sizeof(*rdatalist1));
}
if (data1 != NULL) {
isc_mem_put(mctx, data1, 256);
}
if (rdata2 != NULL) {
isc_mem_put(mctx, rdata2, sizeof(*rdata2));
}
if (rdatalist2 != NULL) {
isc_mem_put(mctx, rdatalist2, sizeof(*rdatalist2));
}
if (data2 != NULL) {
isc_mem_put(mctx, data2, 256);
}
}
ISC_TEST_LIST_START
ISC_TEST_ENTRY_CUSTOM(merge_headers, setup_mctx, teardown_mctx)
ISC_TEST_ENTRY_CUSTOM(merge_case_preservation, setup_mctx, teardown_mctx)
ISC_TEST_ENTRY_CUSTOM(setcase_size_consistency, setup_mctx, teardown_mctx)
ISC_TEST_ENTRY_CUSTOM(rdatavec_subtract_assertion_failure, setup_mctx,
teardown_mctx)
ISC_TEST_ENTRY_CUSTOM(rdatavec_refcount_merge, setup_mctx, teardown_mctx)
ISC_TEST_LIST_END
ISC_TEST_MAIN