mirror of
https://github.com/isc-projects/bind9.git
synced 2026-06-11 05:39:59 -04:00
fix: usr: Resolve "Inbound IXFR performance regression between 9.18.31 and 9.20.9"
This MR adds add some specialized logic to handle IXFR in qpzone, avoiding the need to have one qp transaction per rdataset. We do this in multiple steps: - We extend dns_rdatacallbacks_t vtable to allow subtraction and resigning. - We add a new set of api (begin|commit|abort)update to the dbmethods vtable. These API model an incremental update that can be aborted, and make diff apply use these functions instead of adding the rdatasets directly to the database. - We add a specialization of dns_rdatacallbacks_t to qpzone that uses a single qp transaction for the entire IXFR. With this batch API, we see performance improvements over adding one rdataset at a time. Closes #5442 Merge branch '5442-ixfr-batch-transaction' into 'main' See merge request isc-projects/bind9!11077
This commit is contained in:
commit
c47239985b
13 changed files with 1034 additions and 224 deletions
51
lib/dns/db.c
51
lib/dns/db.c
|
|
@ -300,6 +300,57 @@ dns_db_endload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
|
|||
return ISC_R_NOTIMPLEMENTED;
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
dns_db_beginupdate(dns_db_t *db, dns_dbversion_t *ver,
|
||||
dns_rdatacallbacks_t *callbacks) {
|
||||
/*
|
||||
* Begin updating 'db'.
|
||||
*/
|
||||
|
||||
REQUIRE(DNS_DB_VALID(db));
|
||||
REQUIRE(dns_db_iszone(db));
|
||||
REQUIRE(DNS_CALLBACK_VALID(callbacks));
|
||||
|
||||
if (db->methods->beginupdate != NULL) {
|
||||
return (db->methods->beginupdate)(db, ver, callbacks);
|
||||
}
|
||||
return ISC_R_NOTIMPLEMENTED;
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
dns_db_commitupdate(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
|
||||
/*
|
||||
* Commit the update to 'db'.
|
||||
*/
|
||||
|
||||
REQUIRE(DNS_DB_VALID(db));
|
||||
REQUIRE(dns_db_iszone(db));
|
||||
REQUIRE(DNS_CALLBACK_VALID(callbacks));
|
||||
|
||||
if (db->methods->commitupdate != NULL) {
|
||||
return (db->methods->commitupdate)(db, callbacks);
|
||||
}
|
||||
|
||||
return ISC_R_NOTIMPLEMENTED;
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
dns_db_abortupdate(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
|
||||
/*
|
||||
* Abort the update to 'db'.
|
||||
*/
|
||||
|
||||
REQUIRE(DNS_DB_VALID(db));
|
||||
REQUIRE(dns_db_iszone(db));
|
||||
REQUIRE(DNS_CALLBACK_VALID(callbacks));
|
||||
|
||||
if (db->methods->abortupdate != NULL) {
|
||||
return (db->methods->abortupdate)(db, callbacks);
|
||||
}
|
||||
|
||||
return ISC_R_NOTIMPLEMENTED;
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
dns_db_load(dns_db_t *db, const char *filename, dns_masterformat_t format,
|
||||
unsigned int options) {
|
||||
|
|
|
|||
285
lib/dns/diff.c
285
lib/dns/diff.c
|
|
@ -203,42 +203,6 @@ dns_diff_appendminimal(dns_diff_t *diff, dns_difftuple_t **tuplep) {
|
|||
}
|
||||
}
|
||||
|
||||
static isc_stdtime_t
|
||||
setresign(dns_rdataset_t *modified) {
|
||||
dns_rdata_t rdata = DNS_RDATA_INIT;
|
||||
dns_rdata_rrsig_t sig;
|
||||
int64_t when;
|
||||
isc_result_t result;
|
||||
|
||||
result = dns_rdataset_first(modified);
|
||||
INSIST(result == ISC_R_SUCCESS);
|
||||
dns_rdataset_current(modified, &rdata);
|
||||
(void)dns_rdata_tostruct(&rdata, &sig, NULL);
|
||||
if ((rdata.flags & DNS_RDATA_OFFLINE) != 0) {
|
||||
when = 0;
|
||||
} else {
|
||||
when = dns_time64_from32(sig.timeexpire);
|
||||
}
|
||||
dns_rdata_reset(&rdata);
|
||||
|
||||
result = dns_rdataset_next(modified);
|
||||
while (result == ISC_R_SUCCESS) {
|
||||
dns_rdataset_current(modified, &rdata);
|
||||
(void)dns_rdata_tostruct(&rdata, &sig, NULL);
|
||||
if ((rdata.flags & DNS_RDATA_OFFLINE) != 0) {
|
||||
goto next_rr;
|
||||
}
|
||||
if (when == 0 || dns_time64_from32(sig.timeexpire) < when) {
|
||||
when = dns_time64_from32(sig.timeexpire);
|
||||
}
|
||||
next_rr:
|
||||
dns_rdata_reset(&rdata);
|
||||
result = dns_rdataset_next(modified);
|
||||
}
|
||||
INSIST(result == ISC_R_NOMORE);
|
||||
return (isc_stdtime_t)when;
|
||||
}
|
||||
|
||||
static void
|
||||
getownercase(dns_rdataset_t *rdataset, dns_name_t *name) {
|
||||
if (dns_rdataset_isassociated(rdataset)) {
|
||||
|
|
@ -246,6 +210,80 @@ getownercase(dns_rdataset_t *rdataset, dns_name_t *name) {
|
|||
}
|
||||
}
|
||||
|
||||
static isc_result_t
|
||||
update_rdataset(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
|
||||
dns_rdataset_t *rds, dns_diffop_t op) {
|
||||
isc_result_t result;
|
||||
unsigned int options;
|
||||
dns_rdataset_t ardataset;
|
||||
dns_dbnode_t *node = NULL;
|
||||
bool is_resign;
|
||||
|
||||
dns_rdataset_init(&ardataset);
|
||||
|
||||
is_resign = rds->type == dns_rdatatype_rrsig &&
|
||||
(op == DNS_DIFFOP_DELRESIGN || op == DNS_DIFFOP_ADDRESIGN);
|
||||
|
||||
if (rds->type != dns_rdatatype_nsec3 &&
|
||||
rds->covers != dns_rdatatype_nsec3)
|
||||
{
|
||||
CHECK(dns_db_findnode(db, name, true, &node));
|
||||
} else {
|
||||
CHECK(dns_db_findnsec3node(db, name, true, &node));
|
||||
}
|
||||
|
||||
switch (op) {
|
||||
case DNS_DIFFOP_ADD:
|
||||
case DNS_DIFFOP_ADDRESIGN:
|
||||
options = DNS_DBADD_MERGE | DNS_DBADD_EXACT |
|
||||
DNS_DBADD_EXACTTTL;
|
||||
CHECK(dns_db_addrdataset(db, node, ver, 0, rds, options,
|
||||
&ardataset));
|
||||
break;
|
||||
case DNS_DIFFOP_DEL:
|
||||
case DNS_DIFFOP_DELRESIGN:
|
||||
options = DNS_DBSUB_EXACT | DNS_DBSUB_WANTOLD;
|
||||
result = dns_db_subtractrdataset(db, node, ver, rds, options,
|
||||
&ardataset);
|
||||
switch (result) {
|
||||
case ISC_R_SUCCESS:
|
||||
case DNS_R_UNCHANGED:
|
||||
case DNS_R_NXRRSET:
|
||||
getownercase(&ardataset, name);
|
||||
CHECK(result);
|
||||
break;
|
||||
default:
|
||||
CHECK(result);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
if (is_resign) {
|
||||
isc_stdtime_t resign;
|
||||
resign = dns_rdataset_minresign(&ardataset);
|
||||
dns_db_setsigningtime(db, &ardataset, resign);
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (node != NULL) {
|
||||
dns_db_detachnode(&node);
|
||||
}
|
||||
if (dns_rdataset_isassociated(&ardataset)) {
|
||||
dns_rdataset_disassociate(&ardataset);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static isc_result_t
|
||||
update_callback(void *arg, const dns_name_t *name, dns_rdataset_t *rds,
|
||||
dns_diffop_t op DNS__DB_FLARG) {
|
||||
dns_updatectx_t *ctx = arg;
|
||||
return update_rdataset(ctx->db, ctx->ver, (dns_name_t *)name, rds, op);
|
||||
}
|
||||
|
||||
static const char *
|
||||
optotext(dns_diffop_t op) {
|
||||
switch (op) {
|
||||
|
|
@ -263,23 +301,24 @@ optotext(dns_diffop_t op) {
|
|||
}
|
||||
|
||||
static isc_result_t
|
||||
diff_apply(const dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *ver,
|
||||
bool warn) {
|
||||
diff_apply(const dns_diff_t *diff, dns_rdatacallbacks_t *callbacks) {
|
||||
dns_difftuple_t *t;
|
||||
dns_dbnode_t *node = NULL;
|
||||
isc_result_t result;
|
||||
char namebuf[DNS_NAME_FORMATSIZE];
|
||||
char typebuf[DNS_RDATATYPE_FORMATSIZE];
|
||||
char classbuf[DNS_RDATACLASS_FORMATSIZE];
|
||||
dns_updatectx_t *ctx;
|
||||
|
||||
REQUIRE(DNS_DIFF_VALID(diff));
|
||||
REQUIRE(DNS_DB_VALID(db));
|
||||
REQUIRE(callbacks != NULL);
|
||||
REQUIRE(callbacks->update != NULL);
|
||||
|
||||
ctx = callbacks->add_private;
|
||||
|
||||
t = ISC_LIST_HEAD(diff->tuples);
|
||||
while (t != NULL) {
|
||||
dns_name_t *name;
|
||||
|
||||
INSIST(node == NULL);
|
||||
name = &t->name;
|
||||
/*
|
||||
* Find the node.
|
||||
|
|
@ -296,8 +335,6 @@ diff_apply(const dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *ver,
|
|||
dns_diffop_t op;
|
||||
dns_rdatalist_t rdl;
|
||||
dns_rdataset_t rds;
|
||||
dns_rdataset_t ardataset;
|
||||
unsigned int options;
|
||||
|
||||
op = t->op;
|
||||
type = t->rdata.type;
|
||||
|
|
@ -325,16 +362,6 @@ diff_apply(const dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *ver,
|
|||
rdl.rdclass = t->rdata.rdclass;
|
||||
rdl.ttl = t->ttl;
|
||||
|
||||
node = NULL;
|
||||
if (type != dns_rdatatype_nsec3 &&
|
||||
covers != dns_rdatatype_nsec3)
|
||||
{
|
||||
CHECK(dns_db_findnode(db, name, true, &node));
|
||||
} else {
|
||||
CHECK(dns_db_findnsec3node(db, name, true,
|
||||
&node));
|
||||
}
|
||||
|
||||
while (t != NULL && dns_name_equal(&t->name, name) &&
|
||||
t->op == op && t->rdata.type == type &&
|
||||
rdata_covers(&t->rdata) == covers)
|
||||
|
|
@ -344,7 +371,7 @@ diff_apply(const dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *ver,
|
|||
* dns_rdataset_setownercase.
|
||||
*/
|
||||
name = &t->name;
|
||||
if (t->ttl != rdl.ttl && warn) {
|
||||
if (t->ttl != rdl.ttl && ctx->warn) {
|
||||
dns_name_format(name, namebuf,
|
||||
sizeof(namebuf));
|
||||
dns_rdatatype_format(t->rdata.type,
|
||||
|
|
@ -373,7 +400,6 @@ diff_apply(const dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *ver,
|
|||
* Convert the rdatalist into a rdataset.
|
||||
*/
|
||||
dns_rdataset_init(&rds);
|
||||
dns_rdataset_init(&ardataset);
|
||||
dns_rdatalist_tordataset(&rdl, &rds);
|
||||
dns_rdataset_setownercase(&rds, name);
|
||||
rds.trust = dns_trust_ultimate;
|
||||
|
|
@ -381,42 +407,16 @@ diff_apply(const dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *ver,
|
|||
/*
|
||||
* Merge the rdataset into the database.
|
||||
*/
|
||||
switch (op) {
|
||||
case DNS_DIFFOP_ADD:
|
||||
case DNS_DIFFOP_ADDRESIGN:
|
||||
options = DNS_DBADD_MERGE | DNS_DBADD_EXACT |
|
||||
DNS_DBADD_EXACTTTL;
|
||||
result = dns_db_addrdataset(db, node, ver, 0,
|
||||
&rds, options,
|
||||
&ardataset);
|
||||
break;
|
||||
case DNS_DIFFOP_DEL:
|
||||
case DNS_DIFFOP_DELRESIGN:
|
||||
options = DNS_DBSUB_EXACT | DNS_DBSUB_WANTOLD;
|
||||
result = dns_db_subtractrdataset(db, node, ver,
|
||||
&rds, options,
|
||||
&ardataset);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
result = callbacks->update(callbacks->add_private, name,
|
||||
&rds, op DNS__DB_FILELINE);
|
||||
|
||||
if (result == ISC_R_SUCCESS) {
|
||||
if (rds.type == dns_rdatatype_rrsig &&
|
||||
(op == DNS_DIFFOP_DELRESIGN ||
|
||||
op == DNS_DIFFOP_ADDRESIGN))
|
||||
{
|
||||
isc_stdtime_t resign;
|
||||
resign = setresign(&ardataset);
|
||||
dns_db_setsigningtime(db, &ardataset,
|
||||
resign);
|
||||
}
|
||||
if (op == DNS_DIFFOP_DEL ||
|
||||
op == DNS_DIFFOP_DELRESIGN)
|
||||
{
|
||||
getownercase(&ardataset, name);
|
||||
}
|
||||
} else if (result == DNS_R_UNCHANGED) {
|
||||
switch (result) {
|
||||
case ISC_R_SUCCESS:
|
||||
/*
|
||||
* OK.
|
||||
*/
|
||||
break;
|
||||
case DNS_R_UNCHANGED:
|
||||
/*
|
||||
* This will not happen when executing a
|
||||
* dynamic update, because that code will
|
||||
|
|
@ -425,13 +425,13 @@ diff_apply(const dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *ver,
|
|||
* from a server that is not as careful.
|
||||
* Issue a warning and continue.
|
||||
*/
|
||||
if (warn) {
|
||||
dns_name_format(dns_db_origin(db),
|
||||
if (ctx->warn) {
|
||||
dns_name_format(dns_db_origin(ctx->db),
|
||||
namebuf,
|
||||
sizeof(namebuf));
|
||||
dns_rdataclass_format(dns_db_class(db),
|
||||
classbuf,
|
||||
sizeof(classbuf));
|
||||
dns_rdataclass_format(
|
||||
dns_db_class(ctx->db), classbuf,
|
||||
sizeof(classbuf));
|
||||
isc_log_write(DNS_LOGCATEGORY_GENERAL,
|
||||
DNS_LOGMODULE_DIFF,
|
||||
ISC_LOG_WARNING,
|
||||
|
|
@ -439,70 +439,70 @@ diff_apply(const dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *ver,
|
|||
"update with no effect",
|
||||
namebuf, classbuf);
|
||||
}
|
||||
if (op == DNS_DIFFOP_DEL ||
|
||||
op == DNS_DIFFOP_DELRESIGN)
|
||||
{
|
||||
getownercase(&ardataset, name);
|
||||
}
|
||||
} else if (result == DNS_R_NXRRSET) {
|
||||
result = ISC_R_SUCCESS;
|
||||
break;
|
||||
case DNS_R_NXRRSET:
|
||||
/*
|
||||
* OK.
|
||||
*/
|
||||
if (op == DNS_DIFFOP_DEL ||
|
||||
op == DNS_DIFFOP_DELRESIGN)
|
||||
{
|
||||
getownercase(&ardataset, name);
|
||||
}
|
||||
if (dns_rdataset_isassociated(&ardataset)) {
|
||||
dns_rdataset_disassociate(&ardataset);
|
||||
}
|
||||
} else {
|
||||
if (result == DNS_R_NOTEXACT) {
|
||||
dns_name_format(name, namebuf,
|
||||
sizeof(namebuf));
|
||||
dns_rdatatype_format(type, typebuf,
|
||||
sizeof(typebuf));
|
||||
dns_rdataclass_format(rdclass, classbuf,
|
||||
sizeof(classbuf));
|
||||
isc_log_write(
|
||||
DNS_LOGCATEGORY_GENERAL,
|
||||
DNS_LOGMODULE_DIFF,
|
||||
ISC_LOG_ERROR,
|
||||
"dns_diff_apply: %s/%s/%s: %s "
|
||||
"%s",
|
||||
namebuf, typebuf, classbuf,
|
||||
optotext(op),
|
||||
isc_result_totext(result));
|
||||
}
|
||||
if (dns_rdataset_isassociated(&ardataset)) {
|
||||
dns_rdataset_disassociate(&ardataset);
|
||||
}
|
||||
CHECK(result);
|
||||
}
|
||||
dns_db_detachnode(&node);
|
||||
if (dns_rdataset_isassociated(&ardataset)) {
|
||||
dns_rdataset_disassociate(&ardataset);
|
||||
result = ISC_R_SUCCESS;
|
||||
break;
|
||||
case DNS_R_NOTEXACT:
|
||||
dns_name_format(name, namebuf, sizeof(namebuf));
|
||||
dns_rdatatype_format(type, typebuf,
|
||||
sizeof(typebuf));
|
||||
dns_rdataclass_format(rdclass, classbuf,
|
||||
sizeof(classbuf));
|
||||
isc_log_write(DNS_LOGCATEGORY_GENERAL,
|
||||
DNS_LOGMODULE_DIFF, ISC_LOG_ERROR,
|
||||
"dns_diff_apply: %s/%s/%s: %s "
|
||||
"%s",
|
||||
namebuf, typebuf, classbuf,
|
||||
optotext(op),
|
||||
isc_result_totext(result));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
CHECK(result);
|
||||
}
|
||||
}
|
||||
return ISC_R_SUCCESS;
|
||||
|
||||
cleanup:
|
||||
if (node != NULL) {
|
||||
dns_db_detachnode(&node);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
dns_diff_apply(const dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *ver) {
|
||||
return diff_apply(diff, db, ver, true);
|
||||
dns_updatectx_t ctx = { .db = db, .ver = ver, .warn = true };
|
||||
dns_rdatacallbacks_t callbacks;
|
||||
dns_rdatacallbacks_init(&callbacks);
|
||||
callbacks.update = update_callback;
|
||||
callbacks.add_private = &ctx;
|
||||
return diff_apply(diff, &callbacks);
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
dns_diff_applysilently(const dns_diff_t *diff, dns_db_t *db,
|
||||
dns_dbversion_t *ver) {
|
||||
return diff_apply(diff, db, ver, false);
|
||||
dns_updatectx_t ctx = { .db = db, .ver = ver, .warn = false };
|
||||
dns_rdatacallbacks_t callbacks;
|
||||
dns_rdatacallbacks_init(&callbacks);
|
||||
callbacks.update = update_callback;
|
||||
callbacks.add_private = &ctx;
|
||||
return diff_apply(diff, &callbacks);
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
dns_diff_apply_with_callbacks(const dns_diff_t *diff,
|
||||
dns_rdatacallbacks_t *callbacks) {
|
||||
REQUIRE(DNS_DIFF_VALID(diff));
|
||||
REQUIRE(callbacks != NULL);
|
||||
REQUIRE(callbacks->update != NULL);
|
||||
|
||||
return diff_apply(diff, callbacks);
|
||||
}
|
||||
|
||||
/* XXX this duplicates lots of code in diff_apply(). */
|
||||
|
|
@ -556,8 +556,9 @@ dns_diff_load(const dns_diff_t *diff, dns_rdatacallbacks_t *callbacks) {
|
|||
rds.trust = dns_trust_ultimate;
|
||||
|
||||
INSIST(op == DNS_DIFFOP_ADD);
|
||||
result = callbacks->add(callbacks->add_private, name,
|
||||
&rds DNS__DB_FILELINE);
|
||||
result = callbacks->update(
|
||||
callbacks->add_private, name, &rds,
|
||||
DNS_DIFFOP_ADD DNS__DB_FILELINE);
|
||||
if (result == DNS_R_UNCHANGED) {
|
||||
isc_log_write(DNS_LOGCATEGORY_GENERAL,
|
||||
DNS_LOGMODULE_DIFF,
|
||||
|
|
|
|||
|
|
@ -34,18 +34,18 @@ struct dns_rdatacallbacks {
|
|||
unsigned int magic;
|
||||
|
||||
/*%
|
||||
* dns_load_master calls 'add' when it has an rdataset to add
|
||||
* to the database. If defined, it calls 'setup' before and
|
||||
* 'commit' after adding rdatasets.
|
||||
* dns_load_master calls 'update' when it has an rdataset to update
|
||||
* in the database. If defined, it calls 'setup' before and
|
||||
* 'commit' after updating rdatasets.
|
||||
*
|
||||
* Some database implementations will commit each rdataset as
|
||||
* soon as it's added, in which case 'setup' and 'commit' need
|
||||
* soon as it's updated, in which case 'setup' and 'commit' need
|
||||
* not be defined. However, other implementations can be
|
||||
* optimized by grouping rdatasets into a transaction; the
|
||||
* setup and commit functions allow this transaction to be
|
||||
* opened and committed.
|
||||
*/
|
||||
dns_addrdatasetfunc_t add;
|
||||
dns_addrdatasetfunc_t update;
|
||||
dns_transactionfunc_t setup;
|
||||
dns_transactionfunc_t commit;
|
||||
|
||||
|
|
|
|||
|
|
@ -89,6 +89,12 @@ typedef struct dns_db_methods {
|
|||
isc_result_t (*beginload)(dns_db_t *db,
|
||||
dns_rdatacallbacks_t *callbacks);
|
||||
isc_result_t (*endload)(dns_db_t *db, dns_rdatacallbacks_t *callbacks);
|
||||
isc_result_t (*beginupdate)(dns_db_t *db, dns_dbversion_t *ver,
|
||||
dns_rdatacallbacks_t *callbacks);
|
||||
isc_result_t (*commitupdate)(dns_db_t *db,
|
||||
dns_rdatacallbacks_t *callbacks);
|
||||
isc_result_t (*abortupdate)(dns_db_t *db,
|
||||
dns_rdatacallbacks_t *callbacks);
|
||||
void (*currentversion)(dns_db_t *db, dns_dbversion_t **versionp);
|
||||
isc_result_t (*newversion)(dns_db_t *db, dns_dbversion_t **versionp);
|
||||
void (*attachversion)(dns_db_t *db, dns_dbversion_t *source,
|
||||
|
|
@ -530,6 +536,90 @@ dns_db_endload(dns_db_t *db, dns_rdatacallbacks_t *callbacks);
|
|||
* implementation used, syntax errors in the master file, etc.
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
dns_db_beginupdate(dns_db_t *db, dns_dbversion_t *ver,
|
||||
dns_rdatacallbacks_t *callbacks);
|
||||
/*%<
|
||||
* Begin updating 'db'.
|
||||
*
|
||||
* Requires:
|
||||
*
|
||||
* \li 'db' is a valid database.
|
||||
*
|
||||
* \li 'callbacks' is a pointer to an initialized dns_rdatacallbacks_t
|
||||
* structure.
|
||||
*
|
||||
* Ensures:
|
||||
*
|
||||
* \li On success, callbacks->add will be a valid dns_addrdatasetfunc_t
|
||||
* suitable for updating records in 'db' from IXFR operations.
|
||||
* callbacks->add_private will be a valid DB update context
|
||||
* which should be used as 'arg' when callbacks->add is called.
|
||||
*
|
||||
* Returns:
|
||||
*
|
||||
* \li #ISC_R_SUCCESS
|
||||
*
|
||||
* \li Other results are possible, depending upon the database
|
||||
* implementation used.
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
dns_db_commitupdate(dns_db_t *db, dns_rdatacallbacks_t *callbacks);
|
||||
/*%<
|
||||
* Commit the update to 'db'. Must be safe to double-call or call after
|
||||
* dns_db_abortupdate.
|
||||
*
|
||||
* Requires:
|
||||
*
|
||||
* \li 'db' is a valid database that is being updated.
|
||||
*
|
||||
* \li 'callbacks' is a valid dns_rdatacallbacks_t structure.
|
||||
*
|
||||
* \li callbacks->add_private is not NULL and is a valid database update
|
||||
* context.
|
||||
*
|
||||
* Ensures:
|
||||
*
|
||||
* \li 'callbacks' is returned to its state prior to calling
|
||||
* dns_db_beginupdate()
|
||||
*
|
||||
* Returns:
|
||||
*
|
||||
* \li #ISC_R_SUCCESS
|
||||
*
|
||||
* \li Other results are possible, depending upon the database
|
||||
* implementation used.
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
dns_db_abortupdate(dns_db_t *db, dns_rdatacallbacks_t *callbacks);
|
||||
/*%<
|
||||
* Abort the update to 'db'. Must be safe to double-call or call after
|
||||
* dns_db_commitupdate.
|
||||
*
|
||||
* Requires:
|
||||
*
|
||||
* \li 'db' is a valid database that is being updated.
|
||||
*
|
||||
* \li 'callbacks' is a valid dns_rdatacallbacks_t structure.
|
||||
*
|
||||
* \li callbacks->add_private is not NULL and is a valid database update
|
||||
* context.
|
||||
*
|
||||
* Ensures:
|
||||
*
|
||||
* \li 'callbacks' is returned to its state prior to calling
|
||||
* dns_db_beginupdate()
|
||||
*
|
||||
* Returns:
|
||||
*
|
||||
* \li #ISC_R_SUCCESS
|
||||
*
|
||||
* \li Other results are possible, depending upon the database
|
||||
* implementation used.
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
dns_db_load(dns_db_t *db, const char *filename, dns_masterformat_t format,
|
||||
unsigned int options);
|
||||
|
|
|
|||
|
|
@ -59,14 +59,6 @@
|
|||
* timeexpire.
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
DNS_DIFFOP_ADD = 0, /*%< Add an RR. */
|
||||
DNS_DIFFOP_DEL = 1, /*%< Delete an RR. */
|
||||
DNS_DIFFOP_EXISTS = 2, /*%< Assert RR existence. */
|
||||
DNS_DIFFOP_ADDRESIGN = 4, /*%< ADD + RESIGN. */
|
||||
DNS_DIFFOP_DELRESIGN = 5 /*%< DEL + RESIGN. */
|
||||
} dns_diffop_t;
|
||||
|
||||
typedef struct dns_difftuple dns_difftuple_t;
|
||||
typedef ISC_LIST(dns_difftuple_t) dns_difftuplelist_t;
|
||||
|
||||
|
|
@ -259,6 +251,25 @@ dns_diff_applysilently(const dns_diff_t *diff, dns_db_t *db,
|
|||
*
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
dns_db_t *db;
|
||||
dns_dbversion_t *ver;
|
||||
bool warn;
|
||||
} dns_updatectx_t;
|
||||
|
||||
isc_result_t
|
||||
dns_diff_apply_with_callbacks(const dns_diff_t *diff,
|
||||
dns_rdatacallbacks_t *callbacks);
|
||||
/*%<
|
||||
* Apply 'diff' to the database using the provided callbacks and context.
|
||||
* The context contains the database, version, and warning flag.
|
||||
* This allows for custom callback implementations.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'callbacks' points to a valid dns_rdatacallbacks_t structure
|
||||
*\li 'callbacks->update' is not NULL
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
dns_diff_load(const dns_diff_t *diff, dns_rdatacallbacks_t *callbacks);
|
||||
/*%<
|
||||
|
|
|
|||
|
|
@ -632,6 +632,19 @@ dns_rdataset_getheader(const dns_rdataset_t *rdataset);
|
|||
* \li 'rdataset' is a valid rdataset.
|
||||
*/
|
||||
|
||||
isc_stdtime_t
|
||||
dns_rdataset_minresign(dns_rdataset_t *rdataset);
|
||||
/*%<
|
||||
* Return the minimum resign time from an RRSIG rdataset.
|
||||
*
|
||||
* This function iterates through all RRSIG records in the rdataset
|
||||
* and returns the earliest expiration time, which indicates when
|
||||
* the signatures should be resigned.
|
||||
*
|
||||
* Requires:
|
||||
* \li 'rdataset' is a valid rdataset.
|
||||
*/
|
||||
|
||||
/*%
|
||||
* Returns true if the rdataset is of type 'type', or type RRSIG
|
||||
* and covers 'type'.
|
||||
|
|
|
|||
|
|
@ -183,6 +183,14 @@ typedef struct dst_gssapi_signverifyctx dst_gssapi_signverifyctx_t;
|
|||
|
||||
typedef enum { dns_hash_sha1 = 1 } dns_hash_t;
|
||||
|
||||
typedef enum {
|
||||
DNS_DIFFOP_ADD = 0, /*%< Add an RR. */
|
||||
DNS_DIFFOP_DEL = 1, /*%< Delete an RR. */
|
||||
DNS_DIFFOP_EXISTS = 2, /*%< Assert RR existence. */
|
||||
DNS_DIFFOP_ADDRESIGN = 4, /*%< ADD + RESIGN. */
|
||||
DNS_DIFFOP_DELRESIGN = 5 /*%< DEL + RESIGN. */
|
||||
} dns_diffop_t;
|
||||
|
||||
typedef enum {
|
||||
dns_fwdpolicy_none = 0,
|
||||
dns_fwdpolicy_first = 1,
|
||||
|
|
@ -424,8 +432,8 @@ typedef void (*dns_loaddonefunc_t)(void *, isc_result_t);
|
|||
typedef void (*dns_rawdatafunc_t)(dns_zone_t *, dns_masterrawheader_t *);
|
||||
|
||||
typedef isc_result_t (*dns_addrdatasetfunc_t)(void *arg, const dns_name_t *name,
|
||||
dns_rdataset_t *rdataset
|
||||
DNS__DB_FLARG);
|
||||
dns_rdataset_t *rdataset,
|
||||
dns_diffop_t op DNS__DB_FLARG);
|
||||
typedef void (*dns_transactionfunc_t)(void *arg);
|
||||
|
||||
typedef isc_result_t (*dns_additionaldatafunc_t)(
|
||||
|
|
|
|||
|
|
@ -498,7 +498,7 @@ loadctx_create(dns_masterformat_t format, isc_mem_t *mctx, unsigned int options,
|
|||
|
||||
REQUIRE(lctxp != NULL && *lctxp == NULL);
|
||||
REQUIRE(callbacks != NULL);
|
||||
REQUIRE(callbacks->add != NULL);
|
||||
REQUIRE(callbacks->update != NULL);
|
||||
REQUIRE(callbacks->error != NULL);
|
||||
REQUIRE(callbacks->warn != NULL);
|
||||
REQUIRE(mctx != NULL);
|
||||
|
|
@ -2857,8 +2857,9 @@ commit(dns_rdatacallbacks_t *callbacks, dns_loadctx_t *lctx,
|
|||
dataset.attributes.resign = true;
|
||||
dataset.resign = resign_fromlist(this, lctx);
|
||||
}
|
||||
result = callbacks->add(callbacks->add_private, owner,
|
||||
&dataset DNS__DB_FILELINE);
|
||||
result = callbacks->update(callbacks->add_private, owner,
|
||||
&dataset,
|
||||
DNS_DIFFOP_ADD DNS__DB_FILELINE);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
dns_name_format(owner, namebuf, sizeof(namebuf));
|
||||
if (source != NULL) {
|
||||
|
|
|
|||
354
lib/dns/qpzone.c
354
lib/dns/qpzone.c
|
|
@ -42,6 +42,7 @@
|
|||
#include <dns/callbacks.h>
|
||||
#include <dns/db.h>
|
||||
#include <dns/dbiterator.h>
|
||||
#include <dns/diff.h>
|
||||
#include <dns/dnssec.h>
|
||||
#include <dns/fixedname.h>
|
||||
#include <dns/masterdump.h>
|
||||
|
|
@ -96,6 +97,15 @@ typedef struct qpzone_bucket {
|
|||
(sizeof(isc_rwlock_t)) % ISC_OS_CACHELINE_SIZE];
|
||||
} qpzone_bucket_t;
|
||||
|
||||
/*
|
||||
* Qpzone-specific update context that extends dns_updatectx_t, used in IXFR.
|
||||
*/
|
||||
typedef struct qpzone_updatectx {
|
||||
dns_updatectx_t base;
|
||||
dns_qp_t *qp;
|
||||
dns_qpread_t qpr;
|
||||
} qpzone_updatectx_t;
|
||||
|
||||
static qpzone_bucket_t qpzone_buckets_g[1024];
|
||||
|
||||
typedef struct qpz_changed {
|
||||
|
|
@ -2063,8 +2073,8 @@ addwildcards(qpzonedb_t *qpdb, dns_qp_t *qp, const dns_name_t *name,
|
|||
}
|
||||
|
||||
static isc_result_t
|
||||
loading_addrdataset(void *arg, const dns_name_t *name,
|
||||
dns_rdataset_t *rdataset DNS__DB_FLARG) {
|
||||
loading_addrdataset(void *arg, const dns_name_t *name, dns_rdataset_t *rdataset,
|
||||
dns_diffop_t op ISC_ATTR_UNUSED DNS__DB_FLARG) {
|
||||
qpz_load_t *loadctx = arg;
|
||||
qpzonedb_t *qpdb = (qpzonedb_t *)loadctx->db;
|
||||
qpznode_t *node = NULL;
|
||||
|
|
@ -2194,7 +2204,7 @@ beginload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
|
|||
|
||||
RWUNLOCK(&qpdb->lock, isc_rwlocktype_write);
|
||||
|
||||
callbacks->add = loading_addrdataset;
|
||||
callbacks->update = loading_addrdataset;
|
||||
callbacks->setup = loading_setup;
|
||||
callbacks->commit = loading_commit;
|
||||
callbacks->add_private = loadctx;
|
||||
|
|
@ -2229,7 +2239,7 @@ endload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
|
|||
RWUNLOCK(&qpdb->lock, isc_rwlocktype_write);
|
||||
}
|
||||
|
||||
callbacks->add = NULL;
|
||||
callbacks->update = NULL;
|
||||
callbacks->setup = NULL;
|
||||
callbacks->commit = NULL;
|
||||
callbacks->add_private = NULL;
|
||||
|
|
@ -2430,57 +2440,99 @@ setgluecachestats(dns_db_t *db, isc_stats_t *stats) {
|
|||
return ISC_R_SUCCESS;
|
||||
}
|
||||
|
||||
static isc_result_t
|
||||
findnodeintree(qpzonedb_t *qpdb, const dns_name_t *name, bool create,
|
||||
bool nsec3, dns_dbnode_t **nodep DNS__DB_FLARG) {
|
||||
isc_result_t result;
|
||||
qpznode_t *node = NULL;
|
||||
dns_namespace_t nspace = nsec3 ? DNS_DBNAMESPACE_NSEC3
|
||||
: DNS_DBNAMESPACE_NORMAL;
|
||||
dns_qpread_t qpr = { 0 };
|
||||
static dns_qp_t *
|
||||
begin_transaction(qpzonedb_t *qpdb, dns_qpread_t *qprp, bool create) {
|
||||
dns_qp_t *qp = NULL;
|
||||
|
||||
if (create) {
|
||||
dns_qpmulti_write(qpdb->tree, &qp);
|
||||
} else {
|
||||
dns_qpmulti_query(qpdb->tree, &qpr);
|
||||
qp = (dns_qp_t *)&qpr;
|
||||
dns_qpmulti_query(qpdb->tree, qprp);
|
||||
qp = (dns_qp_t *)qprp;
|
||||
}
|
||||
|
||||
result = dns_qp_getname(qp, name, nspace, (void **)&node, NULL);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
if (!create) {
|
||||
dns_qpread_destroy(qpdb->tree, &qpr);
|
||||
return result;
|
||||
}
|
||||
return qp;
|
||||
}
|
||||
|
||||
static void
|
||||
end_transaction(qpzonedb_t *qpdb, dns_qp_t *qp, bool create) {
|
||||
if (create) {
|
||||
dns_qp_compact(qp, DNS_QPGC_MAYBE);
|
||||
dns_qpmulti_commit(qpdb->tree, &qp);
|
||||
} else {
|
||||
dns_qpread_t *qprp = (dns_qpread_t *)qp;
|
||||
dns_qpread_destroy(qpdb->tree, qprp);
|
||||
}
|
||||
}
|
||||
|
||||
static isc_result_t
|
||||
findnodeintree(qpzonedb_t *qpdb, dns_qp_t *qp, const dns_name_t *name,
|
||||
bool create, bool nsec3, dns_dbnode_t **nodep DNS__DB_FLARG) {
|
||||
isc_result_t result;
|
||||
qpznode_t *node = NULL;
|
||||
dns_namespace_t nspace = nsec3 ? DNS_DBNAMESPACE_NSEC3
|
||||
: DNS_DBNAMESPACE_NORMAL;
|
||||
/*
|
||||
* findnodeintree is a wrapper around dns_qp_getname that does some
|
||||
* qpzone-specific bookkeeping before returning the lookup result to the
|
||||
* caller.
|
||||
*
|
||||
* First, we do a lookup ...
|
||||
*/
|
||||
result = dns_qp_getname(qp, name, nspace, (void **)&node, NULL);
|
||||
if (result == ISC_R_SUCCESS) {
|
||||
/*
|
||||
* ... if the lookup is successful, we need to increase both the
|
||||
* internal and external reference count before returning to
|
||||
* the caller. qpznode_acquire takes care of that.
|
||||
*/
|
||||
qpznode_acquire(node DNS__DB_FLARG_PASS);
|
||||
|
||||
INSIST(node->nspace == DNS_DBNAMESPACE_NSEC3 || !nsec3);
|
||||
} else if (result != ISC_R_SUCCESS && create) {
|
||||
/*
|
||||
* ... if the lookup is unsuccesful, but the caller asked us to
|
||||
* create a new node, create one and insert it into the tree.
|
||||
*/
|
||||
node = new_qpznode(qpdb, name, nspace);
|
||||
result = dns_qp_insert(qp, node, 0);
|
||||
INSIST(result == ISC_R_SUCCESS);
|
||||
qpznode_unref(node);
|
||||
|
||||
/*
|
||||
* The new node now has two internal references:
|
||||
* - One from new_qpznode, that initializes references at 1.
|
||||
* - One from attach_leaf, that increases the reference by
|
||||
* one at insertion in the qp-tree.
|
||||
* We want the node to have two internal and one external
|
||||
* reference:
|
||||
* - One internal reference from the qp-tree.
|
||||
* - One internal and one external reference from the caller.
|
||||
*
|
||||
* So we increase the external reference count by one.
|
||||
*/
|
||||
qpznode_erefs_increment(node DNS__DB_FLARG_PASS);
|
||||
|
||||
if (!nsec3) {
|
||||
/*
|
||||
* Add empty non-terminal nodes to help with wildcards.
|
||||
*/
|
||||
addwildcards(qpdb, qp, name, nspace);
|
||||
if (dns_name_iswildcard(name)) {
|
||||
wildcardmagic(qpdb, qp, name, nspace);
|
||||
}
|
||||
}
|
||||
|
||||
INSIST(node->nspace == DNS_DBNAMESPACE_NSEC3 || !nsec3);
|
||||
}
|
||||
|
||||
INSIST(node->nspace == DNS_DBNAMESPACE_NSEC3 || !nsec3);
|
||||
|
||||
qpznode_acquire(node DNS__DB_FLARG_PASS);
|
||||
|
||||
if (create) {
|
||||
dns_qp_compact(qp, DNS_QPGC_MAYBE);
|
||||
dns_qpmulti_commit(qpdb->tree, &qp);
|
||||
} else {
|
||||
dns_qpread_destroy(qpdb->tree, &qpr);
|
||||
}
|
||||
/*
|
||||
* ... if the lookup is unsucessful, and the caller didn't ask us
|
||||
* to create a new node, there is nothing to do. Return the result
|
||||
* of the lookup to the caller, and set *nodep to NULL
|
||||
*/
|
||||
|
||||
*nodep = (dns_dbnode_t *)node;
|
||||
|
||||
return ISC_R_SUCCESS;
|
||||
return result;
|
||||
}
|
||||
|
||||
static isc_result_t
|
||||
|
|
@ -2492,8 +2544,15 @@ qpzone_findnode(dns_db_t *db, const dns_name_t *name, bool create,
|
|||
|
||||
REQUIRE(VALID_QPZONE(qpdb));
|
||||
|
||||
return findnodeintree(qpdb, name, create, false,
|
||||
nodep DNS__DB_FLARG_PASS);
|
||||
dns_qpread_t qpr = { 0 };
|
||||
dns_qp_t *qp = begin_transaction(qpdb, &qpr, create);
|
||||
|
||||
isc_result_t result = findnodeintree(qpdb, qp, name, create, false,
|
||||
nodep DNS__DB_FLARG_PASS);
|
||||
|
||||
end_transaction(qpdb, qp, create);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static isc_result_t
|
||||
|
|
@ -2503,8 +2562,15 @@ qpzone_findnsec3node(dns_db_t *db, const dns_name_t *name, bool create,
|
|||
|
||||
REQUIRE(VALID_QPZONE(qpdb));
|
||||
|
||||
return findnodeintree(qpdb, name, create, true,
|
||||
nodep DNS__DB_FLARG_PASS);
|
||||
dns_qpread_t qpr = { 0 };
|
||||
dns_qp_t *qp = begin_transaction(qpdb, &qpr, create);
|
||||
|
||||
isc_result_t result = findnodeintree(qpdb, qp, name, create, true,
|
||||
nodep DNS__DB_FLARG_PASS);
|
||||
|
||||
end_transaction(qpdb, qp, create);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool
|
||||
|
|
@ -4592,12 +4658,21 @@ qpzone_createiterator(dns_db_t *db, unsigned int options,
|
|||
return ISC_R_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Main logic to add a new rdataset to a node.
|
||||
*
|
||||
* The reason this is split from qpzone_addrdataset is to allow the reuse of
|
||||
* the same qp transaction for multiple adds.
|
||||
*
|
||||
* qpzone_subtractrdataset doesn't have the same problem since it cannot delete
|
||||
* nodes, only rdatasets.
|
||||
*/
|
||||
static isc_result_t
|
||||
qpzone_addrdataset(dns_db_t *db, dns_dbnode_t *dbnode,
|
||||
dns_dbversion_t *dbversion,
|
||||
isc_stdtime_t now ISC_ATTR_UNUSED, dns_rdataset_t *rdataset,
|
||||
unsigned int options,
|
||||
dns_rdataset_t *addedrdataset DNS__DB_FLARG) {
|
||||
qpzone_addrdataset_inner(dns_db_t *db, dns_dbnode_t *dbnode,
|
||||
dns_dbversion_t *dbversion,
|
||||
isc_stdtime_t now ISC_ATTR_UNUSED,
|
||||
dns_rdataset_t *rdataset, unsigned int options,
|
||||
dns_rdataset_t *addedrdataset, dns_qp_t *nsec) {
|
||||
isc_result_t result;
|
||||
qpzonedb_t *qpdb = (qpzonedb_t *)db;
|
||||
qpznode_t *node = (qpznode_t *)dbnode;
|
||||
|
|
@ -4608,7 +4683,6 @@ qpzone_addrdataset(dns_db_t *db, dns_dbnode_t *dbnode,
|
|||
isc_rwlock_t *nlock = NULL;
|
||||
dns_fixedname_t fn;
|
||||
dns_name_t *name = dns_fixedname_initname(&fn);
|
||||
dns_qp_t *nsec = NULL;
|
||||
|
||||
REQUIRE(VALID_QPZONE(qpdb));
|
||||
REQUIRE(version != NULL && version->qpdb == qpdb);
|
||||
|
|
@ -4662,9 +4736,8 @@ qpzone_addrdataset(dns_db_t *db, dns_dbnode_t *dbnode,
|
|||
/*
|
||||
* Add to the auxiliary NSEC tree if we're adding an NSEC record.
|
||||
*/
|
||||
if (!node->havensec && rdataset->type == dns_rdatatype_nsec) {
|
||||
dns_qpmulti_write(qpdb->tree, &nsec);
|
||||
}
|
||||
bool is_nsec = !node->havensec && rdataset->type == dns_rdatatype_nsec;
|
||||
REQUIRE(!is_nsec || nsec != NULL);
|
||||
|
||||
/*
|
||||
* If we're adding a delegation type or adding to the auxiliary NSEC
|
||||
|
|
@ -4680,7 +4753,7 @@ qpzone_addrdataset(dns_db_t *db, dns_dbnode_t *dbnode,
|
|||
NODE_WRLOCK(nlock, &nlocktype);
|
||||
|
||||
result = ISC_R_SUCCESS;
|
||||
if (nsec != NULL) {
|
||||
if (is_nsec) {
|
||||
node->havensec = true;
|
||||
|
||||
/*
|
||||
|
|
@ -4690,6 +4763,10 @@ qpzone_addrdataset(dns_db_t *db, dns_dbnode_t *dbnode,
|
|||
*/
|
||||
qpznode_t *nsecnode = new_qpznode(qpdb, name,
|
||||
DNS_DBNAMESPACE_NSEC);
|
||||
/*
|
||||
* We don't need a separate transaction since the NSEC tree and
|
||||
* the normal tree are part of the same qp-tree.
|
||||
*/
|
||||
(void)dns_qp_insert(nsec, nsecnode, 0);
|
||||
qpznode_detach(&nsecnode);
|
||||
}
|
||||
|
|
@ -4711,6 +4788,32 @@ qpzone_addrdataset(dns_db_t *db, dns_dbnode_t *dbnode,
|
|||
|
||||
NODE_UNLOCK(nlock, &nlocktype);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static isc_result_t
|
||||
qpzone_addrdataset(dns_db_t *db, dns_dbnode_t *dbnode,
|
||||
dns_dbversion_t *dbversion,
|
||||
isc_stdtime_t now ISC_ATTR_UNUSED, dns_rdataset_t *rdataset,
|
||||
unsigned int options,
|
||||
dns_rdataset_t *addedrdataset DNS__DB_FLARG) {
|
||||
qpzonedb_t *qpdb = (qpzonedb_t *)db;
|
||||
qpznode_t *node = (qpznode_t *)dbnode;
|
||||
dns_qp_t *nsec = NULL;
|
||||
|
||||
REQUIRE(VALID_QPZONE(qpdb));
|
||||
|
||||
/*
|
||||
* Add to the auxiliary NSEC tree if we're adding an NSEC record.
|
||||
*/
|
||||
if (!node->havensec && rdataset->type == dns_rdatatype_nsec) {
|
||||
dns_qpmulti_write(qpdb->tree, &nsec);
|
||||
}
|
||||
|
||||
isc_result_t result = qpzone_addrdataset_inner(db, dbnode, dbversion,
|
||||
now, rdataset, options,
|
||||
addedrdataset, nsec);
|
||||
|
||||
if (nsec != NULL) {
|
||||
dns_qpmulti_commit(qpdb->tree, &nsec);
|
||||
}
|
||||
|
|
@ -5270,10 +5373,167 @@ setmaxtypepername(dns_db_t *db, uint32_t value) {
|
|||
qpdb->maxtypepername = value;
|
||||
}
|
||||
|
||||
/*
|
||||
* Qpzone specialization of the update function from dns_rdatacallbacks_t,
|
||||
* meant to reuse the same qp transaction for multiple operations.
|
||||
*/
|
||||
static isc_result_t
|
||||
qpzone_update_rdataset(qpzonedb_t *qpdb, qpz_version_t *version, dns_qp_t *qp,
|
||||
dns_name_t *name, dns_rdataset_t *rds, dns_diffop_t op) {
|
||||
isc_result_t result;
|
||||
unsigned int options;
|
||||
dns_rdataset_t ardataset;
|
||||
dns_dbnode_t *node = NULL;
|
||||
bool is_nsec3;
|
||||
|
||||
dns_rdataset_init(&ardataset);
|
||||
|
||||
is_nsec3 = (rds->type == dns_rdatatype_nsec3 ||
|
||||
rds->covers == dns_rdatatype_nsec3);
|
||||
|
||||
result = findnodeintree(qpdb, qp, name, true, is_nsec3,
|
||||
&node DNS__DB_FLARG_PASS);
|
||||
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
goto failure;
|
||||
}
|
||||
|
||||
switch (op) {
|
||||
case DNS_DIFFOP_ADD:
|
||||
case DNS_DIFFOP_ADDRESIGN:
|
||||
/*
|
||||
* See the comment on qpzone_addrdataset_inner for why we
|
||||
* cannot use qpzone_addrdataset directly.
|
||||
*/
|
||||
options = DNS_DBADD_MERGE | DNS_DBADD_EXACT |
|
||||
DNS_DBADD_EXACTTTL;
|
||||
result = qpzone_addrdataset_inner(
|
||||
(dns_db_t *)qpdb, node, (dns_dbversion_t *)version, 0,
|
||||
rds, options, &ardataset, qp DNS__DB_FLARG_PASS);
|
||||
break;
|
||||
case DNS_DIFFOP_DEL:
|
||||
case DNS_DIFFOP_DELRESIGN:
|
||||
options = DNS_DBSUB_EXACT | DNS_DBSUB_WANTOLD;
|
||||
result = qpzone_subtractrdataset(
|
||||
(dns_db_t *)qpdb, node, (dns_dbversion_t *)version, rds,
|
||||
options, &ardataset DNS__DB_FLARG_PASS);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
bool is_resign = rds->type == dns_rdatatype_rrsig &&
|
||||
(op == DNS_DIFFOP_DELRESIGN ||
|
||||
op == DNS_DIFFOP_ADDRESIGN);
|
||||
|
||||
if (result == ISC_R_SUCCESS && is_resign) {
|
||||
isc_stdtime_t resign;
|
||||
resign = dns_rdataset_minresign(&ardataset);
|
||||
dns_db_setsigningtime((dns_db_t *)qpdb, &ardataset, resign);
|
||||
}
|
||||
|
||||
failure:
|
||||
if (node != NULL) {
|
||||
dns_db_detachnode(&node);
|
||||
}
|
||||
if (dns_rdataset_isassociated(&ardataset)) {
|
||||
dns_rdataset_disassociate(&ardataset);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static isc_result_t
|
||||
qpzone_update_callback(void *arg, const dns_name_t *name, dns_rdataset_t *rds,
|
||||
dns_diffop_t op DNS__DB_FLARG) {
|
||||
qpzone_updatectx_t *ctx = arg;
|
||||
qpzonedb_t *qpdb = (qpzonedb_t *)ctx->base.db;
|
||||
qpz_version_t *version = (qpz_version_t *)ctx->base.ver;
|
||||
|
||||
return qpzone_update_rdataset(qpdb, version, ctx->qp,
|
||||
(dns_name_t *)name, rds, op);
|
||||
}
|
||||
|
||||
static isc_result_t
|
||||
qpzone_beginupdate(dns_db_t *db, dns_dbversion_t *ver,
|
||||
dns_rdatacallbacks_t *callbacks) {
|
||||
qpzonedb_t *qpdb = (qpzonedb_t *)db;
|
||||
|
||||
REQUIRE(VALID_QPZONE(qpdb));
|
||||
REQUIRE(ver != NULL);
|
||||
REQUIRE(DNS_CALLBACK_VALID(callbacks));
|
||||
|
||||
qpzone_updatectx_t *ctx = isc_mem_get(qpdb->common.mctx, sizeof(*ctx));
|
||||
*ctx = (qpzone_updatectx_t){
|
||||
.base.db = db,
|
||||
.base.ver = ver,
|
||||
.base.warn = true,
|
||||
.qpr = (dns_qpread_t){ 0 },
|
||||
};
|
||||
ctx->qp = begin_transaction(qpdb, &ctx->qpr, true);
|
||||
|
||||
callbacks->update = qpzone_update_callback;
|
||||
callbacks->add_private = ctx;
|
||||
|
||||
return ISC_R_SUCCESS;
|
||||
}
|
||||
|
||||
static isc_result_t
|
||||
qpzone_commitupdate(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
|
||||
qpzonedb_t *qpdb = (qpzonedb_t *)db;
|
||||
qpzone_updatectx_t *ctx;
|
||||
|
||||
REQUIRE(VALID_QPZONE(qpdb));
|
||||
REQUIRE(DNS_CALLBACK_VALID(callbacks));
|
||||
|
||||
ctx = (qpzone_updatectx_t *)callbacks->add_private;
|
||||
if (ctx != NULL) {
|
||||
end_transaction(qpdb, ctx->qp, true);
|
||||
|
||||
isc_mem_put(qpdb->common.mctx, ctx, sizeof(*ctx));
|
||||
/*
|
||||
* We need to allow the context to be committed or aborted
|
||||
* multiple times, so we set the callback data to NULL
|
||||
* to signal that nothing needs to be done with this context.
|
||||
*/
|
||||
callbacks->add_private = NULL;
|
||||
}
|
||||
|
||||
return ISC_R_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* For now, abortupdate needs to *commit* the results, so that closeversion
|
||||
* cleanup might work.
|
||||
*/
|
||||
static isc_result_t
|
||||
qpzone_abortupdate(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
|
||||
qpzonedb_t *qpdb = (qpzonedb_t *)db;
|
||||
qpzone_updatectx_t *ctx;
|
||||
|
||||
REQUIRE(VALID_QPZONE(qpdb));
|
||||
REQUIRE(DNS_CALLBACK_VALID(callbacks));
|
||||
|
||||
ctx = (qpzone_updatectx_t *)callbacks->add_private;
|
||||
if (ctx != NULL) {
|
||||
end_transaction(qpdb, ctx->qp, true);
|
||||
|
||||
isc_mem_put(qpdb->common.mctx, ctx, sizeof(*ctx));
|
||||
/*
|
||||
* See qpzone_commitupdate.
|
||||
*/
|
||||
callbacks->add_private = NULL;
|
||||
}
|
||||
|
||||
return ISC_R_SUCCESS;
|
||||
}
|
||||
|
||||
static dns_dbmethods_t qpdb_zonemethods = {
|
||||
.destroy = qpdb_destroy,
|
||||
.beginload = beginload,
|
||||
.endload = endload,
|
||||
.beginupdate = qpzone_beginupdate,
|
||||
.commitupdate = qpzone_commitupdate,
|
||||
.abortupdate = qpzone_abortupdate,
|
||||
.currentversion = currentversion,
|
||||
.newversion = newversion,
|
||||
.attachversion = attachversion,
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
#include <dns/ncache.h>
|
||||
#include <dns/rdata.h>
|
||||
#include <dns/rdataset.h>
|
||||
#include <dns/time.h>
|
||||
#include <dns/types.h>
|
||||
|
||||
#define MAX_SHUFFLE 100
|
||||
|
|
@ -597,3 +598,41 @@ dns_rdataset_getheader(const dns_rdataset_t *rdataset) {
|
|||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
isc_stdtime_t
|
||||
dns_rdataset_minresign(dns_rdataset_t *rdataset) {
|
||||
dns_rdata_t rdata = DNS_RDATA_INIT;
|
||||
dns_rdata_rrsig_t sig;
|
||||
int64_t when;
|
||||
isc_result_t result;
|
||||
|
||||
REQUIRE(DNS_RDATASET_VALID(rdataset));
|
||||
|
||||
result = dns_rdataset_first(rdataset);
|
||||
INSIST(result == ISC_R_SUCCESS);
|
||||
dns_rdataset_current(rdataset, &rdata);
|
||||
(void)dns_rdata_tostruct(&rdata, &sig, NULL);
|
||||
if ((rdata.flags & DNS_RDATA_OFFLINE) != 0) {
|
||||
when = 0;
|
||||
} else {
|
||||
when = dns_time64_from32(sig.timeexpire);
|
||||
}
|
||||
dns_rdata_reset(&rdata);
|
||||
|
||||
result = dns_rdataset_next(rdataset);
|
||||
while (result == ISC_R_SUCCESS) {
|
||||
dns_rdataset_current(rdataset, &rdata);
|
||||
(void)dns_rdata_tostruct(&rdata, &sig, NULL);
|
||||
if ((rdata.flags & DNS_RDATA_OFFLINE) != 0) {
|
||||
goto next_rr;
|
||||
}
|
||||
if (when == 0 || dns_time64_from32(sig.timeexpire) < when) {
|
||||
when = dns_time64_from32(sig.timeexpire);
|
||||
}
|
||||
next_rr:
|
||||
dns_rdata_reset(&rdata);
|
||||
result = dns_rdataset_next(rdataset);
|
||||
}
|
||||
INSIST(result == ISC_R_NOMORE);
|
||||
return (isc_stdtime_t)when;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,6 +80,8 @@ typedef enum {
|
|||
* Incoming zone transfer context.
|
||||
*/
|
||||
|
||||
typedef struct dns_ixfr dns_ixfr_t;
|
||||
|
||||
struct dns_xfrin {
|
||||
unsigned int magic;
|
||||
isc_mem_t *mctx;
|
||||
|
|
@ -172,7 +174,7 @@ struct dns_xfrin {
|
|||
*/
|
||||
dns_rdatacallbacks_t axfr;
|
||||
|
||||
struct {
|
||||
struct dns_ixfr {
|
||||
uint32_t diffs;
|
||||
uint32_t maxdiffs;
|
||||
uint32_t request_serial;
|
||||
|
|
@ -488,24 +490,22 @@ cleanup:
|
|||
}
|
||||
|
||||
static isc_result_t
|
||||
ixfr_begin_transaction(dns_xfrin_t *xfr) {
|
||||
ixfr_begin_transaction(dns_ixfr_t *ixfr) {
|
||||
isc_result_t result = ISC_R_SUCCESS;
|
||||
|
||||
if (xfr->ixfr.journal != NULL) {
|
||||
CHECK(dns_journal_begin_transaction(xfr->ixfr.journal));
|
||||
if (ixfr->journal != NULL) {
|
||||
CHECK(dns_journal_begin_transaction(ixfr->journal));
|
||||
}
|
||||
cleanup:
|
||||
return result;
|
||||
}
|
||||
|
||||
static isc_result_t
|
||||
ixfr_end_transaction(dns_xfrin_t *xfr) {
|
||||
ixfr_end_transaction(dns_ixfr_t *ixfr) {
|
||||
isc_result_t result = ISC_R_SUCCESS;
|
||||
|
||||
CHECK(dns_zone_verifydb(xfr->zone, xfr->db, xfr->ver));
|
||||
/* XXX enter ready-to-commit state here */
|
||||
if (xfr->ixfr.journal != NULL) {
|
||||
CHECK(dns_journal_commit(xfr->ixfr.journal));
|
||||
if (ixfr->journal != NULL) {
|
||||
CHECK(dns_journal_commit(ixfr->journal));
|
||||
}
|
||||
cleanup:
|
||||
return result;
|
||||
|
|
@ -516,9 +516,14 @@ ixfr_apply_one(dns_xfrin_t *xfr, ixfr_apply_data_t *data) {
|
|||
isc_result_t result = ISC_R_SUCCESS;
|
||||
uint64_t records;
|
||||
|
||||
CHECK(ixfr_begin_transaction(xfr));
|
||||
dns_rdatacallbacks_t callbacks;
|
||||
dns_rdatacallbacks_init(&callbacks);
|
||||
|
||||
CHECK(dns_diff_apply(&data->diff, xfr->db, xfr->ver));
|
||||
CHECK(ixfr_begin_transaction(&xfr->ixfr));
|
||||
|
||||
dns_db_beginupdate(xfr->db, xfr->ver, &callbacks);
|
||||
|
||||
CHECK(dns_diff_apply_with_callbacks(&data->diff, &callbacks));
|
||||
if (xfr->maxrecords != 0U) {
|
||||
result = dns_db_getsize(xfr->db, xfr->ver, &records, NULL);
|
||||
if (result == ISC_R_SUCCESS && records > xfr->maxrecords) {
|
||||
|
|
@ -528,13 +533,36 @@ ixfr_apply_one(dns_xfrin_t *xfr, ixfr_apply_data_t *data) {
|
|||
if (xfr->ixfr.journal != NULL) {
|
||||
CHECK(dns_journal_writediff(xfr->ixfr.journal, &data->diff));
|
||||
}
|
||||
/*
|
||||
* In the current implementation, we always commit the results, since
|
||||
* dns_zone_verifydb doesn't know how to access the in-progress
|
||||
* transaction.
|
||||
*/
|
||||
dns_db_commitupdate(xfr->db, &callbacks);
|
||||
|
||||
result = ixfr_end_transaction(xfr);
|
||||
/*
|
||||
* At the moment, rdatacallbacks doesn't offer a way to inspect the
|
||||
* result of a transaction before committing it.
|
||||
*
|
||||
* So we need to commit *before* calling dns_zone_verifydb, and rely
|
||||
* on closeversion to actually do cleanup.
|
||||
*/
|
||||
dns_db_commitupdate(xfr->db, &callbacks);
|
||||
|
||||
CHECK(dns_zone_verifydb(xfr->zone, xfr->db, xfr->ver));
|
||||
|
||||
result = ixfr_end_transaction(&xfr->ixfr);
|
||||
|
||||
return result;
|
||||
cleanup:
|
||||
/*
|
||||
* For the reason stated above, dns_db_abortupdate must *commit* the
|
||||
* changes and rely on closeversion to clean them up.
|
||||
*/
|
||||
dns_db_abortupdate(xfr->db, &callbacks);
|
||||
|
||||
/* We need to end the transaction, but keep the previous error */
|
||||
(void)ixfr_end_transaction(xfr);
|
||||
(void)ixfr_end_transaction(&xfr->ixfr);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,13 +65,14 @@ static void
|
|||
rawdata_callback(dns_zone_t *zone, dns_masterrawheader_t *header);
|
||||
|
||||
static isc_result_t
|
||||
add_callback(void *arg, const dns_name_t *owner,
|
||||
dns_rdataset_t *dataset DNS__DB_FLARG) {
|
||||
add_callback(void *arg, const dns_name_t *owner, dns_rdataset_t *dataset,
|
||||
dns_diffop_t op DNS__DB_FLARG) {
|
||||
char buf[BIGBUFLEN];
|
||||
isc_buffer_t target;
|
||||
isc_result_t result;
|
||||
|
||||
UNUSED(arg);
|
||||
UNUSED(op);
|
||||
|
||||
isc_buffer_init(&target, buf, BIGBUFLEN);
|
||||
result = dns_rdataset_totext(dataset, owner, false, false, &target);
|
||||
|
|
@ -102,7 +103,7 @@ setup_master(void (*warn)(struct dns_rdatacallbacks *, const char *, ...),
|
|||
RETERR(dns_name_fromtext(dns_origin, &source, dns_rootname, 0));
|
||||
|
||||
dns_rdatacallbacks_init_stdio(&callbacks);
|
||||
callbacks.add = add_callback;
|
||||
callbacks.update = add_callback;
|
||||
callbacks.rawdata = rawdata_callback;
|
||||
callbacks.zone = NULL;
|
||||
if (warn != NULL) {
|
||||
|
|
@ -125,7 +126,7 @@ test_master(const char *workdir, const char *testfile,
|
|||
RETERR(setup_master(warn, error));
|
||||
|
||||
dns_rdatacallbacks_init_stdio(&callbacks);
|
||||
callbacks.add = add_callback;
|
||||
callbacks.update = add_callback;
|
||||
callbacks.rawdata = rawdata_callback;
|
||||
callbacks.zone = NULL;
|
||||
if (warn != NULL) {
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@
|
|||
#include <isc/lib.h>
|
||||
#include <isc/util.h>
|
||||
|
||||
#include <dns/callbacks.h>
|
||||
#include <dns/diff.h>
|
||||
#include <dns/lib.h>
|
||||
#include <dns/qp.h>
|
||||
#include <dns/rdatalist.h>
|
||||
|
|
@ -46,6 +48,22 @@
|
|||
|
||||
#include <tests/dns.h>
|
||||
|
||||
/*
|
||||
* Macro that uses a for loop to execute a cleanup at the end of scope.
|
||||
*/
|
||||
#define WITH_NEWVERSION(db, version_var, should_commit) \
|
||||
for (dns_dbversion_t *version_var = NULL, *_tmp = ({ \
|
||||
isc_result_t _result = dns_db_newversion(db, \
|
||||
&version_var); \
|
||||
assert_int_equal(_result, ISC_R_SUCCESS); \
|
||||
(dns_dbversion_t *)1; \
|
||||
}); \
|
||||
_tmp != NULL; \
|
||||
_tmp = ({ \
|
||||
dns_db_closeversion(db, &version_var, should_commit); \
|
||||
(dns_dbversion_t *)NULL; \
|
||||
}))
|
||||
|
||||
const char *ownercase_vectors[12][2] = {
|
||||
{
|
||||
"AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz",
|
||||
|
|
@ -97,6 +115,68 @@ const char *ownercase_vectors[12][2] = {
|
|||
}
|
||||
};
|
||||
|
||||
static unsigned char example_org_data[] = { 7, 'e', 'x', 'a', 'm', 'p', 'l',
|
||||
'e', 3, 'o', 'r', 'g', 0 };
|
||||
static dns_name_t example_org_name = DNS_NAME_INITABSOLUTE(example_org_data);
|
||||
|
||||
/* IPv6 test addresses */
|
||||
static unsigned char aaaa_test_data[][16] = {
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, /* ::1 */
|
||||
{ 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
2 } /* 2001:db8::2
|
||||
*/
|
||||
};
|
||||
|
||||
/* RRSIG test signatures */
|
||||
static unsigned char rrsig_signature1[64] = {
|
||||
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
|
||||
};
|
||||
|
||||
static unsigned char rrsig_signature2[64] = {
|
||||
0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b,
|
||||
0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56,
|
||||
0x57, 0x58, 0x59, 0x5a, 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
|
||||
};
|
||||
|
||||
/* RRSIG test structures */
|
||||
static dns_rdata_rrsig_t rrsig_test_data1 = {
|
||||
.common = { .rdtype = dns_rdatatype_rrsig,
|
||||
.rdclass = dns_rdataclass_in },
|
||||
.covered = dns_rdatatype_a,
|
||||
.algorithm = DST_ALG_RSASHA256,
|
||||
.labels = 2,
|
||||
.originalttl = 300,
|
||||
.timeexpire = 1695820800,
|
||||
.timesigned = 1695744000,
|
||||
.keyid = 0x1234,
|
||||
.signer = DNS_NAME_INITABSOLUTE(example_org_data),
|
||||
.siglen = 64,
|
||||
.signature = rrsig_signature1,
|
||||
};
|
||||
|
||||
static dns_rdata_rrsig_t rrsig_test_data2 = {
|
||||
.common = { .rdtype = dns_rdatatype_rrsig,
|
||||
.rdclass = dns_rdataclass_in },
|
||||
.covered = dns_rdatatype_a,
|
||||
.algorithm = DST_ALG_RSASHA256,
|
||||
.labels = 2,
|
||||
.originalttl = 300,
|
||||
.timeexpire = 1695820800,
|
||||
.timesigned = 1695744000,
|
||||
.keyid = 0x5678,
|
||||
.signer = DNS_NAME_INITABSOLUTE(example_org_data),
|
||||
.siglen = 64,
|
||||
.signature = rrsig_signature2,
|
||||
};
|
||||
|
||||
static bool
|
||||
ownercase_test_one(const char *str1, const char *str2) {
|
||||
isc_result_t result;
|
||||
|
|
@ -160,6 +240,118 @@ ISC_RUN_TEST_IMPL(ownercase) {
|
|||
assert_false(ownercase_test_one("\\216", "\\246"));
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
find_ip_index(const unsigned char *target_ip, unsigned char (*ips)[16],
|
||||
ssize_t count) {
|
||||
for (ssize_t i = 0; i < count; i++) {
|
||||
if (memcmp(target_ip, ips[i], 16) == 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static isc_result_t
|
||||
apply_dns_update(dns_db_t *db, dns_dbversion_t *version, const dns_name_t *name,
|
||||
dns_rdatatype_t rdtype, dns_rdataclass_t rdclass, uint32_t ttl,
|
||||
const unsigned char *data, size_t data_len, dns_diffop_t op) {
|
||||
isc_result_t result;
|
||||
dns_rdatacallbacks_t callbacks;
|
||||
dns_rdatalist_t rdatalist;
|
||||
dns_rdataset_t rdataset;
|
||||
dns_rdata_t rdata = DNS_RDATA_INIT;
|
||||
|
||||
dns_rdatacallbacks_init(&callbacks);
|
||||
|
||||
result = dns_db_beginupdate(db, version, &callbacks);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
|
||||
/* Set rdata fields directly without reinitializing */
|
||||
rdata.data = (unsigned char *)data;
|
||||
rdata.length = data_len;
|
||||
rdata.rdclass = rdclass;
|
||||
rdata.type = rdtype;
|
||||
|
||||
dns_rdatalist_init(&rdatalist);
|
||||
rdatalist.ttl = ttl;
|
||||
rdatalist.type = rdtype;
|
||||
rdatalist.rdclass = rdclass;
|
||||
ISC_LIST_APPEND(rdatalist.rdata, &rdata, link);
|
||||
|
||||
dns_rdataset_init(&rdataset);
|
||||
dns_rdatalist_tordataset(&rdatalist, &rdataset);
|
||||
|
||||
isc_result_t callback_result = callbacks.update(callbacks.add_private,
|
||||
name, &rdataset, op);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
|
||||
dns_rdataset_disassociate(&rdataset);
|
||||
|
||||
result = dns_db_commitupdate(db, &callbacks);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
|
||||
return callback_result;
|
||||
}
|
||||
|
||||
static void
|
||||
verify_aaaa_records(dns_db_t *db, dns_dbversion_t *version,
|
||||
const dns_name_t *name, unsigned char (*ips)[16],
|
||||
ssize_t expected_count, uint32_t expected_ttl) {
|
||||
isc_result_t result;
|
||||
dns_dbnode_t *node = NULL;
|
||||
dns_rdataset_t rdataset;
|
||||
bool *found_ips = NULL;
|
||||
dns_fixedname_t found_fname;
|
||||
dns_name_t *found_name = dns_fixedname_initname(&found_fname);
|
||||
|
||||
/* Allocate zero-initialized found flags array */
|
||||
found_ips = isc_mem_cget(isc_g_mctx, (size_t)expected_count,
|
||||
sizeof(bool));
|
||||
|
||||
dns_rdataset_init(&rdataset);
|
||||
|
||||
result = dns_db_find(db, name, version, dns_rdatatype_aaaa, 0, 0, &node,
|
||||
found_name, &rdataset, NULL);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
|
||||
/* Check rdataset metadata */
|
||||
assert_int_equal(rdataset.type, dns_rdatatype_aaaa);
|
||||
assert_int_equal(rdataset.rdclass, dns_rdataclass_in);
|
||||
assert_int_equal(rdataset.ttl, expected_ttl);
|
||||
|
||||
/* Iterate through all AAAA records */
|
||||
DNS_RDATASET_FOREACH(&rdataset) {
|
||||
dns_rdata_t rdata = DNS_RDATA_INIT;
|
||||
dns_rdataset_current(&rdataset, &rdata);
|
||||
|
||||
/* Verify this is a valid IPv6 address */
|
||||
assert_int_equal(rdata.length, 16);
|
||||
|
||||
/*
|
||||
* Find whether the IP is in our expected list, and detect
|
||||
* duplicates. Index will be -1 if the IP is not found.
|
||||
*/
|
||||
ssize_t index = find_ip_index(rdata.data, ips, expected_count);
|
||||
assert_true(index >= 0);
|
||||
assert_false(found_ips[index]);
|
||||
found_ips[index] = true;
|
||||
}
|
||||
|
||||
/* Count found IPs by summing overt the boolean array */
|
||||
ssize_t found_count = 0;
|
||||
for (ssize_t i = 0; i < expected_count; i++) {
|
||||
found_count += found_ips[i];
|
||||
}
|
||||
|
||||
/* Verify we found exactly the expected number of records */
|
||||
assert_int_equal(found_count, expected_count);
|
||||
|
||||
dns_db_detachnode(&node);
|
||||
dns_rdataset_disassociate(&rdataset);
|
||||
isc_mem_cput(isc_g_mctx, found_ips, (size_t)expected_count,
|
||||
sizeof(bool));
|
||||
}
|
||||
|
||||
ISC_RUN_TEST_IMPL(setownercase) {
|
||||
isc_result_t result;
|
||||
uint8_t qpdb_s[sizeof(qpzonedb_t) + sizeof(qpzone_bucket_t)];
|
||||
|
|
@ -209,9 +401,124 @@ ISC_RUN_TEST_IMPL(setownercase) {
|
|||
assert_true(dns_name_caseequal(name1, name2));
|
||||
}
|
||||
|
||||
ISC_RUN_TEST_IMPL(diffop_add_sub) {
|
||||
isc_result_t result;
|
||||
dns_db_t *db = NULL;
|
||||
|
||||
result = dns__qpzone_create(isc_g_mctx, &example_org_name,
|
||||
dns_dbtype_zone, dns_rdataclass_in, 0, NULL,
|
||||
NULL, &db);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
assert_non_null(db);
|
||||
|
||||
WITH_NEWVERSION(db, version, true) {
|
||||
apply_dns_update(db, version, &example_org_name,
|
||||
dns_rdatatype_aaaa, dns_rdataclass_in, 300,
|
||||
aaaa_test_data[0], 16, DNS_DIFFOP_ADD);
|
||||
}
|
||||
|
||||
WITH_NEWVERSION(db, version, true) {
|
||||
result = apply_dns_update(db, version, &example_org_name,
|
||||
dns_rdatatype_aaaa, dns_rdataclass_in,
|
||||
300, aaaa_test_data[1], 16,
|
||||
DNS_DIFFOP_ADD);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
|
||||
verify_aaaa_records(db, version, &example_org_name,
|
||||
aaaa_test_data, 2, 300);
|
||||
}
|
||||
|
||||
WITH_NEWVERSION(db, version, true) {
|
||||
result = apply_dns_update(db, version, &example_org_name,
|
||||
dns_rdatatype_aaaa, dns_rdataclass_in,
|
||||
300, aaaa_test_data[0], 16,
|
||||
DNS_DIFFOP_DEL);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
|
||||
verify_aaaa_records(db, version, &example_org_name,
|
||||
&aaaa_test_data[1], 1, 300);
|
||||
}
|
||||
|
||||
WITH_NEWVERSION(db, version, false) {
|
||||
result = apply_dns_update(db, version, &example_org_name,
|
||||
dns_rdatatype_aaaa, dns_rdataclass_in,
|
||||
600, aaaa_test_data[0], 16,
|
||||
DNS_DIFFOP_ADD);
|
||||
assert_int_equal(result, DNS_R_NOTEXACT);
|
||||
}
|
||||
|
||||
dns_db_detach(&db);
|
||||
assert_null(db);
|
||||
}
|
||||
|
||||
ISC_RUN_TEST_IMPL(diffop_addresign) {
|
||||
isc_result_t result;
|
||||
dns_db_t *db = NULL;
|
||||
|
||||
/* Create RRSIG structures and convert to wire format */
|
||||
dns_rdata_t rdata1 = DNS_RDATA_INIT, rdata2 = DNS_RDATA_INIT;
|
||||
isc_buffer_t buffer1, buffer2;
|
||||
unsigned char rrsig_data1[512], rrsig_data2[512];
|
||||
|
||||
isc_buffer_init(&buffer1, rrsig_data1, sizeof(rrsig_data1));
|
||||
result = dns_rdata_fromstruct(&rdata1, dns_rdataclass_in,
|
||||
dns_rdatatype_rrsig, &rrsig_test_data1,
|
||||
&buffer1);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
|
||||
isc_buffer_init(&buffer2, rrsig_data2, sizeof(rrsig_data2));
|
||||
result = dns_rdata_fromstruct(&rdata2, dns_rdataclass_in,
|
||||
dns_rdatatype_rrsig, &rrsig_test_data2,
|
||||
&buffer2);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
|
||||
result = dns__qpzone_create(isc_g_mctx, &example_org_name,
|
||||
dns_dbtype_zone, dns_rdataclass_in, 0, NULL,
|
||||
NULL, &db);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
assert_non_null(db);
|
||||
|
||||
WITH_NEWVERSION(db, version, true) {
|
||||
result = apply_dns_update(db, version, &example_org_name,
|
||||
dns_rdatatype_rrsig,
|
||||
dns_rdataclass_in, 300, rdata1.data,
|
||||
rdata1.length, DNS_DIFFOP_ADDRESIGN);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
}
|
||||
|
||||
WITH_NEWVERSION(db, version, true) {
|
||||
result = apply_dns_update(db, version, &example_org_name,
|
||||
dns_rdatatype_rrsig,
|
||||
dns_rdataclass_in, 300, rdata2.data,
|
||||
rdata2.length, DNS_DIFFOP_ADDRESIGN);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
}
|
||||
|
||||
WITH_NEWVERSION(db, version, true) {
|
||||
result = apply_dns_update(db, version, &example_org_name,
|
||||
dns_rdatatype_rrsig,
|
||||
dns_rdataclass_in, 300, rdata1.data,
|
||||
rdata1.length, DNS_DIFFOP_DELRESIGN);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
}
|
||||
|
||||
WITH_NEWVERSION(db, version, true) {
|
||||
result = apply_dns_update(db, version, &example_org_name,
|
||||
dns_rdatatype_rrsig,
|
||||
dns_rdataclass_in, 300, rdata2.data,
|
||||
rdata2.length, DNS_DIFFOP_DELRESIGN);
|
||||
assert_int_equal(result, DNS_R_NXRRSET);
|
||||
}
|
||||
|
||||
dns_db_detach(&db);
|
||||
assert_null(db);
|
||||
}
|
||||
|
||||
ISC_TEST_LIST_START
|
||||
ISC_TEST_ENTRY(ownercase)
|
||||
ISC_TEST_ENTRY(setownercase)
|
||||
ISC_TEST_ENTRY(diffop_add_sub)
|
||||
ISC_TEST_ENTRY(diffop_addresign)
|
||||
ISC_TEST_LIST_END
|
||||
|
||||
ISC_TEST_MAIN
|
||||
|
|
|
|||
Loading…
Reference in a new issue