mirror of
https://github.com/isc-projects/bind9.git
synced 2026-05-23 10:37:43 -04:00
chg: dev: Compact rdataset implementation for authoritative
This MR introduces a specialized rdataset implementation for authoritative workloads, which leads to substantial memory savings in our perflab tests. Merge branch 'alessio/auth-compact-slabs' into 'main' See merge request isc-projects/bind9!11269
This commit is contained in:
commit
22d49db2b0
13 changed files with 2012 additions and 318 deletions
|
|
@ -17,6 +17,7 @@
|
|||
#include <isc/urcu.h>
|
||||
|
||||
#include <dns/nsec3.h>
|
||||
#include <dns/rdatavec.h>
|
||||
#include <dns/types.h>
|
||||
|
||||
#ifdef STRONG_RWLOCK_CHECK
|
||||
|
|
@ -97,7 +98,7 @@ struct dns_gluelist {
|
|||
isc_mem_t *mctx;
|
||||
|
||||
const dns_dbversion_t *version;
|
||||
dns_slabheader_t *header;
|
||||
dns_vecheader_t *header;
|
||||
|
||||
struct dns_glue *glue;
|
||||
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@
|
|||
|
||||
#include <dns/rdataslab.h>
|
||||
#include <dns/rdatastruct.h>
|
||||
#include <dns/rdatavec.h>
|
||||
#include <dns/types.h>
|
||||
|
||||
#define DNS_RDATASET_MAXADDITIONAL 13
|
||||
|
|
@ -86,7 +87,6 @@ struct dns_rdatasetmethods {
|
|||
void (*getownercase)(const dns_rdataset_t *rdataset, dns_name_t *name);
|
||||
isc_result_t (*addglue)(dns_rdataset_t *rdataset,
|
||||
dns_dbversion_t *version, dns_message_t *msg);
|
||||
dns_slabheader_t *(*getheader)(const dns_rdataset_t *rdataset);
|
||||
};
|
||||
|
||||
#define DNS_RDATASET_MAGIC ISC_MAGIC('D', 'N', 'S', 'R')
|
||||
|
|
@ -200,6 +200,20 @@ struct dns_rdataset {
|
|||
dns_slabheader_proof_t *noqname, *closest;
|
||||
} slab;
|
||||
|
||||
/*
|
||||
* A vec rdataset provides access to an rdatavec. In
|
||||
* a QP database, 'header' points to the vecheader
|
||||
* structure. (There is an exception in the case of
|
||||
* rdatasets returned by the `getnoqname` and `getclosest`
|
||||
* 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;
|
||||
|
||||
/*
|
||||
* A simple rdatalist, plus an optional dbnode used by
|
||||
* builtin and sdlz.
|
||||
|
|
@ -621,17 +635,6 @@ dns_trust_totext(dns_trust_t trust);
|
|||
* Display trust in textual form.
|
||||
*/
|
||||
|
||||
dns_slabheader_t *
|
||||
dns_rdataset_getheader(const dns_rdataset_t *rdataset);
|
||||
/*%<
|
||||
* Return a pointer to the slabheader for a slab rdataset. If 'rdataset'
|
||||
* is not a slab rdataset or if the slab is raw (lacking a header), return
|
||||
* NULL.
|
||||
*
|
||||
* Requires:
|
||||
* \li 'rdataset' is a valid rdataset.
|
||||
*/
|
||||
|
||||
isc_stdtime_t
|
||||
dns_rdataset_minresign(dns_rdataset_t *rdataset);
|
||||
/*%<
|
||||
|
|
|
|||
268
lib/dns/include/dns/rdatavec.h
Normal file
268
lib/dns/include/dns/rdatavec.h
Normal file
|
|
@ -0,0 +1,268 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/*! \file dns/rdatavec.h
|
||||
* \brief
|
||||
* Implements storage of rdatasets into vectors of memory.
|
||||
*
|
||||
* MP:
|
||||
*\li Clients of this module must impose any required synchronization.
|
||||
*
|
||||
* Reliability:
|
||||
*\li This module deals with low-level byte streams. Errors in any of
|
||||
* the functions are likely to crash the server or corrupt memory.
|
||||
*
|
||||
*\li If the caller passes invalid memory references, these functions are
|
||||
* likely to crash the server or corrupt memory.
|
||||
*
|
||||
* Resources:
|
||||
*\li None.
|
||||
*
|
||||
* Security:
|
||||
*\li None.
|
||||
*
|
||||
* Standards:
|
||||
*\li None.
|
||||
*/
|
||||
|
||||
/***
|
||||
*** Imports
|
||||
***/
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <isc/atomic.h>
|
||||
#include <isc/heap.h>
|
||||
#include <isc/slist.h>
|
||||
#include <isc/stdtime.h>
|
||||
#include <isc/urcu.h>
|
||||
|
||||
#include <dns/name.h>
|
||||
#include <dns/rdataset.h>
|
||||
#include <dns/types.h>
|
||||
|
||||
#define DNS_RDATAVEC_FORCE 0x1
|
||||
#define DNS_RDATAVEC_EXACT 0x2
|
||||
|
||||
#define DNS_RDATAVEC_OFFLINE 0x01 /* RRSIG is for offline DNSKEY */
|
||||
|
||||
typedef struct dns_vectop dns_vectop_t;
|
||||
typedef struct dns_vecheader dns_vecheader_t;
|
||||
|
||||
struct rdatavec_iter {
|
||||
unsigned char *iter_pos;
|
||||
unsigned int iter_count;
|
||||
dns_rdataclass_t iter_rdclass;
|
||||
dns_rdatatype_t iter_type;
|
||||
};
|
||||
|
||||
typedef struct rdatavec_iter rdatavec_iter_t;
|
||||
|
||||
struct dns_vectop {
|
||||
ISC_SLINK(dns_vectop_t) next_type;
|
||||
ISC_SLIST(dns_vecheader_t) headers;
|
||||
|
||||
dns_typepair_t typepair;
|
||||
};
|
||||
|
||||
struct dns_vecheader {
|
||||
_Atomic(uint16_t) attributes;
|
||||
_Atomic(dns_trust_t) trust;
|
||||
|
||||
/*%
|
||||
* Locked by the owning node's lock.
|
||||
*/
|
||||
uint16_t resign_lsb : 1;
|
||||
unsigned int heap_index : 31;
|
||||
uint32_t serial;
|
||||
dns_ttl_t ttl;
|
||||
dns_typepair_t typepair;
|
||||
|
||||
/* resigning (zone). The lsb is not adjacent for struct packing reasons
|
||||
*/
|
||||
isc_stdtime_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).
|
||||
*/
|
||||
dns_gluelist_t *gluelist;
|
||||
|
||||
/*%
|
||||
* Case vector. If the bit is set then the corresponding
|
||||
* character in the owner name needs to be AND'd with 0x20,
|
||||
* rendering that character upper case.
|
||||
*/
|
||||
unsigned char upper[32];
|
||||
|
||||
/*%
|
||||
* Flexible member indicates the address of the raw data
|
||||
* following this header.
|
||||
*/
|
||||
unsigned char raw[];
|
||||
};
|
||||
|
||||
enum {
|
||||
DNS_VECHEADERATTR_NONEXISTENT = 1 << 0,
|
||||
DNS_VECHEADERATTR_IGNORE = 1 << 1,
|
||||
DNS_VECHEADERATTR_RESIGN = 1 << 2,
|
||||
DNS_VECHEADERATTR_OPTOUT = 1 << 3,
|
||||
DNS_VECHEADERATTR_CASESET = 1 << 4,
|
||||
DNS_VECHEADERATTR_ZEROTTL = 1 << 5,
|
||||
DNS_VECHEADERATTR_CASEFULLYLOWER = 1 << 6,
|
||||
};
|
||||
|
||||
/* clang-format off : RemoveParentheses */
|
||||
#define DNS_VECHEADER_GETATTR(header, attribute) \
|
||||
(atomic_load_acquire(&(header)->attributes) & (attribute))
|
||||
/* clang-format on */
|
||||
#define DNS_VECHEADER_SETATTR(header, attribute) \
|
||||
atomic_fetch_or_release(&(header)->attributes, attribute)
|
||||
#define DNS_VECHEADER_CLRATTR(header, attribute) \
|
||||
atomic_fetch_and_release(&(header)->attributes, ~(attribute))
|
||||
|
||||
extern dns_rdatasetmethods_t dns_rdatavec_rdatasetmethods;
|
||||
|
||||
/***
|
||||
*** Functions
|
||||
***/
|
||||
|
||||
isc_result_t
|
||||
dns_rdatavec_fromrdataset(dns_rdataset_t *rdataset, isc_mem_t *mctx,
|
||||
isc_region_t *region, uint32_t limit);
|
||||
/*%<
|
||||
* Allocate space for a vec to hold the data in rdataset, and copy the
|
||||
* data into it. The resulting vec will be returned in 'region'.
|
||||
*
|
||||
* dns_rdatavec_fromrdataset() allocates space for a dns_vecheader object
|
||||
* and the memory needed for a raw vec, and partially initializes
|
||||
* it, setting the type, trust, and TTL fields to match rdataset->type,
|
||||
* rdataset->covers, rdataset->trust, and rdataset->ttl. (Note that the
|
||||
* last field needs to be overridden when used in the cache database,
|
||||
* since cache headers use an expire time instead of a TTL.)
|
||||
*
|
||||
* Requires:
|
||||
*\li 'rdataset' is valid.
|
||||
*
|
||||
* Ensures:
|
||||
*\li 'region' will have base pointing to the start of allocated memory,
|
||||
* with the vecified region beginning at region->base + reservelen.
|
||||
* region->length contains the total length allocated.
|
||||
*
|
||||
* Returns:
|
||||
*\li ISC_R_SUCCESS - successful completion
|
||||
*\li ISC_R_NOSPACE - more than 64k RRs
|
||||
*\li DNS_R_TOOMANYRECORDS - more than max-records-per-rrset RRs
|
||||
*\li DNS_R_SINGLETON - singleton type has more than one RR
|
||||
*/
|
||||
|
||||
unsigned int
|
||||
dns_rdatavec_size(dns_vecheader_t *header);
|
||||
/*%<
|
||||
* Return the total size of the rdatavec following 'header'.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'header' points to a vecheader with an rdatavec following it.
|
||||
*
|
||||
* Returns:
|
||||
*\li The number of bytes in the vec, plus the header.
|
||||
*/
|
||||
|
||||
unsigned int
|
||||
dns_rdatavec_count(dns_vecheader_t *header);
|
||||
/*%<
|
||||
* Return the number of records in the rdatavec following 'header'.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'header' points to a vecheader with an rdatavec following it.
|
||||
*
|
||||
* Returns:
|
||||
*\li The number of records in the vec.
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
dns_rdatavec_merge(dns_vecheader_t *oheader, dns_vecheader_t *nheader,
|
||||
isc_mem_t *mctx, dns_rdataclass_t rdclass,
|
||||
dns_rdatatype_t type, unsigned int flags,
|
||||
uint32_t maxrrperset, dns_vecheader_t **theaderp);
|
||||
/*%<
|
||||
* Merge the vecs following 'oheader' and 'nheader'.
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
dns_rdatavec_subtract(dns_vecheader_t *mheader, dns_vecheader_t *sheader,
|
||||
isc_mem_t *mctx, dns_rdataclass_t rdclass,
|
||||
dns_rdatatype_t type, unsigned int flags,
|
||||
dns_vecheader_t **theaderp);
|
||||
/*%<
|
||||
* Subtract the vec following 'sheader' from the one following 'mheader'.
|
||||
* If 'exact' is true then all elements from the 'sheader' vec must exist
|
||||
* in the 'mheader' vec.
|
||||
*
|
||||
* XXX
|
||||
* valid flags are DNS_RDATAVEC_EXACT
|
||||
*/
|
||||
|
||||
void
|
||||
dns_vecheader_setownercase(dns_vecheader_t *header, const dns_name_t *name);
|
||||
/*%<
|
||||
* Store the casing of 'name', into a bitfield in 'header'.
|
||||
*
|
||||
* Requires:
|
||||
* \li 'header' is a valid vecheader.
|
||||
* \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);
|
||||
/*%<
|
||||
* 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'.
|
||||
*/
|
||||
|
||||
dns_vectop_t *
|
||||
dns_vectop_new(isc_mem_t *mctx, dns_typepair_t typepair);
|
||||
/*%<
|
||||
* Allocate memory for an rdatavec top and initialize it for use
|
||||
* with 'typepair' type and covers pair.
|
||||
*/
|
||||
|
||||
void
|
||||
dns_vectop_destroy(isc_mem_t *mctx, dns_vectop_t **topp);
|
||||
/*%<
|
||||
* Free all memory associated with '*vectopp'.
|
||||
*/
|
||||
|
|
@ -140,6 +140,7 @@ dns_srcset.add(
|
|||
'rdataset.c',
|
||||
'rdatasetiter.c',
|
||||
'rdataslab.c',
|
||||
'rdatavec.c',
|
||||
'remote.c',
|
||||
'request.c',
|
||||
'resconf.c',
|
||||
|
|
|
|||
520
lib/dns/qpzone.c
520
lib/dns/qpzone.c
File diff suppressed because it is too large
Load diff
|
|
@ -588,17 +588,6 @@ dns_rdataset_trimttl(dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset,
|
|||
sigrdataset->ttl = ttl;
|
||||
}
|
||||
|
||||
dns_slabheader_t *
|
||||
dns_rdataset_getheader(const dns_rdataset_t *rdataset) {
|
||||
REQUIRE(DNS_RDATASET_VALID(rdataset));
|
||||
|
||||
if (rdataset->methods->getheader != NULL) {
|
||||
return (rdataset->methods->getheader)(rdataset);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
isc_stdtime_t
|
||||
dns_rdataset_minresign(dns_rdataset_t *rdataset) {
|
||||
dns_rdata_t rdata = DNS_RDATA_INIT;
|
||||
|
|
|
|||
|
|
@ -92,7 +92,6 @@ dns_rdatasetmethods_t dns_rdataslab_rdatasetmethods = {
|
|||
.expire = rdataset_expire,
|
||||
.clearprefetch = rdataset_clearprefetch,
|
||||
.getownercase = rdataset_getownercase,
|
||||
.getheader = rdataset_getheader,
|
||||
};
|
||||
|
||||
/*% Note: the "const void *" are just to make qsort happy. */
|
||||
|
|
@ -126,7 +125,7 @@ makeslab(dns_rdataset_t *rdataset, isc_mem_t *mctx, isc_region_t *region,
|
|||
* new buffer.
|
||||
*/
|
||||
if (rdataset->methods == &dns_rdataslab_rdatasetmethods) {
|
||||
dns_slabheader_t *header = dns_rdataset_getheader(rdataset);
|
||||
dns_slabheader_t *header = rdataset_getheader(rdataset);
|
||||
buflen = dns_rdataslab_size(header);
|
||||
|
||||
rawbuf = isc_mem_get(mctx, buflen);
|
||||
|
|
@ -1120,7 +1119,7 @@ rdataset_getclosest(dns_rdataset_t *rdataset, dns_name_t *name,
|
|||
|
||||
static void
|
||||
rdataset_settrust(dns_rdataset_t *rdataset, dns_trust_t trust) {
|
||||
dns_slabheader_t *header = dns_rdataset_getheader(rdataset);
|
||||
dns_slabheader_t *header = rdataset_getheader(rdataset);
|
||||
|
||||
rdataset->trust = trust;
|
||||
atomic_store(&header->trust, trust);
|
||||
|
|
@ -1128,21 +1127,21 @@ rdataset_settrust(dns_rdataset_t *rdataset, dns_trust_t trust) {
|
|||
|
||||
static void
|
||||
rdataset_expire(dns_rdataset_t *rdataset DNS__DB_FLARG) {
|
||||
dns_slabheader_t *header = dns_rdataset_getheader(rdataset);
|
||||
dns_slabheader_t *header = rdataset_getheader(rdataset);
|
||||
|
||||
dns_db_expiredata(header->node, header);
|
||||
}
|
||||
|
||||
static void
|
||||
rdataset_clearprefetch(dns_rdataset_t *rdataset) {
|
||||
dns_slabheader_t *header = dns_rdataset_getheader(rdataset);
|
||||
dns_slabheader_t *header = rdataset_getheader(rdataset);
|
||||
|
||||
DNS_SLABHEADER_CLRATTR(header, DNS_SLABHEADERATTR_PREFETCH);
|
||||
}
|
||||
|
||||
static void
|
||||
rdataset_getownercase(const dns_rdataset_t *rdataset, dns_name_t *name) {
|
||||
dns_slabheader_t *header = dns_rdataset_getheader(rdataset);
|
||||
dns_slabheader_t *header = rdataset_getheader(rdataset);
|
||||
uint8_t mask = (1 << 7);
|
||||
uint8_t bits = 0;
|
||||
|
||||
|
|
|
|||
1004
lib/dns/rdatavec.c
Normal file
1004
lib/dns/rdatavec.c
Normal file
File diff suppressed because it is too large
Load diff
83
lib/dns/rdatavec_p.h
Normal file
83
lib/dns/rdatavec_p.h
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <isc/endian.h>
|
||||
|
||||
#include <dns/rdatavec.h>
|
||||
|
||||
#define CASEFULLYLOWER(header) \
|
||||
((atomic_load_acquire(&(header)->attributes) & \
|
||||
DNS_VECHEADERATTR_CASEFULLYLOWER) != 0)
|
||||
#define CASESET(header) \
|
||||
((atomic_load_acquire(&(header)->attributes) & \
|
||||
DNS_VECHEADERATTR_CASESET) != 0)
|
||||
#define EXISTS(header) \
|
||||
((atomic_load_acquire(&(header)->attributes) & \
|
||||
DNS_VECHEADERATTR_NONEXISTENT) == 0)
|
||||
#define IGNORE(header) \
|
||||
((atomic_load_acquire(&(header)->attributes) & \
|
||||
DNS_VECHEADERATTR_IGNORE) != 0)
|
||||
#define OPTOUT(header) \
|
||||
((atomic_load_acquire(&(header)->attributes) & \
|
||||
DNS_VECHEADERATTR_OPTOUT) != 0)
|
||||
#define RESIGN(header) \
|
||||
((atomic_load_acquire(&(header)->attributes) & \
|
||||
DNS_VECHEADERATTR_RESIGN) != 0)
|
||||
|
||||
#define peek_uint16(buffer) ISC_U8TO16_BE(buffer)
|
||||
#define get_uint16(buffer) \
|
||||
({ \
|
||||
uint16_t __ret = peek_uint16(buffer); \
|
||||
buffer += sizeof(uint16_t); \
|
||||
__ret; \
|
||||
})
|
||||
#define put_uint16(buffer, val) \
|
||||
{ \
|
||||
ISC_U16TO8_BE(buffer, val); \
|
||||
(buffer) += sizeof(uint16_t); \
|
||||
}
|
||||
|
||||
dns_vecheader_t *
|
||||
dns_vecheader_getheader(const dns_rdataset_t *rdataset);
|
||||
/*%<
|
||||
* Return a pointer to the vecheader for a vec rdataset.
|
||||
*
|
||||
* Requires:
|
||||
* \li 'rdataset' is a valid rdataset using rdatavec methods.
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
vecheader_first(rdatavec_iter_t *iter, dns_vecheader_t *header,
|
||||
dns_rdataclass_t rdclass);
|
||||
|
||||
isc_result_t
|
||||
vecheader_next(rdatavec_iter_t *iter);
|
||||
|
||||
void
|
||||
vecheader_current(rdatavec_iter_t *iter, dns_rdata_t *rdata);
|
||||
|
||||
/* clang-format off */
|
||||
/*
|
||||
* An adaptation of DNS_RDATASET_FOREACH from rdataset.h. Used to manipulate a
|
||||
* vecheader without going through the rdataset interface, e.g. in qpzone.c.
|
||||
*/
|
||||
#define DNS__VECHEADER_CONNECT(x,y) x##y
|
||||
#define DNS__VECHEADER_CONCAT(x,y) DNS__VECHEADER_CONNECT(x,y)
|
||||
#define DNS_VECHEADER_FOREACH_RES(iter, header, rdclass, res) \
|
||||
for (isc_result_t res = vecheader_first((iter), (header), (rdclass)); \
|
||||
res == ISC_R_SUCCESS; res = vecheader_next((iter)))
|
||||
#define DNS_VECHEADER_FOREACH(iter, header, rdclass) \
|
||||
DNS_VECHEADER_FOREACH_RES(iter, header, rdclass, DNS__VECHEADER_CONCAT(x, __LINE__))
|
||||
/* clang-format on */
|
||||
82
lib/isc/include/isc/slist.h
Normal file
82
lib/isc/include/isc/slist.h
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/*! \file isc/slist.h
|
||||
* \brief
|
||||
* Implements macros for singly-linked lists.
|
||||
*
|
||||
* This module provides a generic implementation of singly-linked lists
|
||||
* similar to isc/list.h but optimized for forward-only traversal.
|
||||
*/
|
||||
|
||||
#define ISC_SLIST_INITIALIZER \
|
||||
{ \
|
||||
.head = NULL, \
|
||||
}
|
||||
|
||||
#define ISC_SLINK_INITIALIZER \
|
||||
{ \
|
||||
.next = NULL, \
|
||||
}
|
||||
|
||||
#define ISC_SLIST(type) \
|
||||
struct { \
|
||||
type *head; \
|
||||
}
|
||||
|
||||
#define ISC_SLINK(type) \
|
||||
struct { \
|
||||
type *next; \
|
||||
}
|
||||
|
||||
#define ISC_SLIST_HEAD(list) ((list).head)
|
||||
#define ISC_SLIST_EMPTY(list) ((list).head == NULL)
|
||||
|
||||
#define ISC_SLIST_PREPEND(list, elt, link) \
|
||||
({ \
|
||||
(elt)->link.next = (list).head; \
|
||||
(list).head = (elt); \
|
||||
})
|
||||
|
||||
#define ISC_SLIST_INSERTAFTER(after, elt, link) \
|
||||
({ \
|
||||
(elt)->link.next = (after)->link.next; \
|
||||
(after)->link.next = (elt); \
|
||||
})
|
||||
|
||||
#define ISC_SLIST_NEXT(elt, link) ((elt)->link.next)
|
||||
|
||||
/* clang-format off */
|
||||
#define ISC_SLIST_FOREACH_FROM(elt, list, link, first) \
|
||||
for (typeof(first) elt = first, \
|
||||
elt##_next = (elt != NULL) ? ISC_SLIST_NEXT(elt, link) : NULL; \
|
||||
elt != NULL; \
|
||||
elt = elt##_next, \
|
||||
elt##_next = (elt != NULL) ? ISC_SLIST_NEXT(elt, link) : NULL)
|
||||
|
||||
#define ISC_SLIST_FOREACH(elt, list, link) \
|
||||
ISC_SLIST_FOREACH_FROM(elt, list, link, ISC_SLIST_HEAD(list))
|
||||
|
||||
/* clang-format on */
|
||||
|
||||
/* Iteration over pointer-to-pointer for safe operations */
|
||||
#define ISC_SLIST_FOREACH_PTR(p, head) \
|
||||
for (typeof(head) p = (head); *p != NULL;)
|
||||
|
||||
#define ISC_SLIST_PTR_REMOVE(p, elt, link_field) \
|
||||
(*(p) = ISC_SLIST_NEXT(elt, link_field))
|
||||
|
||||
#define ISC_SLIST_PTR_ADVANCE(p, link_field) \
|
||||
(p = &ISC_SLIST_NEXT(*p, link_field))
|
||||
|
|
@ -44,6 +44,7 @@ dns_tests = [
|
|||
'tsig',
|
||||
'unreachcache',
|
||||
'update',
|
||||
'vecheader',
|
||||
'zonefile',
|
||||
'zonemgr',
|
||||
'zt',
|
||||
|
|
|
|||
|
|
@ -33,11 +33,11 @@
|
|||
#include <dns/qp.h>
|
||||
#include <dns/rdatalist.h>
|
||||
#include <dns/rdataset.h>
|
||||
#include <dns/rdataslab.h>
|
||||
#include <dns/rdatastruct.h>
|
||||
#include <dns/rdatavec.h>
|
||||
#define KEEP_BEFORE
|
||||
|
||||
#include "rdataslab_p.h"
|
||||
#include "rdatavec_p.h"
|
||||
|
||||
/* Include the main file */
|
||||
|
||||
|
|
@ -187,17 +187,16 @@ ownercase_test_one(const char *str1, const char *str2) {
|
|||
.common.mctx = isc_g_mctx,
|
||||
};
|
||||
qpznode_t node = { .methods = &qpznode_methods, .locknum = 0 };
|
||||
dns_slabheader_t header = {
|
||||
dns_vecheader_t header = {
|
||||
.node = (dns_dbnode_t *)&node,
|
||||
};
|
||||
unsigned char *raw = (unsigned char *)(&header) + sizeof(header);
|
||||
dns_rdataset_t rdataset = {
|
||||
.magic = DNS_RDATASET_MAGIC,
|
||||
.slab = { .db = (dns_db_t *)qpdb,
|
||||
.node = (dns_dbnode_t *)&node,
|
||||
.raw = raw,
|
||||
.vec = { .db = (dns_db_t *)qpdb,
|
||||
.node = (dns_dbnode_t *)&node,
|
||||
.header = &header,
|
||||
},
|
||||
.methods = &dns_rdataslab_rdatasetmethods,
|
||||
.methods = &dns_rdatavec_rdatasetmethods,
|
||||
};
|
||||
isc_buffer_t b;
|
||||
dns_fixedname_t fname1, fname2;
|
||||
|
|
@ -216,7 +215,7 @@ ownercase_test_one(const char *str1, const char *str2) {
|
|||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
|
||||
/* Store the case from name1 */
|
||||
dns_slabheader_setownercase(&header, name1);
|
||||
dns_vecheader_setownercase(&header, name1);
|
||||
|
||||
assert_true(CASESET(&header));
|
||||
|
||||
|
|
@ -361,17 +360,16 @@ ISC_RUN_TEST_IMPL(setownercase) {
|
|||
.common.mctx = isc_g_mctx,
|
||||
};
|
||||
qpznode_t node = { .methods = &qpznode_methods, .locknum = 0 };
|
||||
dns_slabheader_t header = {
|
||||
dns_vecheader_t header = {
|
||||
.node = (dns_dbnode_t *)&node,
|
||||
};
|
||||
unsigned char *raw = (unsigned char *)(&header) + sizeof(header);
|
||||
dns_rdataset_t rdataset = {
|
||||
.magic = DNS_RDATASET_MAGIC,
|
||||
.slab = { .db = (dns_db_t *)qpdb,
|
||||
.node = (dns_dbnode_t *)&node,
|
||||
.raw = raw,
|
||||
.vec = { .db = (dns_db_t *)qpdb,
|
||||
.node = (dns_dbnode_t *)&node,
|
||||
.header = &header,
|
||||
},
|
||||
.methods = &dns_rdataslab_rdatasetmethods,
|
||||
.methods = &dns_rdatavec_rdatasetmethods,
|
||||
};
|
||||
const char *str1 =
|
||||
"AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz";
|
||||
|
|
|
|||
291
tests/dns/vecheader_test.c
Normal file
291
tests/dns/vecheader_test.c
Normal file
|
|
@ -0,0 +1,291 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <sched.h> /* IWYU pragma: keep */
|
||||
#include <setjmp.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define UNIT_TESTING
|
||||
#include <cmocka.h>
|
||||
|
||||
#include <isc/buffer.h>
|
||||
#include <isc/lib.h>
|
||||
#include <isc/mem.h>
|
||||
#include <isc/result.h>
|
||||
#include <isc/util.h>
|
||||
|
||||
#include <dns/db.h>
|
||||
#include <dns/fixedname.h>
|
||||
#include <dns/lib.h>
|
||||
#include <dns/name.h>
|
||||
#include <dns/rdata.h>
|
||||
#include <dns/rdataclass.h>
|
||||
#include <dns/rdatalist.h>
|
||||
#include <dns/rdataset.h>
|
||||
#include <dns/rdatatype.h>
|
||||
#include <dns/rdatavec.h>
|
||||
|
||||
#include <tests/dns.h>
|
||||
#include <tests/isc.h>
|
||||
|
||||
/* Helper function to create a vecheader directly */
|
||||
static isc_result_t
|
||||
create_vecheader(isc_mem_t *mctx, dns_rdatatype_t type,
|
||||
dns_rdataclass_t rdclass, dns_ttl_t ttl,
|
||||
const char *rdata_text, dns_vecheader_t **headerp) {
|
||||
dns_rdataset_t rdataset;
|
||||
dns_rdatalist_t *rdatalist;
|
||||
dns_rdata_t *rdata;
|
||||
unsigned char *data;
|
||||
isc_region_t region;
|
||||
isc_result_t result;
|
||||
|
||||
/* Allocate temporary structures */
|
||||
data = isc_mem_get(mctx, 256);
|
||||
rdatalist = isc_mem_get(mctx, sizeof(*rdatalist));
|
||||
rdata = isc_mem_get(mctx, sizeof(*rdata));
|
||||
|
||||
/* Initialize rdataset and rdatalist */
|
||||
dns_rdataset_init(&rdataset);
|
||||
dns_rdatalist_init(rdatalist);
|
||||
rdatalist->type = type;
|
||||
rdatalist->rdclass = rdclass;
|
||||
rdatalist->ttl = ttl;
|
||||
|
||||
/* Create rdata */
|
||||
dns_rdata_init(rdata);
|
||||
CHECK(dns_test_rdatafromstring(rdata, rdclass, type, data, 256,
|
||||
rdata_text, false));
|
||||
|
||||
ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
|
||||
dns_rdatalist_tordataset(rdatalist, &rdataset);
|
||||
|
||||
/* Convert to vecheader */
|
||||
CHECK(dns_rdatavec_fromrdataset(&rdataset, mctx, ®ion, 0));
|
||||
*headerp = (dns_vecheader_t *)region.base;
|
||||
|
||||
/* Cleanup rdataset */
|
||||
dns_rdataset_disassociate(&rdataset);
|
||||
|
||||
cleanup:
|
||||
/* Cleanup temporary structures */
|
||||
isc_mem_put(mctx, rdata, sizeof(*rdata));
|
||||
isc_mem_put(mctx, rdatalist, sizeof(*rdatalist));
|
||||
isc_mem_put(mctx, data, 256);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Helper function to create an rdataset from a vecheader */
|
||||
static void
|
||||
create_rdataset_from_vecheader(dns_vecheader_t *header,
|
||||
dns_rdataclass_t rdclass, dns_rdatatype_t type,
|
||||
dns_rdataset_t *rdataset) {
|
||||
dns_rdataset_init(rdataset);
|
||||
rdataset->methods = &dns_rdatavec_rdatasetmethods;
|
||||
rdataset->rdclass = rdclass;
|
||||
rdataset->type = type;
|
||||
rdataset->vec.header = header;
|
||||
}
|
||||
|
||||
/* Test merging two headers */
|
||||
ISC_RUN_TEST_IMPL(merge_headers) {
|
||||
isc_mem_t *mctx = isc_g_mctx;
|
||||
UNUSED(state);
|
||||
dns_vecheader_t *header1 = NULL, *header2 = NULL, *merged_header = NULL;
|
||||
unsigned int size1, size2, merged_size, expected_size;
|
||||
unsigned int count1, count2, merged_count, expected_count;
|
||||
isc_result_t result;
|
||||
|
||||
/* Create vecheaders with A records */
|
||||
CHECK(create_vecheader(mctx, dns_rdatatype_a, dns_rdataclass_in, 300,
|
||||
"192.168.1.1", &header1));
|
||||
|
||||
CHECK(create_vecheader(mctx, dns_rdatatype_a, dns_rdataclass_in, 300,
|
||||
"192.168.1.2", &header2));
|
||||
|
||||
/* Get sizes and counts before merging */
|
||||
size1 = dns_rdatavec_size(header1);
|
||||
size2 = dns_rdatavec_size(header2);
|
||||
count1 = dns_rdatavec_count(header1);
|
||||
count2 = dns_rdatavec_count(header2);
|
||||
|
||||
/* Merge headers */
|
||||
CHECK(dns_rdatavec_merge(header1, header2, mctx, dns_rdataclass_in,
|
||||
dns_rdatatype_a, 0, 0, &merged_header));
|
||||
assert_non_null(merged_header);
|
||||
|
||||
/* Get merged size and count */
|
||||
merged_size = dns_rdatavec_size(merged_header);
|
||||
merged_count = dns_rdatavec_count(merged_header);
|
||||
|
||||
/* Test: merged size should be first_size + second_size - sizeof(header)
|
||||
* - count_field_size */
|
||||
expected_size = size1 + size2 - sizeof(dns_vecheader_t) - 2;
|
||||
assert_int_equal(merged_size, expected_size);
|
||||
|
||||
/* Test: merged count should be first_count + second_count */
|
||||
expected_count = count1 + count2;
|
||||
assert_int_equal(merged_count, expected_count);
|
||||
|
||||
cleanup:
|
||||
/* Cleanup */
|
||||
if (header1 != NULL) {
|
||||
size1 = dns_rdatavec_size(header1);
|
||||
isc_mem_put(mctx, header1, size1);
|
||||
}
|
||||
if (header2 != NULL) {
|
||||
size2 = dns_rdatavec_size(header2);
|
||||
isc_mem_put(mctx, header2, size2);
|
||||
}
|
||||
if (merged_header != NULL) {
|
||||
merged_size = dns_rdatavec_size(merged_header);
|
||||
isc_mem_put(mctx, merged_header, merged_size);
|
||||
}
|
||||
}
|
||||
|
||||
/* Test case preservation during merge */
|
||||
ISC_RUN_TEST_IMPL(merge_case_preservation) {
|
||||
isc_mem_t *mctx = isc_g_mctx;
|
||||
UNUSED(state);
|
||||
dns_vecheader_t *header1 = NULL, *header2 = NULL, *merged_header = NULL;
|
||||
dns_fixedname_t fname1, fname2, fmerged_name;
|
||||
dns_name_t *name1 = dns_fixedname_initname(&fname1);
|
||||
dns_name_t *name2 = dns_fixedname_initname(&fname2);
|
||||
dns_name_t *merged_name = dns_fixedname_initname(&fmerged_name);
|
||||
unsigned int size1, size2, merged_size;
|
||||
isc_result_t result;
|
||||
|
||||
dns_test_namefromstring("Example.COM", &fname1);
|
||||
dns_test_namefromstring("example.com", &fname2);
|
||||
|
||||
/* Create vecheaders */
|
||||
CHECK(create_vecheader(mctx, dns_rdatatype_a, dns_rdataclass_in, 300,
|
||||
"192.168.1.1", &header1));
|
||||
|
||||
CHECK(create_vecheader(mctx, dns_rdatatype_a, dns_rdataclass_in, 300,
|
||||
"192.168.1.2", &header2));
|
||||
|
||||
/* Set case on first header */
|
||||
dns_vecheader_setownercase(header1, name1);
|
||||
|
||||
/* Set case on second header */
|
||||
dns_vecheader_setownercase(header2, name2);
|
||||
|
||||
/* Get sizes */
|
||||
size1 = dns_rdatavec_size(header1);
|
||||
size2 = dns_rdatavec_size(header2);
|
||||
|
||||
/* Merge headers */
|
||||
CHECK(dns_rdatavec_merge(header1, header2, mctx, dns_rdataclass_in,
|
||||
dns_rdatatype_a, 0, 0, &merged_header));
|
||||
assert_non_null(merged_header);
|
||||
|
||||
/* Test: case should be the same as the first header */
|
||||
/* Copy the name for testing */
|
||||
dns_name_copy(name1, merged_name);
|
||||
|
||||
/* Create a test rdataset from merged header to test case */
|
||||
dns_rdataset_t test_rdataset;
|
||||
create_rdataset_from_vecheader(merged_header, dns_rdataclass_in,
|
||||
dns_rdatatype_a, &test_rdataset);
|
||||
|
||||
/* Apply case from merged header */
|
||||
dns_rdataset_getownercase(&test_rdataset, merged_name);
|
||||
|
||||
/* The case should match the original first name */
|
||||
assert_true(dns_name_caseequal(name1, merged_name));
|
||||
|
||||
cleanup:
|
||||
/* Cleanup */
|
||||
if (header1 != NULL) {
|
||||
size1 = dns_rdatavec_size(header1);
|
||||
isc_mem_put(mctx, header1, size1);
|
||||
}
|
||||
if (header2 != NULL) {
|
||||
size2 = dns_rdatavec_size(header2);
|
||||
isc_mem_put(mctx, header2, size2);
|
||||
}
|
||||
if (merged_header != NULL) {
|
||||
merged_size = dns_rdatavec_size(merged_header);
|
||||
isc_mem_put(mctx, merged_header, merged_size);
|
||||
}
|
||||
}
|
||||
|
||||
/* Test size consistency after setting case */
|
||||
ISC_RUN_TEST_IMPL(setcase_size_consistency) {
|
||||
isc_mem_t *mctx = isc_g_mctx;
|
||||
UNUSED(state);
|
||||
dns_vecheader_t *header = NULL;
|
||||
dns_fixedname_t fname, flower_fname, fretrieved_fname;
|
||||
dns_name_t *name = dns_fixedname_initname(&fname);
|
||||
dns_name_t *lower_name = dns_fixedname_initname(&flower_fname);
|
||||
dns_name_t *retrieved_name = dns_fixedname_initname(&fretrieved_fname);
|
||||
unsigned int original_size, cased_size;
|
||||
dns_rdataset_t test_rdataset;
|
||||
isc_result_t result;
|
||||
|
||||
/* Initialize name */
|
||||
dns_test_namefromstring("Example.COM", &fname);
|
||||
|
||||
/* Create vecheader */
|
||||
CHECK(create_vecheader(mctx, dns_rdatatype_a, dns_rdataclass_in, 300,
|
||||
"192.168.1.1", &header));
|
||||
|
||||
/* Get original size */
|
||||
original_size = dns_rdatavec_size(header);
|
||||
|
||||
/* Set case */
|
||||
dns_vecheader_setownercase(header, name);
|
||||
|
||||
/* Get size after setting case */
|
||||
cased_size = dns_rdatavec_size(header);
|
||||
|
||||
/* Test: size should be the same after setting case */
|
||||
assert_int_equal(cased_size, original_size);
|
||||
|
||||
/* Create lowercase version of the original name */
|
||||
dns_test_namefromstring("example.com", &flower_fname);
|
||||
|
||||
/* Copy lowercase name to retrieved_name for testing */
|
||||
dns_name_copy(lower_name, retrieved_name);
|
||||
|
||||
/* Create a test rdataset from cased header */
|
||||
create_rdataset_from_vecheader(header, dns_rdataclass_in,
|
||||
dns_rdatatype_a, &test_rdataset);
|
||||
|
||||
/* Apply case from cased header to retrieved_name */
|
||||
dns_rdataset_getownercase(&test_rdataset, retrieved_name);
|
||||
|
||||
/* Test: retrieved case should match the original mixed case */
|
||||
assert_true(dns_name_caseequal(name, retrieved_name));
|
||||
|
||||
cleanup:
|
||||
/* Cleanup */
|
||||
if (header != NULL) {
|
||||
cased_size = dns_rdatavec_size(header);
|
||||
isc_mem_put(mctx, header, cased_size);
|
||||
}
|
||||
}
|
||||
|
||||
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_LIST_END
|
||||
|
||||
ISC_TEST_MAIN
|
||||
Loading…
Reference in a new issue