1*bcda20f6Schristos /* $NetBSD: diff.c,v 1.10 2025/01/26 16:25:22 christos Exp $ */ 2d68c78b8Schristos 3d68c78b8Schristos /* 4d68c78b8Schristos * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5d68c78b8Schristos * 68596601aSchristos * SPDX-License-Identifier: MPL-2.0 78596601aSchristos * 8d68c78b8Schristos * This Source Code Form is subject to the terms of the Mozilla Public 9d68c78b8Schristos * License, v. 2.0. If a copy of the MPL was not distributed with this 10fce770bdSchristos * file, you can obtain one at https://mozilla.org/MPL/2.0/. 11d68c78b8Schristos * 12d68c78b8Schristos * See the COPYRIGHT file distributed with this work for additional 13d68c78b8Schristos * information regarding copyright ownership. 14d68c78b8Schristos */ 15d68c78b8Schristos 16d68c78b8Schristos /*! \file */ 17d68c78b8Schristos 18d4a20c3eSchristos #include <inttypes.h> 19d4a20c3eSchristos #include <stdbool.h> 20*bcda20f6Schristos #include <stddef.h> 21d68c78b8Schristos #include <stdlib.h> 22d68c78b8Schristos 23d68c78b8Schristos #include <isc/buffer.h> 24d68c78b8Schristos #include <isc/file.h> 25d68c78b8Schristos #include <isc/mem.h> 26bb5aa156Schristos #include <isc/result.h> 27d68c78b8Schristos #include <isc/string.h> 28d68c78b8Schristos #include <isc/util.h> 29d68c78b8Schristos 30*bcda20f6Schristos #include <dns/callbacks.h> 31d68c78b8Schristos #include <dns/db.h> 32d68c78b8Schristos #include <dns/diff.h> 33d68c78b8Schristos #include <dns/log.h> 34d68c78b8Schristos #include <dns/rdataclass.h> 35d68c78b8Schristos #include <dns/rdatalist.h> 36d68c78b8Schristos #include <dns/rdataset.h> 37d68c78b8Schristos #include <dns/rdatastruct.h> 38d68c78b8Schristos #include <dns/rdatatype.h> 39d68c78b8Schristos #include <dns/time.h> 40d68c78b8Schristos 41d68c78b8Schristos #define CHECK(op) \ 425606745fSchristos do { \ 435606745fSchristos result = (op); \ 445606745fSchristos if (result != ISC_R_SUCCESS) \ 455606745fSchristos goto failure; \ 4653cc4e50Srillig } while (0) 47d68c78b8Schristos 48d68c78b8Schristos #define DIFF_COMMON_LOGARGS \ 49d68c78b8Schristos dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_DIFF 50d68c78b8Schristos 51d68c78b8Schristos static dns_rdatatype_t 52d68c78b8Schristos rdata_covers(dns_rdata_t *rdata) { 53*bcda20f6Schristos return rdata->type == dns_rdatatype_rrsig ? dns_rdata_covers(rdata) : 0; 54d68c78b8Schristos } 55d68c78b8Schristos 56d68c78b8Schristos isc_result_t 575606745fSchristos dns_difftuple_create(isc_mem_t *mctx, dns_diffop_t op, const dns_name_t *name, 585606745fSchristos dns_ttl_t ttl, dns_rdata_t *rdata, dns_difftuple_t **tp) { 59d68c78b8Schristos dns_difftuple_t *t; 60d68c78b8Schristos unsigned int size; 61d68c78b8Schristos unsigned char *datap; 62d68c78b8Schristos 63d68c78b8Schristos REQUIRE(tp != NULL && *tp == NULL); 64d68c78b8Schristos 65d68c78b8Schristos /* 66d68c78b8Schristos * Create a new tuple. The variable-size wire-format name data and 67d68c78b8Schristos * rdata immediately follow the dns_difftuple_t structure 68d68c78b8Schristos * in memory. 69d68c78b8Schristos */ 70d68c78b8Schristos size = sizeof(*t) + name->length + rdata->length; 71d68c78b8Schristos t = isc_mem_allocate(mctx, size); 72d68c78b8Schristos t->mctx = NULL; 73d68c78b8Schristos isc_mem_attach(mctx, &t->mctx); 74d68c78b8Schristos t->op = op; 75d68c78b8Schristos 76d68c78b8Schristos datap = (unsigned char *)(t + 1); 77d68c78b8Schristos 78d68c78b8Schristos memmove(datap, name->ndata, name->length); 79d68c78b8Schristos dns_name_init(&t->name, NULL); 80d68c78b8Schristos dns_name_clone(name, &t->name); 81d68c78b8Schristos t->name.ndata = datap; 82d68c78b8Schristos datap += name->length; 83d68c78b8Schristos 84d68c78b8Schristos t->ttl = ttl; 85d68c78b8Schristos 86d68c78b8Schristos dns_rdata_init(&t->rdata); 87d68c78b8Schristos dns_rdata_clone(rdata, &t->rdata); 88d68c78b8Schristos if (rdata->data != NULL) { 89d68c78b8Schristos memmove(datap, rdata->data, rdata->length); 90d68c78b8Schristos t->rdata.data = datap; 91d68c78b8Schristos datap += rdata->length; 92d68c78b8Schristos } else { 93d68c78b8Schristos t->rdata.data = NULL; 94d68c78b8Schristos INSIST(rdata->length == 0); 95d68c78b8Schristos } 96d68c78b8Schristos 97d68c78b8Schristos ISC_LINK_INIT(&t->rdata, link); 98d68c78b8Schristos ISC_LINK_INIT(t, link); 99d68c78b8Schristos t->magic = DNS_DIFFTUPLE_MAGIC; 100d68c78b8Schristos 101d68c78b8Schristos INSIST(datap == (unsigned char *)t + size); 102d68c78b8Schristos 103d68c78b8Schristos *tp = t; 104*bcda20f6Schristos return ISC_R_SUCCESS; 105d68c78b8Schristos } 106d68c78b8Schristos 107d68c78b8Schristos void 108d68c78b8Schristos dns_difftuple_free(dns_difftuple_t **tp) { 109d68c78b8Schristos dns_difftuple_t *t = *tp; 1105606745fSchristos *tp = NULL; 111d68c78b8Schristos isc_mem_t *mctx; 112d68c78b8Schristos 113d68c78b8Schristos REQUIRE(DNS_DIFFTUPLE_VALID(t)); 114d68c78b8Schristos 115d68c78b8Schristos dns_name_invalidate(&t->name); 116d68c78b8Schristos t->magic = 0; 117d68c78b8Schristos mctx = t->mctx; 118d68c78b8Schristos isc_mem_free(mctx, t); 119d68c78b8Schristos isc_mem_detach(&mctx); 120d68c78b8Schristos } 121d68c78b8Schristos 122d68c78b8Schristos isc_result_t 123d68c78b8Schristos dns_difftuple_copy(dns_difftuple_t *orig, dns_difftuple_t **copyp) { 124*bcda20f6Schristos return dns_difftuple_create(orig->mctx, orig->op, &orig->name, 125*bcda20f6Schristos orig->ttl, &orig->rdata, copyp); 126d68c78b8Schristos } 127d68c78b8Schristos 128d68c78b8Schristos void 129d68c78b8Schristos dns_diff_init(isc_mem_t *mctx, dns_diff_t *diff) { 130d68c78b8Schristos diff->mctx = mctx; 131d68c78b8Schristos ISC_LIST_INIT(diff->tuples); 132d68c78b8Schristos diff->magic = DNS_DIFF_MAGIC; 133*bcda20f6Schristos diff->size = 0; 134d68c78b8Schristos } 135d68c78b8Schristos 136d68c78b8Schristos void 137d68c78b8Schristos dns_diff_clear(dns_diff_t *diff) { 138d68c78b8Schristos dns_difftuple_t *t; 139d68c78b8Schristos REQUIRE(DNS_DIFF_VALID(diff)); 140d68c78b8Schristos while ((t = ISC_LIST_HEAD(diff->tuples)) != NULL) { 141d68c78b8Schristos ISC_LIST_UNLINK(diff->tuples, t, link); 142d68c78b8Schristos dns_difftuple_free(&t); 143d68c78b8Schristos } 144*bcda20f6Schristos diff->size = 0; 145d68c78b8Schristos ENSURE(ISC_LIST_EMPTY(diff->tuples)); 146d68c78b8Schristos } 147d68c78b8Schristos 148d68c78b8Schristos void 1495606745fSchristos dns_diff_append(dns_diff_t *diff, dns_difftuple_t **tuplep) { 150*bcda20f6Schristos REQUIRE(DNS_DIFF_VALID(diff)); 151d68c78b8Schristos ISC_LIST_APPEND(diff->tuples, *tuplep, link); 152*bcda20f6Schristos diff->size += 1; 153d68c78b8Schristos *tuplep = NULL; 154d68c78b8Schristos } 155d68c78b8Schristos 156*bcda20f6Schristos bool 157*bcda20f6Schristos dns_diff_is_boundary(const dns_diff_t *diff, dns_name_t *new_name) { 158*bcda20f6Schristos REQUIRE(DNS_DIFF_VALID(diff)); 159*bcda20f6Schristos REQUIRE(DNS_NAME_VALID(new_name)); 160*bcda20f6Schristos 161*bcda20f6Schristos if (ISC_LIST_EMPTY(diff->tuples)) { 162*bcda20f6Schristos return false; 163*bcda20f6Schristos } 164*bcda20f6Schristos 165*bcda20f6Schristos dns_difftuple_t *tail = ISC_LIST_TAIL(diff->tuples); 166*bcda20f6Schristos return !dns_name_caseequal(&tail->name, new_name); 167*bcda20f6Schristos } 168*bcda20f6Schristos 169*bcda20f6Schristos size_t 170*bcda20f6Schristos dns_diff_size(const dns_diff_t *diff) { 171*bcda20f6Schristos REQUIRE(DNS_DIFF_VALID(diff)); 172*bcda20f6Schristos return diff->size; 173*bcda20f6Schristos } 174*bcda20f6Schristos 175d68c78b8Schristos /* XXX this is O(N) */ 176d68c78b8Schristos 177d68c78b8Schristos void 1785606745fSchristos dns_diff_appendminimal(dns_diff_t *diff, dns_difftuple_t **tuplep) { 179d68c78b8Schristos dns_difftuple_t *ot, *next_ot; 180d68c78b8Schristos 181d68c78b8Schristos REQUIRE(DNS_DIFF_VALID(diff)); 182d68c78b8Schristos REQUIRE(DNS_DIFFTUPLE_VALID(*tuplep)); 183d68c78b8Schristos 184d68c78b8Schristos /* 185d68c78b8Schristos * Look for an existing tuple with the same owner name, 186d68c78b8Schristos * rdata, and TTL. If we are doing an addition and find a 187d68c78b8Schristos * deletion or vice versa, remove both the old and the 188d68c78b8Schristos * new tuple since they cancel each other out (assuming 189d68c78b8Schristos * that we never delete nonexistent data or add existing 190d68c78b8Schristos * data). 191d68c78b8Schristos * 192d68c78b8Schristos * If we find an old update of the same kind as 193d68c78b8Schristos * the one we are doing, there must be a programming 194d68c78b8Schristos * error. We report it but try to continue anyway. 195d68c78b8Schristos */ 1965606745fSchristos for (ot = ISC_LIST_HEAD(diff->tuples); ot != NULL; ot = next_ot) { 197d68c78b8Schristos next_ot = ISC_LIST_NEXT(ot, link); 198d68c78b8Schristos if (dns_name_caseequal(&ot->name, &(*tuplep)->name) && 199d68c78b8Schristos dns_rdata_compare(&ot->rdata, &(*tuplep)->rdata) == 0 && 200d68c78b8Schristos ot->ttl == (*tuplep)->ttl) 201d68c78b8Schristos { 202d68c78b8Schristos ISC_LIST_UNLINK(diff->tuples, ot, link); 203*bcda20f6Schristos INSIST(diff->size > 0); 204*bcda20f6Schristos diff->size -= 1; 205*bcda20f6Schristos 206d68c78b8Schristos if ((*tuplep)->op == ot->op) { 207bb5aa156Schristos UNEXPECTED_ERROR("unexpected non-minimal diff"); 208d68c78b8Schristos } else { 209d68c78b8Schristos dns_difftuple_free(tuplep); 210d68c78b8Schristos } 211d68c78b8Schristos dns_difftuple_free(&ot); 212d68c78b8Schristos break; 213d68c78b8Schristos } 214d68c78b8Schristos } 215d68c78b8Schristos 216d68c78b8Schristos if (*tuplep != NULL) { 217d68c78b8Schristos ISC_LIST_APPEND(diff->tuples, *tuplep, link); 218*bcda20f6Schristos diff->size += 1; 219d68c78b8Schristos *tuplep = NULL; 220d68c78b8Schristos } 221d68c78b8Schristos } 222d68c78b8Schristos 223d68c78b8Schristos static isc_stdtime_t 224d68c78b8Schristos setresign(dns_rdataset_t *modified) { 225d68c78b8Schristos dns_rdata_t rdata = DNS_RDATA_INIT; 226d68c78b8Schristos dns_rdata_rrsig_t sig; 227d4a20c3eSchristos int64_t when; 228d68c78b8Schristos isc_result_t result; 229d68c78b8Schristos 230d68c78b8Schristos result = dns_rdataset_first(modified); 231d68c78b8Schristos INSIST(result == ISC_R_SUCCESS); 232d68c78b8Schristos dns_rdataset_current(modified, &rdata); 233d68c78b8Schristos (void)dns_rdata_tostruct(&rdata, &sig, NULL); 2345606745fSchristos if ((rdata.flags & DNS_RDATA_OFFLINE) != 0) { 235d68c78b8Schristos when = 0; 2365606745fSchristos } else { 237d68c78b8Schristos when = dns_time64_from32(sig.timeexpire); 2385606745fSchristos } 239d68c78b8Schristos dns_rdata_reset(&rdata); 240d68c78b8Schristos 241d68c78b8Schristos result = dns_rdataset_next(modified); 242d68c78b8Schristos while (result == ISC_R_SUCCESS) { 243d68c78b8Schristos dns_rdataset_current(modified, &rdata); 244d68c78b8Schristos (void)dns_rdata_tostruct(&rdata, &sig, NULL); 245d68c78b8Schristos if ((rdata.flags & DNS_RDATA_OFFLINE) != 0) { 246d68c78b8Schristos goto next_rr; 247d68c78b8Schristos } 2485606745fSchristos if (when == 0 || dns_time64_from32(sig.timeexpire) < when) { 249d68c78b8Schristos when = dns_time64_from32(sig.timeexpire); 2505606745fSchristos } 251d68c78b8Schristos next_rr: 252d68c78b8Schristos dns_rdata_reset(&rdata); 253d68c78b8Schristos result = dns_rdataset_next(modified); 254d68c78b8Schristos } 255d68c78b8Schristos INSIST(result == ISC_R_NOMORE); 256*bcda20f6Schristos return (isc_stdtime_t)when; 257d68c78b8Schristos } 258d68c78b8Schristos 259d68c78b8Schristos static void 260d68c78b8Schristos getownercase(dns_rdataset_t *rdataset, dns_name_t *name) { 2615606745fSchristos if (dns_rdataset_isassociated(rdataset)) { 262d68c78b8Schristos dns_rdataset_getownercase(rdataset, name); 263d68c78b8Schristos } 2645606745fSchristos } 265d68c78b8Schristos 266d68c78b8Schristos static void 267d68c78b8Schristos setownercase(dns_rdataset_t *rdataset, const dns_name_t *name) { 2685606745fSchristos if (dns_rdataset_isassociated(rdataset)) { 269d68c78b8Schristos dns_rdataset_setownercase(rdataset, name); 270d68c78b8Schristos } 2715606745fSchristos } 272d68c78b8Schristos 273bb5aa156Schristos static const char * 274bb5aa156Schristos optotext(dns_diffop_t op) { 275bb5aa156Schristos switch (op) { 276bb5aa156Schristos case DNS_DIFFOP_ADD: 277*bcda20f6Schristos return "add"; 278bb5aa156Schristos case DNS_DIFFOP_ADDRESIGN: 279*bcda20f6Schristos return "add-resign"; 280bb5aa156Schristos case DNS_DIFFOP_DEL: 281*bcda20f6Schristos return "del"; 282bb5aa156Schristos case DNS_DIFFOP_DELRESIGN: 283*bcda20f6Schristos return "del-resign"; 284bb5aa156Schristos default: 285*bcda20f6Schristos return "unknown"; 286bb5aa156Schristos } 287bb5aa156Schristos } 288bb5aa156Schristos 289d68c78b8Schristos static isc_result_t 290*bcda20f6Schristos diff_apply(const dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *ver, 291*bcda20f6Schristos bool warn) { 292d68c78b8Schristos dns_difftuple_t *t; 293d68c78b8Schristos dns_dbnode_t *node = NULL; 294d68c78b8Schristos isc_result_t result; 295d68c78b8Schristos char namebuf[DNS_NAME_FORMATSIZE]; 296d68c78b8Schristos char typebuf[DNS_RDATATYPE_FORMATSIZE]; 297d68c78b8Schristos char classbuf[DNS_RDATACLASS_FORMATSIZE]; 298d68c78b8Schristos 299d68c78b8Schristos REQUIRE(DNS_DIFF_VALID(diff)); 300d68c78b8Schristos REQUIRE(DNS_DB_VALID(db)); 301d68c78b8Schristos 302d68c78b8Schristos t = ISC_LIST_HEAD(diff->tuples); 303d68c78b8Schristos while (t != NULL) { 304d68c78b8Schristos dns_name_t *name; 305d68c78b8Schristos 306d68c78b8Schristos INSIST(node == NULL); 307d68c78b8Schristos name = &t->name; 308d68c78b8Schristos /* 309d68c78b8Schristos * Find the node. 310d68c78b8Schristos * We create the node if it does not exist. 311d68c78b8Schristos * This will cause an empty node to be created if the diff 312d68c78b8Schristos * contains a deletion of an RR at a nonexistent name, 313d68c78b8Schristos * but such diffs should never be created in the first 314d68c78b8Schristos * place. 315d68c78b8Schristos */ 316d68c78b8Schristos 317d68c78b8Schristos while (t != NULL && dns_name_equal(&t->name, name)) { 318d68c78b8Schristos dns_rdatatype_t type, covers; 319bb5aa156Schristos dns_rdataclass_t rdclass; 320d68c78b8Schristos dns_diffop_t op; 321d68c78b8Schristos dns_rdatalist_t rdl; 322d68c78b8Schristos dns_rdataset_t rds; 323d68c78b8Schristos dns_rdataset_t ardataset; 324d68c78b8Schristos unsigned int options; 325d68c78b8Schristos 326d68c78b8Schristos op = t->op; 327d68c78b8Schristos type = t->rdata.type; 328bb5aa156Schristos rdclass = t->rdata.rdclass; 329d68c78b8Schristos covers = rdata_covers(&t->rdata); 330d68c78b8Schristos 331d68c78b8Schristos /* 332d68c78b8Schristos * Collect a contiguous set of updates with 333d68c78b8Schristos * the same operation (add/delete) and RR type 334d68c78b8Schristos * into a single rdatalist so that the 335d68c78b8Schristos * database rrset merging/subtraction code 336d68c78b8Schristos * can work more efficiently than if each 337d68c78b8Schristos * RR were merged into / subtracted from 338d68c78b8Schristos * the database separately. 339d68c78b8Schristos * 340d68c78b8Schristos * This is done by linking rdata structures from the 341d68c78b8Schristos * diff into "rdatalist". This uses the rdata link 342d68c78b8Schristos * field, not the diff link field, so the structure 343d68c78b8Schristos * of the diff itself is not affected. 344d68c78b8Schristos */ 345d68c78b8Schristos 346d68c78b8Schristos dns_rdatalist_init(&rdl); 347d68c78b8Schristos rdl.type = type; 348d68c78b8Schristos rdl.covers = covers; 349d68c78b8Schristos rdl.rdclass = t->rdata.rdclass; 350d68c78b8Schristos rdl.ttl = t->ttl; 351d68c78b8Schristos 352d68c78b8Schristos node = NULL; 353d68c78b8Schristos if (type != dns_rdatatype_nsec3 && 354903adeddSchristos covers != dns_rdatatype_nsec3) 355903adeddSchristos { 3565606745fSchristos CHECK(dns_db_findnode(db, name, true, &node)); 3575606745fSchristos } else { 358d4a20c3eSchristos CHECK(dns_db_findnsec3node(db, name, true, 359d68c78b8Schristos &node)); 3605606745fSchristos } 361d68c78b8Schristos 3625606745fSchristos while (t != NULL && dns_name_equal(&t->name, name) && 3635606745fSchristos t->op == op && t->rdata.type == type && 364d68c78b8Schristos rdata_covers(&t->rdata) == covers) 365d68c78b8Schristos { 366d68c78b8Schristos /* 367d68c78b8Schristos * Remember the add name for 368d68c78b8Schristos * dns_rdataset_setownercase. 369d68c78b8Schristos */ 370d68c78b8Schristos name = &t->name; 371d68c78b8Schristos if (t->ttl != rdl.ttl && warn) { 372d68c78b8Schristos dns_name_format(name, namebuf, 373d68c78b8Schristos sizeof(namebuf)); 374d68c78b8Schristos dns_rdatatype_format(t->rdata.type, 375d68c78b8Schristos typebuf, 376d68c78b8Schristos sizeof(typebuf)); 377d68c78b8Schristos dns_rdataclass_format(t->rdata.rdclass, 378d68c78b8Schristos classbuf, 379d68c78b8Schristos sizeof(classbuf)); 380d68c78b8Schristos isc_log_write(DIFF_COMMON_LOGARGS, 381d68c78b8Schristos ISC_LOG_WARNING, 3825606745fSchristos "'%s/%s/%s': TTL differs " 3835606745fSchristos "in " 384d68c78b8Schristos "rdataset, adjusting " 385d68c78b8Schristos "%lu -> %lu", 3865606745fSchristos namebuf, typebuf, 3875606745fSchristos classbuf, 388d68c78b8Schristos (unsigned long)t->ttl, 389d68c78b8Schristos (unsigned long)rdl.ttl); 390d68c78b8Schristos } 391d68c78b8Schristos ISC_LIST_APPEND(rdl.rdata, &t->rdata, link); 392d68c78b8Schristos t = ISC_LIST_NEXT(t, link); 393d68c78b8Schristos } 394d68c78b8Schristos 395d68c78b8Schristos /* 396d68c78b8Schristos * Convert the rdatalist into a rdataset. 397d68c78b8Schristos */ 398d68c78b8Schristos dns_rdataset_init(&rds); 399d68c78b8Schristos dns_rdataset_init(&ardataset); 400*bcda20f6Schristos dns_rdatalist_tordataset(&rdl, &rds); 401d68c78b8Schristos rds.trust = dns_trust_ultimate; 402d68c78b8Schristos 403d68c78b8Schristos /* 404d68c78b8Schristos * Merge the rdataset into the database. 405d68c78b8Schristos */ 406d68c78b8Schristos switch (op) { 407d68c78b8Schristos case DNS_DIFFOP_ADD: 408d68c78b8Schristos case DNS_DIFFOP_ADDRESIGN: 409d68c78b8Schristos options = DNS_DBADD_MERGE | DNS_DBADD_EXACT | 410d68c78b8Schristos DNS_DBADD_EXACTTTL; 4115606745fSchristos result = dns_db_addrdataset(db, node, ver, 0, 4125606745fSchristos &rds, options, 413d68c78b8Schristos &ardataset); 414d68c78b8Schristos break; 415d68c78b8Schristos case DNS_DIFFOP_DEL: 416d68c78b8Schristos case DNS_DIFFOP_DELRESIGN: 417d68c78b8Schristos options = DNS_DBSUB_EXACT | DNS_DBSUB_WANTOLD; 418d68c78b8Schristos result = dns_db_subtractrdataset(db, node, ver, 419d68c78b8Schristos &rds, options, 420d68c78b8Schristos &ardataset); 421d68c78b8Schristos break; 422d68c78b8Schristos default: 4238596601aSchristos UNREACHABLE(); 424d68c78b8Schristos } 425d68c78b8Schristos 426d68c78b8Schristos if (result == ISC_R_SUCCESS) { 427d68c78b8Schristos if (rds.type == dns_rdatatype_rrsig && 428d68c78b8Schristos (op == DNS_DIFFOP_DELRESIGN || 4295606745fSchristos op == DNS_DIFFOP_ADDRESIGN)) 4305606745fSchristos { 431d68c78b8Schristos isc_stdtime_t resign; 432d68c78b8Schristos resign = setresign(&ardataset); 433d68c78b8Schristos dns_db_setsigningtime(db, &ardataset, 434d68c78b8Schristos resign); 435d68c78b8Schristos } 436d68c78b8Schristos if (op == DNS_DIFFOP_ADD || 437903adeddSchristos op == DNS_DIFFOP_ADDRESIGN) 438903adeddSchristos { 439d68c78b8Schristos setownercase(&ardataset, name); 4405606745fSchristos } 441d68c78b8Schristos if (op == DNS_DIFFOP_DEL || 442903adeddSchristos op == DNS_DIFFOP_DELRESIGN) 443903adeddSchristos { 444d68c78b8Schristos getownercase(&ardataset, name); 4455606745fSchristos } 446d68c78b8Schristos } else if (result == DNS_R_UNCHANGED) { 447d68c78b8Schristos /* 448d68c78b8Schristos * This will not happen when executing a 449d68c78b8Schristos * dynamic update, because that code will 450d68c78b8Schristos * generate strictly minimal diffs. 451d68c78b8Schristos * It may happen when receiving an IXFR 452d68c78b8Schristos * from a server that is not as careful. 453d68c78b8Schristos * Issue a warning and continue. 454d68c78b8Schristos */ 455d68c78b8Schristos if (warn) { 456d68c78b8Schristos dns_name_format(dns_db_origin(db), 457d68c78b8Schristos namebuf, 458d68c78b8Schristos sizeof(namebuf)); 459d68c78b8Schristos dns_rdataclass_format(dns_db_class(db), 460d68c78b8Schristos classbuf, 461d68c78b8Schristos sizeof(classbuf)); 462d68c78b8Schristos isc_log_write(DIFF_COMMON_LOGARGS, 463d68c78b8Schristos ISC_LOG_WARNING, 464d68c78b8Schristos "%s/%s: dns_diff_apply: " 465d68c78b8Schristos "update with no effect", 466d68c78b8Schristos namebuf, classbuf); 467d68c78b8Schristos } 468d68c78b8Schristos if (op == DNS_DIFFOP_ADD || 469903adeddSchristos op == DNS_DIFFOP_ADDRESIGN) 470903adeddSchristos { 471d68c78b8Schristos setownercase(&ardataset, name); 4725606745fSchristos } 473d68c78b8Schristos if (op == DNS_DIFFOP_DEL || 474903adeddSchristos op == DNS_DIFFOP_DELRESIGN) 475903adeddSchristos { 476d68c78b8Schristos getownercase(&ardataset, name); 4775606745fSchristos } 478d68c78b8Schristos } else if (result == DNS_R_NXRRSET) { 479d68c78b8Schristos /* 480d68c78b8Schristos * OK. 481d68c78b8Schristos */ 482d68c78b8Schristos if (op == DNS_DIFFOP_DEL || 483903adeddSchristos op == DNS_DIFFOP_DELRESIGN) 484903adeddSchristos { 485d68c78b8Schristos getownercase(&ardataset, name); 4865606745fSchristos } 4875606745fSchristos if (dns_rdataset_isassociated(&ardataset)) { 488d68c78b8Schristos dns_rdataset_disassociate(&ardataset); 4895606745fSchristos } 490d68c78b8Schristos } else { 491bb5aa156Schristos if (result == DNS_R_NOTEXACT) { 492bb5aa156Schristos dns_name_format(name, namebuf, 493bb5aa156Schristos sizeof(namebuf)); 494bb5aa156Schristos dns_rdatatype_format(type, typebuf, 495bb5aa156Schristos sizeof(typebuf)); 496bb5aa156Schristos dns_rdataclass_format(rdclass, classbuf, 497bb5aa156Schristos sizeof(classbuf)); 498bb5aa156Schristos isc_log_write( 499bb5aa156Schristos DIFF_COMMON_LOGARGS, 500bb5aa156Schristos ISC_LOG_ERROR, 501bb5aa156Schristos "dns_diff_apply: %s/%s/%s: %s " 502bb5aa156Schristos "%s", 503bb5aa156Schristos namebuf, typebuf, classbuf, 504bb5aa156Schristos optotext(op), 505bb5aa156Schristos isc_result_totext(result)); 506bb5aa156Schristos } 5075606745fSchristos if (dns_rdataset_isassociated(&ardataset)) { 508d68c78b8Schristos dns_rdataset_disassociate(&ardataset); 5095606745fSchristos } 510d68c78b8Schristos CHECK(result); 511d68c78b8Schristos } 512d68c78b8Schristos dns_db_detachnode(db, &node); 5135606745fSchristos if (dns_rdataset_isassociated(&ardataset)) { 514d68c78b8Schristos dns_rdataset_disassociate(&ardataset); 515d68c78b8Schristos } 516d68c78b8Schristos } 5175606745fSchristos } 518*bcda20f6Schristos return ISC_R_SUCCESS; 519d68c78b8Schristos 520d68c78b8Schristos failure: 5215606745fSchristos if (node != NULL) { 522d68c78b8Schristos dns_db_detachnode(db, &node); 5235606745fSchristos } 524*bcda20f6Schristos return result; 525d68c78b8Schristos } 526d68c78b8Schristos 527d68c78b8Schristos isc_result_t 528*bcda20f6Schristos dns_diff_apply(const dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *ver) { 529*bcda20f6Schristos return diff_apply(diff, db, ver, true); 530d68c78b8Schristos } 531d68c78b8Schristos 532d68c78b8Schristos isc_result_t 533*bcda20f6Schristos dns_diff_applysilently(const dns_diff_t *diff, dns_db_t *db, 534*bcda20f6Schristos dns_dbversion_t *ver) { 535*bcda20f6Schristos return diff_apply(diff, db, ver, false); 536d68c78b8Schristos } 537d68c78b8Schristos 538d68c78b8Schristos /* XXX this duplicates lots of code in diff_apply(). */ 539d68c78b8Schristos 540d68c78b8Schristos isc_result_t 541*bcda20f6Schristos dns_diff_load(const dns_diff_t *diff, dns_rdatacallbacks_t *callbacks) { 542d68c78b8Schristos dns_difftuple_t *t; 543d68c78b8Schristos isc_result_t result; 544d68c78b8Schristos 545d68c78b8Schristos REQUIRE(DNS_DIFF_VALID(diff)); 546d68c78b8Schristos 547*bcda20f6Schristos if (callbacks->setup != NULL) { 548*bcda20f6Schristos callbacks->setup(callbacks->add_private); 549*bcda20f6Schristos } 550*bcda20f6Schristos 551d68c78b8Schristos t = ISC_LIST_HEAD(diff->tuples); 552d68c78b8Schristos while (t != NULL) { 553d68c78b8Schristos dns_name_t *name; 554d68c78b8Schristos 555d68c78b8Schristos name = &t->name; 556d4a20c3eSchristos while (t != NULL && dns_name_caseequal(&t->name, name)) { 557d68c78b8Schristos dns_rdatatype_t type, covers; 558d68c78b8Schristos dns_diffop_t op; 559d68c78b8Schristos dns_rdatalist_t rdl; 560d68c78b8Schristos dns_rdataset_t rds; 561d68c78b8Schristos 562d68c78b8Schristos op = t->op; 563d68c78b8Schristos type = t->rdata.type; 564d68c78b8Schristos covers = rdata_covers(&t->rdata); 565d68c78b8Schristos 566d68c78b8Schristos dns_rdatalist_init(&rdl); 567d68c78b8Schristos rdl.type = type; 568d68c78b8Schristos rdl.covers = covers; 569d68c78b8Schristos rdl.rdclass = t->rdata.rdclass; 570d68c78b8Schristos rdl.ttl = t->ttl; 571d68c78b8Schristos 5725606745fSchristos while (t != NULL && 5735606745fSchristos dns_name_caseequal(&t->name, name) && 574d68c78b8Schristos t->op == op && t->rdata.type == type && 575d68c78b8Schristos rdata_covers(&t->rdata) == covers) 576d68c78b8Schristos { 577d68c78b8Schristos ISC_LIST_APPEND(rdl.rdata, &t->rdata, link); 578d68c78b8Schristos t = ISC_LIST_NEXT(t, link); 579d68c78b8Schristos } 580d68c78b8Schristos 581d68c78b8Schristos /* 582d68c78b8Schristos * Convert the rdatalist into a rdataset. 583d68c78b8Schristos */ 584d68c78b8Schristos dns_rdataset_init(&rds); 585*bcda20f6Schristos dns_rdatalist_tordataset(&rdl, &rds); 586d68c78b8Schristos rds.trust = dns_trust_ultimate; 587d68c78b8Schristos 588d68c78b8Schristos INSIST(op == DNS_DIFFOP_ADD); 589*bcda20f6Schristos result = callbacks->add(callbacks->add_private, name, 590*bcda20f6Schristos &rds DNS__DB_FILELINE); 591d68c78b8Schristos if (result == DNS_R_UNCHANGED) { 592d68c78b8Schristos isc_log_write(DIFF_COMMON_LOGARGS, 593d68c78b8Schristos ISC_LOG_WARNING, 594d68c78b8Schristos "dns_diff_load: " 595d68c78b8Schristos "update with no effect"); 596d68c78b8Schristos } else if (result == ISC_R_SUCCESS || 597903adeddSchristos result == DNS_R_NXRRSET) 598903adeddSchristos { 599d68c78b8Schristos /* 600d68c78b8Schristos * OK. 601d68c78b8Schristos */ 602d68c78b8Schristos } else { 603d68c78b8Schristos CHECK(result); 604d68c78b8Schristos } 605d68c78b8Schristos } 606d68c78b8Schristos } 607d68c78b8Schristos result = ISC_R_SUCCESS; 608*bcda20f6Schristos 609d68c78b8Schristos failure: 610*bcda20f6Schristos if (callbacks->commit != NULL) { 611*bcda20f6Schristos callbacks->commit(callbacks->add_private); 612*bcda20f6Schristos } 613*bcda20f6Schristos return result; 614d68c78b8Schristos } 615d68c78b8Schristos 616d68c78b8Schristos /* 617d68c78b8Schristos * XXX uses qsort(); a merge sort would be more natural for lists, 618d68c78b8Schristos * and perhaps safer wrt thread stack overflow. 619d68c78b8Schristos */ 620d68c78b8Schristos isc_result_t 621d68c78b8Schristos dns_diff_sort(dns_diff_t *diff, dns_diff_compare_func *compare) { 622d68c78b8Schristos unsigned int length = 0; 623d68c78b8Schristos unsigned int i; 624d68c78b8Schristos dns_difftuple_t **v; 625d68c78b8Schristos dns_difftuple_t *p; 626d68c78b8Schristos REQUIRE(DNS_DIFF_VALID(diff)); 627d68c78b8Schristos 6285606745fSchristos for (p = ISC_LIST_HEAD(diff->tuples); p != NULL; 629903adeddSchristos p = ISC_LIST_NEXT(p, link)) 630903adeddSchristos { 631d68c78b8Schristos length++; 6325606745fSchristos } 6335606745fSchristos if (length == 0) { 634*bcda20f6Schristos return ISC_R_SUCCESS; 6355606745fSchristos } 636*bcda20f6Schristos v = isc_mem_cget(diff->mctx, length, sizeof(dns_difftuple_t *)); 637d68c78b8Schristos for (i = 0; i < length; i++) { 638d68c78b8Schristos p = ISC_LIST_HEAD(diff->tuples); 639d68c78b8Schristos v[i] = p; 640d68c78b8Schristos ISC_LIST_UNLINK(diff->tuples, p, link); 641d68c78b8Schristos } 642d68c78b8Schristos INSIST(ISC_LIST_HEAD(diff->tuples) == NULL); 643d68c78b8Schristos qsort(v, length, sizeof(v[0]), compare); 644d68c78b8Schristos for (i = 0; i < length; i++) { 645d68c78b8Schristos ISC_LIST_APPEND(diff->tuples, v[i], link); 646d68c78b8Schristos } 647*bcda20f6Schristos isc_mem_cput(diff->mctx, v, length, sizeof(dns_difftuple_t *)); 648*bcda20f6Schristos return ISC_R_SUCCESS; 649d68c78b8Schristos } 650d68c78b8Schristos 651d68c78b8Schristos /* 652d68c78b8Schristos * Create an rdataset containing the single RR of the given 653d68c78b8Schristos * tuple. The caller must allocate the rdata, rdataset and 654d68c78b8Schristos * an rdatalist structure for it to refer to. 655d68c78b8Schristos */ 656d68c78b8Schristos 657*bcda20f6Schristos static void 658d68c78b8Schristos diff_tuple_tordataset(dns_difftuple_t *t, dns_rdata_t *rdata, 6595606745fSchristos dns_rdatalist_t *rdl, dns_rdataset_t *rds) { 660d68c78b8Schristos REQUIRE(DNS_DIFFTUPLE_VALID(t)); 661d68c78b8Schristos REQUIRE(rdl != NULL); 662d68c78b8Schristos REQUIRE(rds != NULL); 663d68c78b8Schristos 664d68c78b8Schristos dns_rdatalist_init(rdl); 665d68c78b8Schristos rdl->type = t->rdata.type; 666d68c78b8Schristos rdl->rdclass = t->rdata.rdclass; 667d68c78b8Schristos rdl->ttl = t->ttl; 668d68c78b8Schristos dns_rdataset_init(rds); 669d68c78b8Schristos ISC_LINK_INIT(rdata, link); 670d68c78b8Schristos dns_rdata_clone(&t->rdata, rdata); 671d68c78b8Schristos ISC_LIST_APPEND(rdl->rdata, rdata, link); 672*bcda20f6Schristos dns_rdatalist_tordataset(rdl, rds); 673d68c78b8Schristos } 674d68c78b8Schristos 675d68c78b8Schristos isc_result_t 676*bcda20f6Schristos dns_diff_print(const dns_diff_t *diff, FILE *file) { 677d68c78b8Schristos isc_result_t result; 678d68c78b8Schristos dns_difftuple_t *t; 679d68c78b8Schristos char *mem = NULL; 680d68c78b8Schristos unsigned int size = 2048; 681d68c78b8Schristos const char *op = NULL; 682d68c78b8Schristos 683d68c78b8Schristos REQUIRE(DNS_DIFF_VALID(diff)); 684d68c78b8Schristos 685d68c78b8Schristos mem = isc_mem_get(diff->mctx, size); 686d68c78b8Schristos 687d68c78b8Schristos for (t = ISC_LIST_HEAD(diff->tuples); t != NULL; 688903adeddSchristos t = ISC_LIST_NEXT(t, link)) 689903adeddSchristos { 690d68c78b8Schristos isc_buffer_t buf; 691d68c78b8Schristos isc_region_t r; 692d68c78b8Schristos 693d68c78b8Schristos dns_rdatalist_t rdl; 694d68c78b8Schristos dns_rdataset_t rds; 695d68c78b8Schristos dns_rdata_t rd = DNS_RDATA_INIT; 696d68c78b8Schristos 697*bcda20f6Schristos diff_tuple_tordataset(t, &rd, &rdl, &rds); 698d68c78b8Schristos again: 699d68c78b8Schristos isc_buffer_init(&buf, mem, size); 7005606745fSchristos result = dns_rdataset_totext(&rds, &t->name, false, false, 7015606745fSchristos &buf); 702d68c78b8Schristos 703d68c78b8Schristos if (result == ISC_R_NOSPACE) { 704d68c78b8Schristos isc_mem_put(diff->mctx, mem, size); 705d68c78b8Schristos size += 1024; 706d68c78b8Schristos mem = isc_mem_get(diff->mctx, size); 707d68c78b8Schristos goto again; 708d68c78b8Schristos } 709d68c78b8Schristos 7105606745fSchristos if (result != ISC_R_SUCCESS) { 711d68c78b8Schristos goto cleanup; 7125606745fSchristos } 713d68c78b8Schristos /* 714d68c78b8Schristos * Get rid of final newline. 715d68c78b8Schristos */ 716d68c78b8Schristos INSIST(buf.used >= 1 && 717d68c78b8Schristos ((char *)buf.base)[buf.used - 1] == '\n'); 718d68c78b8Schristos buf.used--; 719d68c78b8Schristos 720d68c78b8Schristos isc_buffer_usedregion(&buf, &r); 721d68c78b8Schristos switch (t->op) { 7225606745fSchristos case DNS_DIFFOP_EXISTS: 7235606745fSchristos op = "exists"; 7245606745fSchristos break; 7255606745fSchristos case DNS_DIFFOP_ADD: 7265606745fSchristos op = "add"; 7275606745fSchristos break; 7285606745fSchristos case DNS_DIFFOP_DEL: 7295606745fSchristos op = "del"; 7305606745fSchristos break; 7315606745fSchristos case DNS_DIFFOP_ADDRESIGN: 7325606745fSchristos op = "add re-sign"; 7335606745fSchristos break; 7345606745fSchristos case DNS_DIFFOP_DELRESIGN: 7355606745fSchristos op = "del re-sign"; 7365606745fSchristos break; 737d68c78b8Schristos } 7385606745fSchristos if (file != NULL) { 739d68c78b8Schristos fprintf(file, "%s %.*s\n", op, (int)r.length, 740d68c78b8Schristos (char *)r.base); 7415606745fSchristos } else { 742d68c78b8Schristos isc_log_write(DIFF_COMMON_LOGARGS, ISC_LOG_DEBUG(7), 743d68c78b8Schristos "%s %.*s", op, (int)r.length, 744d68c78b8Schristos (char *)r.base); 745d68c78b8Schristos } 7465606745fSchristos } 747d68c78b8Schristos result = ISC_R_SUCCESS; 748d68c78b8Schristos cleanup: 7495606745fSchristos if (mem != NULL) { 750d68c78b8Schristos isc_mem_put(diff->mctx, mem, size); 7515606745fSchristos } 752*bcda20f6Schristos return result; 753d68c78b8Schristos } 754