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