2000-12-09 02:17:12 +00:00
|
|
|
/*
|
2011-03-12 04:59:49 +00:00
|
|
|
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
2000-12-09 02:17:12 +00:00
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: MPL-2.0
|
2021-06-03 08:37:05 +02:00
|
|
|
*
|
2000-12-09 02:17:12 +00:00
|
|
|
* 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/.
|
2018-02-23 09:53:12 +01:00
|
|
|
*
|
2000-12-09 02:17:12 +00:00
|
|
|
* See the COPYRIGHT file distributed with this work for additional
|
|
|
|
* information regarding copyright ownership.
|
|
|
|
*/
|
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*! \file */
|
2000-12-09 02:17:12 +00:00
|
|
|
|
2018-03-28 14:19:37 +02:00
|
|
|
#include <inttypes.h>
|
2018-04-17 08:29:14 -07:00
|
|
|
#include <stdbool.h>
|
2024-11-03 21:25:15 +01:00
|
|
|
#include <stddef.h>
|
2000-12-09 02:17:12 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
|
2000-12-09 03:09:20 +00:00
|
|
|
#include <isc/buffer.h>
|
2000-12-09 02:17:12 +00:00
|
|
|
#include <isc/file.h>
|
2024-08-14 13:25:50 +02:00
|
|
|
#include <isc/log.h>
|
2000-12-09 02:17:12 +00:00
|
|
|
#include <isc/mem.h>
|
2021-10-04 17:14:53 +02:00
|
|
|
#include <isc/result.h>
|
2000-12-09 02:17:12 +00:00
|
|
|
#include <isc/string.h>
|
|
|
|
#include <isc/util.h>
|
|
|
|
|
2024-02-05 16:11:16 -08:00
|
|
|
#include <dns/callbacks.h>
|
2000-12-09 02:17:12 +00:00
|
|
|
#include <dns/db.h>
|
|
|
|
#include <dns/diff.h>
|
2005-01-11 23:56:50 +00:00
|
|
|
#include <dns/rdataclass.h>
|
2000-12-09 02:17:12 +00:00
|
|
|
#include <dns/rdatalist.h>
|
|
|
|
#include <dns/rdataset.h>
|
2008-04-01 01:37:25 +00:00
|
|
|
#include <dns/rdatastruct.h>
|
2005-01-11 23:56:50 +00:00
|
|
|
#include <dns/rdatatype.h>
|
2017-11-29 15:20:23 +11:00
|
|
|
#include <dns/time.h>
|
2000-12-09 02:17:12 +00:00
|
|
|
|
|
|
|
#define CHECK(op) \
|
2000-12-11 19:24:30 +00:00
|
|
|
do { \
|
|
|
|
result = (op); \
|
2000-12-09 02:17:12 +00:00
|
|
|
if (result != ISC_R_SUCCESS) \
|
|
|
|
goto failure; \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
static dns_rdatatype_t
|
|
|
|
rdata_covers(dns_rdata_t *rdata) {
|
2003-09-30 06:00:40 +00:00
|
|
|
return rdata->type == dns_rdatatype_rrsig ? dns_rdata_covers(rdata) : 0;
|
2000-12-09 02:17:12 +00:00
|
|
|
}
|
|
|
|
|
2024-07-11 17:00:38 -07:00
|
|
|
void
|
2016-12-30 15:45:08 +11:00
|
|
|
dns_difftuple_create(isc_mem_t *mctx, dns_diffop_t op, const dns_name_t *name,
|
2000-12-09 02:17:12 +00:00
|
|
|
dns_ttl_t ttl, dns_rdata_t *rdata, dns_difftuple_t **tp) {
|
|
|
|
dns_difftuple_t *t;
|
|
|
|
unsigned int size;
|
|
|
|
unsigned char *datap;
|
|
|
|
|
|
|
|
REQUIRE(tp != NULL && *tp == NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create a new tuple. The variable-size wire-format name data and
|
|
|
|
* rdata immediately follow the dns_difftuple_t structure
|
|
|
|
* in memory.
|
|
|
|
*/
|
|
|
|
size = sizeof(*t) + name->length + rdata->length;
|
|
|
|
t = isc_mem_allocate(mctx, size);
|
2013-02-20 21:39:05 -08:00
|
|
|
t->mctx = NULL;
|
|
|
|
isc_mem_attach(mctx, &t->mctx);
|
2000-12-09 02:17:12 +00:00
|
|
|
t->op = op;
|
|
|
|
|
|
|
|
datap = (unsigned char *)(t + 1);
|
|
|
|
|
2014-01-08 16:27:10 -08:00
|
|
|
memmove(datap, name->ndata, name->length);
|
2025-02-21 12:09:39 +01:00
|
|
|
dns_name_init(&t->name);
|
2000-12-09 02:17:12 +00:00
|
|
|
dns_name_clone(name, &t->name);
|
|
|
|
t->name.ndata = datap;
|
|
|
|
datap += name->length;
|
|
|
|
|
|
|
|
t->ttl = ttl;
|
|
|
|
|
|
|
|
dns_rdata_init(&t->rdata);
|
|
|
|
dns_rdata_clone(rdata, &t->rdata);
|
2018-01-22 09:36:12 +11:00
|
|
|
if (rdata->data != NULL) {
|
|
|
|
memmove(datap, rdata->data, rdata->length);
|
|
|
|
t->rdata.data = datap;
|
|
|
|
datap += rdata->length;
|
|
|
|
} else {
|
|
|
|
t->rdata.data = NULL;
|
|
|
|
INSIST(rdata->length == 0);
|
|
|
|
}
|
2000-12-09 02:17:12 +00:00
|
|
|
|
|
|
|
ISC_LINK_INIT(&t->rdata, link);
|
|
|
|
ISC_LINK_INIT(t, link);
|
|
|
|
t->magic = DNS_DIFFTUPLE_MAGIC;
|
|
|
|
|
|
|
|
INSIST(datap == (unsigned char *)t + size);
|
|
|
|
|
|
|
|
*tp = t;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
dns_difftuple_free(dns_difftuple_t **tp) {
|
|
|
|
dns_difftuple_t *t = *tp;
|
2020-02-08 04:37:54 -08:00
|
|
|
*tp = NULL;
|
2013-02-20 21:39:05 -08:00
|
|
|
isc_mem_t *mctx;
|
|
|
|
|
2000-12-09 02:17:12 +00:00
|
|
|
REQUIRE(DNS_DIFFTUPLE_VALID(t));
|
2013-02-20 21:39:05 -08:00
|
|
|
|
2000-12-09 02:17:12 +00:00
|
|
|
dns_name_invalidate(&t->name);
|
|
|
|
t->magic = 0;
|
2013-02-20 21:39:05 -08:00
|
|
|
mctx = t->mctx;
|
|
|
|
isc_mem_free(mctx, t);
|
|
|
|
isc_mem_detach(&mctx);
|
2000-12-09 02:17:12 +00:00
|
|
|
}
|
|
|
|
|
2024-07-11 17:00:38 -07:00
|
|
|
void
|
2000-12-09 02:17:12 +00:00
|
|
|
dns_difftuple_copy(dns_difftuple_t *orig, dns_difftuple_t **copyp) {
|
2024-07-11 17:00:38 -07:00
|
|
|
dns_difftuple_create(orig->mctx, orig->op, &orig->name, orig->ttl,
|
|
|
|
&orig->rdata, copyp);
|
2000-12-09 02:17:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
dns_diff_init(isc_mem_t *mctx, dns_diff_t *diff) {
|
|
|
|
diff->mctx = mctx;
|
|
|
|
ISC_LIST_INIT(diff->tuples);
|
|
|
|
diff->magic = DNS_DIFF_MAGIC;
|
2024-11-03 21:25:15 +01:00
|
|
|
diff->size = 0;
|
2000-12-09 02:17:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
dns_diff_clear(dns_diff_t *diff) {
|
|
|
|
REQUIRE(DNS_DIFF_VALID(diff));
|
2025-05-23 13:02:22 -07:00
|
|
|
ISC_LIST_FOREACH(diff->tuples, t, link) {
|
2000-12-09 02:17:12 +00:00
|
|
|
ISC_LIST_UNLINK(diff->tuples, t, link);
|
|
|
|
dns_difftuple_free(&t);
|
|
|
|
}
|
2024-11-03 21:25:15 +01:00
|
|
|
diff->size = 0;
|
2000-12-09 02:17:12 +00:00
|
|
|
ENSURE(ISC_LIST_EMPTY(diff->tuples));
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
dns_diff_append(dns_diff_t *diff, dns_difftuple_t **tuplep) {
|
2024-11-03 21:25:15 +01:00
|
|
|
REQUIRE(DNS_DIFF_VALID(diff));
|
2000-12-09 02:17:12 +00:00
|
|
|
ISC_LIST_APPEND(diff->tuples, *tuplep, link);
|
2024-11-03 21:25:15 +01:00
|
|
|
diff->size += 1;
|
2000-12-09 02:17:12 +00:00
|
|
|
*tuplep = NULL;
|
|
|
|
}
|
|
|
|
|
2024-11-03 21:25:15 +01:00
|
|
|
bool
|
|
|
|
dns_diff_is_boundary(const dns_diff_t *diff, dns_name_t *new_name) {
|
|
|
|
REQUIRE(DNS_DIFF_VALID(diff));
|
|
|
|
REQUIRE(DNS_NAME_VALID(new_name));
|
|
|
|
|
|
|
|
if (ISC_LIST_EMPTY(diff->tuples)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
dns_difftuple_t *tail = ISC_LIST_TAIL(diff->tuples);
|
|
|
|
return !dns_name_caseequal(&tail->name, new_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t
|
|
|
|
dns_diff_size(const dns_diff_t *diff) {
|
|
|
|
REQUIRE(DNS_DIFF_VALID(diff));
|
|
|
|
return diff->size;
|
|
|
|
}
|
|
|
|
|
2000-12-09 02:17:12 +00:00
|
|
|
/* XXX this is O(N) */
|
|
|
|
|
|
|
|
void
|
|
|
|
dns_diff_appendminimal(dns_diff_t *diff, dns_difftuple_t **tuplep) {
|
|
|
|
REQUIRE(DNS_DIFF_VALID(diff));
|
|
|
|
REQUIRE(DNS_DIFFTUPLE_VALID(*tuplep));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Look for an existing tuple with the same owner name,
|
|
|
|
* rdata, and TTL. If we are doing an addition and find a
|
|
|
|
* deletion or vice versa, remove both the old and the
|
|
|
|
* new tuple since they cancel each other out (assuming
|
|
|
|
* that we never delete nonexistent data or add existing
|
|
|
|
* data).
|
|
|
|
*
|
|
|
|
* If we find an old update of the same kind as
|
|
|
|
* the one we are doing, there must be a programming
|
|
|
|
* error. We report it but try to continue anyway.
|
|
|
|
*/
|
2025-05-23 13:02:22 -07:00
|
|
|
ISC_LIST_FOREACH(diff->tuples, ot, link) {
|
2015-02-27 15:08:38 +11:00
|
|
|
if (dns_name_caseequal(&ot->name, &(*tuplep)->name) &&
|
2000-12-09 02:17:12 +00:00
|
|
|
dns_rdata_compare(&ot->rdata, &(*tuplep)->rdata) == 0 &&
|
|
|
|
ot->ttl == (*tuplep)->ttl)
|
|
|
|
{
|
|
|
|
ISC_LIST_UNLINK(diff->tuples, ot, link);
|
2024-11-03 21:25:15 +01:00
|
|
|
INSIST(diff->size > 0);
|
|
|
|
diff->size -= 1;
|
|
|
|
|
2000-12-09 02:17:12 +00:00
|
|
|
if ((*tuplep)->op == ot->op) {
|
2022-10-14 16:07:07 +01:00
|
|
|
UNEXPECTED_ERROR("unexpected non-minimal diff");
|
2000-12-09 02:17:12 +00:00
|
|
|
} else {
|
|
|
|
dns_difftuple_free(tuplep);
|
|
|
|
}
|
|
|
|
dns_difftuple_free(&ot);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*tuplep != NULL) {
|
|
|
|
ISC_LIST_APPEND(diff->tuples, *tuplep, link);
|
2024-11-03 21:25:15 +01:00
|
|
|
diff->size += 1;
|
2000-12-09 02:17:12 +00:00
|
|
|
*tuplep = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-04-01 01:37:25 +00:00
|
|
|
static isc_stdtime_t
|
2013-09-04 16:35:11 -07:00
|
|
|
setresign(dns_rdataset_t *modified) {
|
2008-04-01 01:37:25 +00:00
|
|
|
dns_rdata_t rdata = DNS_RDATA_INIT;
|
|
|
|
dns_rdata_rrsig_t sig;
|
2018-03-28 14:19:37 +02:00
|
|
|
int64_t when;
|
2008-04-01 01:37:25 +00:00
|
|
|
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);
|
2008-04-02 02:37:42 +00:00
|
|
|
if ((rdata.flags & DNS_RDATA_OFFLINE) != 0) {
|
2008-04-01 01:37:25 +00:00
|
|
|
when = 0;
|
2008-04-02 02:37:42 +00:00
|
|
|
} else {
|
2017-11-29 15:20:23 +11:00
|
|
|
when = dns_time64_from32(sig.timeexpire);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2008-04-01 01:37:25 +00:00
|
|
|
dns_rdata_reset(&rdata);
|
2008-04-01 23:47:10 +00:00
|
|
|
|
2008-04-01 01:37:25 +00:00
|
|
|
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;
|
|
|
|
}
|
2017-11-29 15:20:23 +11:00
|
|
|
if (when == 0 || dns_time64_from32(sig.timeexpire) < when) {
|
|
|
|
when = dns_time64_from32(sig.timeexpire);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2008-04-01 01:37:25 +00:00
|
|
|
next_rr:
|
|
|
|
dns_rdata_reset(&rdata);
|
|
|
|
result = dns_rdataset_next(modified);
|
|
|
|
}
|
|
|
|
INSIST(result == ISC_R_NOMORE);
|
2017-11-29 15:20:23 +11:00
|
|
|
return (isc_stdtime_t)when;
|
2008-04-01 01:37:25 +00:00
|
|
|
}
|
|
|
|
|
2015-02-27 15:08:38 +11:00
|
|
|
static void
|
|
|
|
getownercase(dns_rdataset_t *rdataset, dns_name_t *name) {
|
|
|
|
if (dns_rdataset_isassociated(rdataset)) {
|
|
|
|
dns_rdataset_getownercase(rdataset, name);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2015-02-27 15:08:38 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-12-30 15:45:08 +11:00
|
|
|
setownercase(dns_rdataset_t *rdataset, const dns_name_t *name) {
|
2015-02-27 15:08:38 +11:00
|
|
|
if (dns_rdataset_isassociated(rdataset)) {
|
|
|
|
dns_rdataset_setownercase(rdataset, name);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2015-02-27 15:08:38 +11:00
|
|
|
}
|
|
|
|
|
2023-12-12 13:50:33 +11:00
|
|
|
static const char *
|
|
|
|
optotext(dns_diffop_t op) {
|
|
|
|
switch (op) {
|
|
|
|
case DNS_DIFFOP_ADD:
|
|
|
|
return "add";
|
|
|
|
case DNS_DIFFOP_ADDRESIGN:
|
|
|
|
return "add-resign";
|
|
|
|
case DNS_DIFFOP_DEL:
|
|
|
|
return "del";
|
|
|
|
case DNS_DIFFOP_DELRESIGN:
|
|
|
|
return "del-resign";
|
|
|
|
default:
|
|
|
|
return "unknown";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2001-11-29 00:15:35 +00:00
|
|
|
static isc_result_t
|
2024-11-03 21:25:15 +01:00
|
|
|
diff_apply(const dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *ver,
|
|
|
|
bool warn) {
|
2000-12-09 02:17:12 +00:00
|
|
|
dns_difftuple_t *t;
|
|
|
|
dns_dbnode_t *node = NULL;
|
|
|
|
isc_result_t result;
|
2005-01-11 23:56:50 +00:00
|
|
|
char namebuf[DNS_NAME_FORMATSIZE];
|
|
|
|
char typebuf[DNS_RDATATYPE_FORMATSIZE];
|
|
|
|
char classbuf[DNS_RDATACLASS_FORMATSIZE];
|
2000-12-09 02:17:12 +00:00
|
|
|
|
|
|
|
REQUIRE(DNS_DIFF_VALID(diff));
|
|
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
|
|
|
|
|
|
t = ISC_LIST_HEAD(diff->tuples);
|
|
|
|
while (t != NULL) {
|
|
|
|
dns_name_t *name;
|
|
|
|
|
|
|
|
INSIST(node == NULL);
|
|
|
|
name = &t->name;
|
|
|
|
/*
|
|
|
|
* Find the node.
|
|
|
|
* We create the node if it does not exist.
|
|
|
|
* This will cause an empty node to be created if the diff
|
|
|
|
* contains a deletion of an RR at a nonexistent name,
|
|
|
|
* but such diffs should never be created in the first
|
|
|
|
* place.
|
|
|
|
*/
|
|
|
|
|
|
|
|
while (t != NULL && dns_name_equal(&t->name, name)) {
|
|
|
|
dns_rdatatype_t type, covers;
|
2023-12-12 13:50:33 +11:00
|
|
|
dns_rdataclass_t rdclass;
|
2000-12-09 02:17:12 +00:00
|
|
|
dns_diffop_t op;
|
|
|
|
dns_rdatalist_t rdl;
|
|
|
|
dns_rdataset_t rds;
|
2008-04-01 01:37:25 +00:00
|
|
|
dns_rdataset_t ardataset;
|
2015-02-27 15:08:38 +11:00
|
|
|
unsigned int options;
|
2000-12-09 02:17:12 +00:00
|
|
|
|
|
|
|
op = t->op;
|
|
|
|
type = t->rdata.type;
|
2023-12-12 13:50:33 +11:00
|
|
|
rdclass = t->rdata.rdclass;
|
2000-12-09 02:17:12 +00:00
|
|
|
covers = rdata_covers(&t->rdata);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Collect a contiguous set of updates with
|
|
|
|
* the same operation (add/delete) and RR type
|
|
|
|
* into a single rdatalist so that the
|
|
|
|
* database rrset merging/subtraction code
|
|
|
|
* can work more efficiently than if each
|
|
|
|
* RR were merged into / subtracted from
|
|
|
|
* the database separately.
|
|
|
|
*
|
|
|
|
* This is done by linking rdata structures from the
|
|
|
|
* diff into "rdatalist". This uses the rdata link
|
|
|
|
* field, not the diff link field, so the structure
|
|
|
|
* of the diff itself is not affected.
|
|
|
|
*/
|
|
|
|
|
2015-03-03 16:43:42 +11:00
|
|
|
dns_rdatalist_init(&rdl);
|
2000-12-09 02:17:12 +00:00
|
|
|
rdl.type = type;
|
|
|
|
rdl.covers = covers;
|
|
|
|
rdl.rdclass = t->rdata.rdclass;
|
|
|
|
rdl.ttl = t->ttl;
|
|
|
|
|
2008-09-24 02:46:23 +00:00
|
|
|
node = NULL;
|
|
|
|
if (type != dns_rdatatype_nsec3 &&
|
2022-11-02 19:33:14 +01:00
|
|
|
covers != dns_rdatatype_nsec3)
|
|
|
|
{
|
2018-04-17 08:29:14 -07:00
|
|
|
CHECK(dns_db_findnode(db, name, true, &node));
|
2008-09-24 02:46:23 +00:00
|
|
|
} else {
|
2018-04-17 08:29:14 -07:00
|
|
|
CHECK(dns_db_findnsec3node(db, name, true,
|
2008-09-24 02:46:23 +00:00
|
|
|
&node));
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2008-09-24 02:46:23 +00:00
|
|
|
|
2000-12-09 02:17:12 +00:00
|
|
|
while (t != NULL && dns_name_equal(&t->name, name) &&
|
|
|
|
t->op == op && t->rdata.type == type &&
|
|
|
|
rdata_covers(&t->rdata) == covers)
|
|
|
|
{
|
2015-02-27 15:08:38 +11:00
|
|
|
/*
|
|
|
|
* Remember the add name for
|
|
|
|
* dns_rdataset_setownercase.
|
|
|
|
*/
|
|
|
|
name = &t->name;
|
|
|
|
if (t->ttl != rdl.ttl && warn) {
|
|
|
|
dns_name_format(name, namebuf,
|
|
|
|
sizeof(namebuf));
|
|
|
|
dns_rdatatype_format(t->rdata.type,
|
|
|
|
typebuf,
|
|
|
|
sizeof(typebuf));
|
|
|
|
dns_rdataclass_format(t->rdata.rdclass,
|
|
|
|
classbuf,
|
|
|
|
sizeof(classbuf));
|
2024-08-13 18:20:26 +02:00
|
|
|
isc_log_write(DNS_LOGCATEGORY_GENERAL,
|
|
|
|
DNS_LOGMODULE_DIFF,
|
2008-04-01 23:47:10 +00:00
|
|
|
ISC_LOG_WARNING,
|
2005-01-11 23:56:50 +00:00
|
|
|
"'%s/%s/%s': TTL differs "
|
|
|
|
"in "
|
|
|
|
"rdataset, adjusting "
|
|
|
|
"%lu -> %lu",
|
|
|
|
namebuf, typebuf,
|
|
|
|
classbuf,
|
2000-12-11 19:24:30 +00:00
|
|
|
(unsigned long)t->ttl,
|
|
|
|
(unsigned long)rdl.ttl);
|
2015-02-27 15:08:38 +11:00
|
|
|
}
|
2000-12-09 02:17:12 +00:00
|
|
|
ISC_LIST_APPEND(rdl.rdata, &t->rdata, link);
|
|
|
|
t = ISC_LIST_NEXT(t, link);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Convert the rdatalist into a rdataset.
|
|
|
|
*/
|
|
|
|
dns_rdataset_init(&rds);
|
2015-02-27 15:08:38 +11:00
|
|
|
dns_rdataset_init(&ardataset);
|
2022-07-29 12:40:45 +00:00
|
|
|
dns_rdatalist_tordataset(&rdl, &rds);
|
2004-03-04 02:46:28 +00:00
|
|
|
rds.trust = dns_trust_ultimate;
|
2000-12-09 02:17:12 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Merge the rdataset into the database.
|
|
|
|
*/
|
2008-04-01 01:37:25 +00:00
|
|
|
switch (op) {
|
|
|
|
case DNS_DIFFOP_ADD:
|
|
|
|
case DNS_DIFFOP_ADDRESIGN:
|
2015-02-27 15:08:38 +11:00
|
|
|
options = DNS_DBADD_MERGE | DNS_DBADD_EXACT |
|
|
|
|
DNS_DBADD_EXACTTTL;
|
2000-12-09 02:17:12 +00:00
|
|
|
result = dns_db_addrdataset(db, node, ver, 0,
|
2015-02-27 15:08:38 +11:00
|
|
|
&rds, options,
|
|
|
|
&ardataset);
|
2008-04-01 01:37:25 +00:00
|
|
|
break;
|
|
|
|
case DNS_DIFFOP_DEL:
|
|
|
|
case DNS_DIFFOP_DELRESIGN:
|
2015-02-27 15:08:38 +11:00
|
|
|
options = DNS_DBSUB_EXACT | DNS_DBSUB_WANTOLD;
|
2000-12-09 02:17:12 +00:00
|
|
|
result = dns_db_subtractrdataset(db, node, ver,
|
2015-02-27 15:08:38 +11:00
|
|
|
&rds, options,
|
|
|
|
&ardataset);
|
2008-04-01 01:37:25 +00:00
|
|
|
break;
|
|
|
|
default:
|
2021-10-11 12:50:17 +02:00
|
|
|
UNREACHABLE();
|
2000-12-09 02:17:12 +00:00
|
|
|
}
|
2008-04-01 01:37:25 +00:00
|
|
|
|
|
|
|
if (result == ISC_R_SUCCESS) {
|
2015-02-27 15:08:38 +11:00
|
|
|
if (rds.type == dns_rdatatype_rrsig &&
|
|
|
|
(op == DNS_DIFFOP_DELRESIGN ||
|
|
|
|
op == DNS_DIFFOP_ADDRESIGN))
|
|
|
|
{
|
2008-04-01 01:37:25 +00:00
|
|
|
isc_stdtime_t resign;
|
2015-02-27 15:08:38 +11:00
|
|
|
resign = setresign(&ardataset);
|
|
|
|
dns_db_setsigningtime(db, &ardataset,
|
2008-04-01 01:37:25 +00:00
|
|
|
resign);
|
|
|
|
}
|
2015-02-27 15:08:38 +11:00
|
|
|
if (op == DNS_DIFFOP_ADD ||
|
2022-11-02 19:33:14 +01:00
|
|
|
op == DNS_DIFFOP_ADDRESIGN)
|
|
|
|
{
|
2015-02-27 15:08:38 +11:00
|
|
|
setownercase(&ardataset, name);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2015-02-27 15:08:38 +11:00
|
|
|
if (op == DNS_DIFFOP_DEL ||
|
2022-11-02 19:33:14 +01:00
|
|
|
op == DNS_DIFFOP_DELRESIGN)
|
|
|
|
{
|
2015-02-27 15:08:38 +11:00
|
|
|
getownercase(&ardataset, name);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2008-04-01 01:37:25 +00:00
|
|
|
} else if (result == DNS_R_UNCHANGED) {
|
2008-04-01 23:47:10 +00:00
|
|
|
/*
|
2000-12-09 02:17:12 +00:00
|
|
|
* This will not happen when executing a
|
|
|
|
* dynamic update, because that code will
|
|
|
|
* generate strictly minimal diffs.
|
|
|
|
* It may happen when receiving an IXFR
|
|
|
|
* from a server that is not as careful.
|
|
|
|
* Issue a warning and continue.
|
|
|
|
*/
|
2009-04-30 06:53:10 +00:00
|
|
|
if (warn) {
|
|
|
|
dns_name_format(dns_db_origin(db),
|
|
|
|
namebuf,
|
|
|
|
sizeof(namebuf));
|
|
|
|
dns_rdataclass_format(dns_db_class(db),
|
|
|
|
classbuf,
|
|
|
|
sizeof(classbuf));
|
2024-08-13 18:20:26 +02:00
|
|
|
isc_log_write(DNS_LOGCATEGORY_GENERAL,
|
|
|
|
DNS_LOGMODULE_DIFF,
|
2001-11-29 00:15:35 +00:00
|
|
|
ISC_LOG_WARNING,
|
2009-12-01 00:47:09 +00:00
|
|
|
"%s/%s: dns_diff_apply: "
|
|
|
|
"update with no effect",
|
|
|
|
namebuf, classbuf);
|
2009-04-30 06:53:10 +00:00
|
|
|
}
|
2015-02-27 15:08:38 +11:00
|
|
|
if (op == DNS_DIFFOP_ADD ||
|
2022-11-02 19:33:14 +01:00
|
|
|
op == DNS_DIFFOP_ADDRESIGN)
|
|
|
|
{
|
2015-02-27 15:08:38 +11:00
|
|
|
setownercase(&ardataset, name);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2015-02-27 15:08:38 +11:00
|
|
|
if (op == DNS_DIFFOP_DEL ||
|
2022-11-02 19:33:14 +01:00
|
|
|
op == DNS_DIFFOP_DELRESIGN)
|
|
|
|
{
|
2015-02-27 15:08:38 +11:00
|
|
|
getownercase(&ardataset, name);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2008-04-01 01:37:25 +00:00
|
|
|
} else if (result == DNS_R_NXRRSET) {
|
2000-12-09 02:17:12 +00:00
|
|
|
/*
|
|
|
|
* OK.
|
|
|
|
*/
|
2015-02-27 15:08:38 +11:00
|
|
|
if (op == DNS_DIFFOP_DEL ||
|
2022-11-02 19:33:14 +01:00
|
|
|
op == DNS_DIFFOP_DELRESIGN)
|
|
|
|
{
|
2015-02-27 15:08:38 +11:00
|
|
|
getownercase(&ardataset, name);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2015-02-27 15:08:38 +11:00
|
|
|
if (dns_rdataset_isassociated(&ardataset)) {
|
|
|
|
dns_rdataset_disassociate(&ardataset);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2000-12-09 02:17:12 +00:00
|
|
|
} else {
|
2023-12-12 13:50:33 +11:00
|
|
|
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(
|
2024-08-13 18:20:26 +02:00
|
|
|
DNS_LOGCATEGORY_GENERAL,
|
|
|
|
DNS_LOGMODULE_DIFF,
|
2023-12-12 13:50:33 +11:00
|
|
|
ISC_LOG_ERROR,
|
|
|
|
"dns_diff_apply: %s/%s/%s: %s "
|
|
|
|
"%s",
|
|
|
|
namebuf, typebuf, classbuf,
|
|
|
|
optotext(op),
|
|
|
|
isc_result_totext(result));
|
|
|
|
}
|
2015-02-27 15:08:38 +11:00
|
|
|
if (dns_rdataset_isassociated(&ardataset)) {
|
|
|
|
dns_rdataset_disassociate(&ardataset);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2000-12-09 02:17:12 +00:00
|
|
|
CHECK(result);
|
|
|
|
}
|
Decouple database and node lifetimes by adding node-specific vtables
All databases in the codebase follow the same structure: a database is
an associative container from DNS names to nodes, and each node is an
associative container from RR types to RR data.
Each database implementation (qpzone, qpcache, sdlz, builtin, dyndb) has
its own corresponding node type (qpznode, qpcnode, etc). However, some
code needs to work with nodes generically regardless of their specific
type - for example, to acquire locks, manage references, or
register/unregister slabs from the heap.
Currently, these generic node operations are implemented as methods in
the database vtable, which creates problematic coupling between database
and node lifetimes. If a node outlives its parent database, the node
destructor will destroy all RR data, and each RR data destructor will
try to unregister from heaps by calling a virtual function from the
database vtable. Since the database was already freed, this causes a
crash.
This commit breaks the coupling by standardizing the layout of all
database nodes, adding a dedicated vtable for node operations, and
moving node-specific methods from the database vtable to the node
vtable.
2025-06-05 11:51:29 +02:00
|
|
|
dns_db_detachnode(&node);
|
2015-02-27 15:08:38 +11:00
|
|
|
if (dns_rdataset_isassociated(&ardataset)) {
|
|
|
|
dns_rdataset_disassociate(&ardataset);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2000-12-09 02:17:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return ISC_R_SUCCESS;
|
|
|
|
|
|
|
|
failure:
|
|
|
|
if (node != NULL) {
|
Decouple database and node lifetimes by adding node-specific vtables
All databases in the codebase follow the same structure: a database is
an associative container from DNS names to nodes, and each node is an
associative container from RR types to RR data.
Each database implementation (qpzone, qpcache, sdlz, builtin, dyndb) has
its own corresponding node type (qpznode, qpcnode, etc). However, some
code needs to work with nodes generically regardless of their specific
type - for example, to acquire locks, manage references, or
register/unregister slabs from the heap.
Currently, these generic node operations are implemented as methods in
the database vtable, which creates problematic coupling between database
and node lifetimes. If a node outlives its parent database, the node
destructor will destroy all RR data, and each RR data destructor will
try to unregister from heaps by calling a virtual function from the
database vtable. Since the database was already freed, this causes a
crash.
This commit breaks the coupling by standardizing the layout of all
database nodes, adding a dedicated vtable for node operations, and
moving node-specific methods from the database vtable to the node
vtable.
2025-06-05 11:51:29 +02:00
|
|
|
dns_db_detachnode(&node);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2000-12-09 02:17:12 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2001-11-29 00:15:35 +00:00
|
|
|
isc_result_t
|
2024-11-03 21:25:15 +01:00
|
|
|
dns_diff_apply(const dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *ver) {
|
2018-04-17 08:29:14 -07:00
|
|
|
return diff_apply(diff, db, ver, true);
|
2001-11-29 00:15:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
isc_result_t
|
2024-11-03 21:25:15 +01:00
|
|
|
dns_diff_applysilently(const dns_diff_t *diff, dns_db_t *db,
|
|
|
|
dns_dbversion_t *ver) {
|
2018-04-17 08:29:14 -07:00
|
|
|
return diff_apply(diff, db, ver, false);
|
2001-11-29 00:15:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* XXX this duplicates lots of code in diff_apply(). */
|
2000-12-09 02:17:12 +00:00
|
|
|
|
|
|
|
isc_result_t
|
2024-11-03 21:25:15 +01:00
|
|
|
dns_diff_load(const dns_diff_t *diff, dns_rdatacallbacks_t *callbacks) {
|
2000-12-09 02:17:12 +00:00
|
|
|
dns_difftuple_t *t;
|
|
|
|
isc_result_t result;
|
|
|
|
|
|
|
|
REQUIRE(DNS_DIFF_VALID(diff));
|
|
|
|
|
2024-02-05 16:11:16 -08:00
|
|
|
if (callbacks->setup != NULL) {
|
|
|
|
callbacks->setup(callbacks->add_private);
|
|
|
|
}
|
|
|
|
|
2000-12-09 02:17:12 +00:00
|
|
|
t = ISC_LIST_HEAD(diff->tuples);
|
|
|
|
while (t != NULL) {
|
|
|
|
dns_name_t *name;
|
|
|
|
|
|
|
|
name = &t->name;
|
2018-07-17 04:56:51 +10:00
|
|
|
while (t != NULL && dns_name_caseequal(&t->name, name)) {
|
2000-12-09 02:17:12 +00:00
|
|
|
dns_rdatatype_t type, covers;
|
|
|
|
dns_diffop_t op;
|
|
|
|
dns_rdatalist_t rdl;
|
|
|
|
dns_rdataset_t rds;
|
|
|
|
|
|
|
|
op = t->op;
|
|
|
|
type = t->rdata.type;
|
|
|
|
covers = rdata_covers(&t->rdata);
|
|
|
|
|
2015-03-03 16:43:42 +11:00
|
|
|
dns_rdatalist_init(&rdl);
|
2000-12-09 02:17:12 +00:00
|
|
|
rdl.type = type;
|
|
|
|
rdl.covers = covers;
|
|
|
|
rdl.rdclass = t->rdata.rdclass;
|
|
|
|
rdl.ttl = t->ttl;
|
|
|
|
|
2018-07-17 04:56:51 +10:00
|
|
|
while (t != NULL &&
|
|
|
|
dns_name_caseequal(&t->name, name) &&
|
2000-12-09 02:17:12 +00:00
|
|
|
t->op == op && t->rdata.type == type &&
|
|
|
|
rdata_covers(&t->rdata) == covers)
|
|
|
|
{
|
|
|
|
ISC_LIST_APPEND(rdl.rdata, &t->rdata, link);
|
|
|
|
t = ISC_LIST_NEXT(t, link);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Convert the rdatalist into a rdataset.
|
|
|
|
*/
|
|
|
|
dns_rdataset_init(&rds);
|
2022-07-29 12:40:45 +00:00
|
|
|
dns_rdatalist_tordataset(&rdl, &rds);
|
2000-12-09 02:17:12 +00:00
|
|
|
rds.trust = dns_trust_ultimate;
|
|
|
|
|
|
|
|
INSIST(op == DNS_DIFFOP_ADD);
|
2024-02-05 16:11:16 -08:00
|
|
|
result = callbacks->add(callbacks->add_private, name,
|
|
|
|
&rds DNS__DB_FILELINE);
|
2000-12-09 02:17:12 +00:00
|
|
|
if (result == DNS_R_UNCHANGED) {
|
2024-08-13 18:20:26 +02:00
|
|
|
isc_log_write(DNS_LOGCATEGORY_GENERAL,
|
|
|
|
DNS_LOGMODULE_DIFF,
|
2000-12-09 02:17:12 +00:00
|
|
|
ISC_LOG_WARNING,
|
2009-12-01 00:47:09 +00:00
|
|
|
"dns_diff_load: "
|
2000-12-09 02:17:12 +00:00
|
|
|
"update with no effect");
|
|
|
|
} else if (result == ISC_R_SUCCESS ||
|
2022-11-02 19:33:14 +01:00
|
|
|
result == DNS_R_NXRRSET)
|
|
|
|
{
|
2000-12-09 02:17:12 +00:00
|
|
|
/*
|
|
|
|
* OK.
|
|
|
|
*/
|
|
|
|
} else {
|
|
|
|
CHECK(result);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
result = ISC_R_SUCCESS;
|
2024-02-05 16:11:16 -08:00
|
|
|
|
2000-12-09 02:17:12 +00:00
|
|
|
failure:
|
2024-02-05 16:11:16 -08:00
|
|
|
if (callbacks->commit != NULL) {
|
|
|
|
callbacks->commit(callbacks->add_private);
|
|
|
|
}
|
2000-12-09 02:17:12 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XXX uses qsort(); a merge sort would be more natural for lists,
|
|
|
|
* and perhaps safer wrt thread stack overflow.
|
|
|
|
*/
|
|
|
|
isc_result_t
|
|
|
|
dns_diff_sort(dns_diff_t *diff, dns_diff_compare_func *compare) {
|
|
|
|
unsigned int length = 0;
|
|
|
|
unsigned int i;
|
2025-03-20 22:25:56 -07:00
|
|
|
dns_difftuple_t **v = NULL;
|
2000-12-09 02:17:12 +00:00
|
|
|
REQUIRE(DNS_DIFF_VALID(diff));
|
|
|
|
|
2025-03-20 22:25:56 -07:00
|
|
|
ISC_LIST_FOREACH(diff->tuples, p, link) {
|
2000-12-09 02:17:12 +00:00
|
|
|
length++;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2000-12-09 02:17:12 +00:00
|
|
|
if (length == 0) {
|
|
|
|
return ISC_R_SUCCESS;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2023-08-23 08:56:31 +02:00
|
|
|
v = isc_mem_cget(diff->mctx, length, sizeof(dns_difftuple_t *));
|
2000-12-09 02:17:12 +00:00
|
|
|
for (i = 0; i < length; i++) {
|
2025-03-20 22:25:56 -07:00
|
|
|
dns_difftuple_t *p = ISC_LIST_HEAD(diff->tuples);
|
2000-12-09 02:17:12 +00:00
|
|
|
v[i] = p;
|
|
|
|
ISC_LIST_UNLINK(diff->tuples, p, link);
|
|
|
|
}
|
|
|
|
INSIST(ISC_LIST_HEAD(diff->tuples) == NULL);
|
|
|
|
qsort(v, length, sizeof(v[0]), compare);
|
|
|
|
for (i = 0; i < length; i++) {
|
|
|
|
ISC_LIST_APPEND(diff->tuples, v[i], link);
|
|
|
|
}
|
2023-08-23 08:56:31 +02:00
|
|
|
isc_mem_cput(diff->mctx, v, length, sizeof(dns_difftuple_t *));
|
2000-12-09 02:17:12 +00:00
|
|
|
return ISC_R_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create an rdataset containing the single RR of the given
|
2009-01-05 23:20:22 +00:00
|
|
|
* tuple. The caller must allocate the rdata, rdataset and
|
2000-12-09 02:17:12 +00:00
|
|
|
* an rdatalist structure for it to refer to.
|
|
|
|
*/
|
|
|
|
|
2022-07-29 12:40:45 +00:00
|
|
|
static void
|
2000-12-09 02:17:12 +00:00
|
|
|
diff_tuple_tordataset(dns_difftuple_t *t, dns_rdata_t *rdata,
|
|
|
|
dns_rdatalist_t *rdl, dns_rdataset_t *rds) {
|
|
|
|
REQUIRE(DNS_DIFFTUPLE_VALID(t));
|
|
|
|
REQUIRE(rdl != NULL);
|
|
|
|
REQUIRE(rds != NULL);
|
|
|
|
|
2015-03-03 16:43:42 +11:00
|
|
|
dns_rdatalist_init(rdl);
|
2000-12-09 02:17:12 +00:00
|
|
|
rdl->type = t->rdata.type;
|
|
|
|
rdl->rdclass = t->rdata.rdclass;
|
|
|
|
rdl->ttl = t->ttl;
|
|
|
|
dns_rdataset_init(rds);
|
|
|
|
ISC_LINK_INIT(rdata, link);
|
|
|
|
dns_rdata_clone(&t->rdata, rdata);
|
|
|
|
ISC_LIST_APPEND(rdl->rdata, rdata, link);
|
2022-07-29 12:40:45 +00:00
|
|
|
dns_rdatalist_tordataset(rdl, rds);
|
2000-12-09 02:17:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
isc_result_t
|
2025-03-20 22:25:56 -07:00
|
|
|
dns_diff_print(dns_diff_t *diff, FILE *file) {
|
2000-12-09 02:17:12 +00:00
|
|
|
isc_result_t result;
|
|
|
|
char *mem = NULL;
|
|
|
|
unsigned int size = 2048;
|
2008-04-01 01:37:25 +00:00
|
|
|
const char *op = NULL;
|
2000-12-09 02:17:12 +00:00
|
|
|
|
|
|
|
REQUIRE(DNS_DIFF_VALID(diff));
|
|
|
|
|
|
|
|
mem = isc_mem_get(diff->mctx, size);
|
|
|
|
|
2025-03-20 22:25:56 -07:00
|
|
|
ISC_LIST_FOREACH(diff->tuples, t, link) {
|
2000-12-09 02:17:12 +00:00
|
|
|
isc_buffer_t buf;
|
|
|
|
isc_region_t r;
|
|
|
|
|
|
|
|
dns_rdatalist_t rdl;
|
|
|
|
dns_rdataset_t rds;
|
|
|
|
dns_rdata_t rd = DNS_RDATA_INIT;
|
|
|
|
|
2022-07-29 12:40:45 +00:00
|
|
|
diff_tuple_tordataset(t, &rd, &rdl, &rds);
|
2000-12-09 02:17:12 +00:00
|
|
|
again:
|
|
|
|
isc_buffer_init(&buf, mem, size);
|
|
|
|
result = dns_rdataset_totext(&rds, &t->name, false, false,
|
|
|
|
&buf);
|
|
|
|
|
|
|
|
if (result == ISC_R_NOSPACE) {
|
|
|
|
isc_mem_put(diff->mctx, mem, size);
|
|
|
|
size += 1024;
|
|
|
|
mem = isc_mem_get(diff->mctx, size);
|
|
|
|
goto again;
|
|
|
|
}
|
2002-08-06 14:33:08 +00:00
|
|
|
|
|
|
|
if (result != ISC_R_SUCCESS) {
|
2000-12-09 02:17:12 +00:00
|
|
|
goto cleanup;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2002-08-06 14:33:08 +00:00
|
|
|
/*
|
|
|
|
* Get rid of final newline.
|
|
|
|
*/
|
|
|
|
INSIST(buf.used >= 1 &&
|
|
|
|
((char *)buf.base)[buf.used - 1] == '\n');
|
|
|
|
buf.used--;
|
|
|
|
|
|
|
|
isc_buffer_usedregion(&buf, &r);
|
2008-04-01 01:37:25 +00:00
|
|
|
switch (t->op) {
|
|
|
|
case DNS_DIFFOP_EXISTS:
|
|
|
|
op = "exists";
|
|
|
|
break;
|
|
|
|
case DNS_DIFFOP_ADD:
|
|
|
|
op = "add";
|
|
|
|
break;
|
|
|
|
case DNS_DIFFOP_DEL:
|
|
|
|
op = "del";
|
|
|
|
break;
|
|
|
|
case DNS_DIFFOP_ADDRESIGN:
|
|
|
|
op = "add re-sign";
|
|
|
|
break;
|
|
|
|
case DNS_DIFFOP_DELRESIGN:
|
|
|
|
op = "del re-sign";
|
|
|
|
break;
|
|
|
|
}
|
2002-08-06 14:33:08 +00:00
|
|
|
if (file != NULL) {
|
2008-04-01 01:37:25 +00:00
|
|
|
fprintf(file, "%s %.*s\n", op, (int)r.length,
|
|
|
|
(char *)r.base);
|
2002-08-06 14:33:08 +00:00
|
|
|
} else {
|
2024-08-13 18:20:26 +02:00
|
|
|
isc_log_write(DNS_LOGCATEGORY_GENERAL,
|
|
|
|
DNS_LOGMODULE_DIFF, ISC_LOG_DEBUG(7),
|
2008-04-01 01:37:25 +00:00
|
|
|
"%s %.*s", op, (int)r.length,
|
|
|
|
(char *)r.base);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2000-12-09 02:17:12 +00:00
|
|
|
}
|
|
|
|
result = ISC_R_SUCCESS;
|
|
|
|
cleanup:
|
|
|
|
if (mem != NULL) {
|
|
|
|
isc_mem_put(diff->mctx, mem, size);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2000-12-09 02:17:12 +00:00
|
|
|
return result;
|
|
|
|
}
|