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