1/* $NetBSD: masterdump.c,v 1.14 2024/02/21 22:52:07 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 <stdlib.h> 21 22#include <isc/atomic.h> 23#include <isc/buffer.h> 24#include <isc/event.h> 25#include <isc/file.h> 26#include <isc/magic.h> 27#include <isc/mem.h> 28#include <isc/print.h> 29#include <isc/refcount.h> 30#include <isc/result.h> 31#include <isc/stdio.h> 32#include <isc/string.h> 33#include <isc/task.h> 34#include <isc/time.h> 35#include <isc/types.h> 36#include <isc/util.h> 37 38#include <dns/db.h> 39#include <dns/dbiterator.h> 40#include <dns/events.h> 41#include <dns/fixedname.h> 42#include <dns/log.h> 43#include <dns/master.h> 44#include <dns/masterdump.h> 45#include <dns/ncache.h> 46#include <dns/rdata.h> 47#include <dns/rdataclass.h> 48#include <dns/rdataset.h> 49#include <dns/rdatasetiter.h> 50#include <dns/rdatatype.h> 51#include <dns/time.h> 52#include <dns/ttl.h> 53 54#define DNS_DCTX_MAGIC ISC_MAGIC('D', 'c', 't', 'x') 55#define DNS_DCTX_VALID(d) ISC_MAGIC_VALID(d, DNS_DCTX_MAGIC) 56 57#define RETERR(x) \ 58 do { \ 59 isc_result_t _r = (x); \ 60 if (_r != ISC_R_SUCCESS) \ 61 return ((_r)); \ 62 } while (0) 63 64#define CHECK(x) \ 65 do { \ 66 if ((x) != ISC_R_SUCCESS) \ 67 goto cleanup; \ 68 } while (0) 69 70struct dns_master_style { 71 dns_masterstyle_flags_t flags; /* DNS_STYLEFLAG_* */ 72 unsigned int ttl_column; 73 unsigned int class_column; 74 unsigned int type_column; 75 unsigned int rdata_column; 76 unsigned int line_length; 77 unsigned int tab_width; 78 unsigned int split_width; 79}; 80 81/*% 82 * The maximum length of the newline+indentation that is output 83 * when inserting a line break in an RR. This effectively puts an 84 * upper limits on the value of "rdata_column", because if it is 85 * very large, the tabs and spaces needed to reach it will not fit. 86 */ 87#define DNS_TOTEXT_LINEBREAK_MAXLEN 100 88 89/*% Does the rdataset 'r' contain a stale answer? */ 90#define STALE(r) (((r)->attributes & DNS_RDATASETATTR_STALE) != 0) 91/*% Does the rdataset 'r' contain an expired answer? */ 92#define ANCIENT(r) (((r)->attributes & DNS_RDATASETATTR_ANCIENT) != 0) 93 94/*% 95 * Context structure for a masterfile dump in progress. 96 */ 97typedef struct dns_totext_ctx { 98 dns_master_style_t style; 99 bool class_printed; 100 char *linebreak; 101 char linebreak_buf[DNS_TOTEXT_LINEBREAK_MAXLEN]; 102 dns_name_t *origin; 103 dns_name_t *neworigin; 104 dns_fixedname_t origin_fixname; 105 uint32_t current_ttl; 106 bool current_ttl_valid; 107 dns_ttl_t serve_stale_ttl; 108 dns_indent_t indent; 109} dns_totext_ctx_t; 110 111const dns_master_style_t dns_master_style_keyzone = { 112 DNS_STYLEFLAG_OMIT_OWNER | DNS_STYLEFLAG_OMIT_CLASS | 113 DNS_STYLEFLAG_REL_OWNER | DNS_STYLEFLAG_REL_DATA | 114 DNS_STYLEFLAG_OMIT_TTL | DNS_STYLEFLAG_TTL | 115 DNS_STYLEFLAG_COMMENT | DNS_STYLEFLAG_RRCOMMENT | 116 DNS_STYLEFLAG_MULTILINE | DNS_STYLEFLAG_KEYDATA, 117 24, 118 24, 119 24, 120 32, 121 80, 122 8, 123 UINT_MAX 124}; 125 126const dns_master_style_t dns_master_style_default = { 127 DNS_STYLEFLAG_OMIT_OWNER | DNS_STYLEFLAG_OMIT_CLASS | 128 DNS_STYLEFLAG_REL_OWNER | DNS_STYLEFLAG_REL_DATA | 129 DNS_STYLEFLAG_OMIT_TTL | DNS_STYLEFLAG_TTL | 130 DNS_STYLEFLAG_COMMENT | DNS_STYLEFLAG_RRCOMMENT | 131 DNS_STYLEFLAG_MULTILINE, 132 24, 133 24, 134 24, 135 32, 136 80, 137 8, 138 UINT_MAX 139}; 140 141const dns_master_style_t dns_master_style_full = { 142 DNS_STYLEFLAG_COMMENT | DNS_STYLEFLAG_RESIGN, 143 46, 144 46, 145 46, 146 64, 147 120, 148 8, 149 UINT_MAX 150}; 151 152const dns_master_style_t dns_master_style_explicitttl = { 153 DNS_STYLEFLAG_OMIT_OWNER | DNS_STYLEFLAG_OMIT_CLASS | 154 DNS_STYLEFLAG_REL_OWNER | DNS_STYLEFLAG_REL_DATA | 155 DNS_STYLEFLAG_COMMENT | DNS_STYLEFLAG_RRCOMMENT | 156 DNS_STYLEFLAG_MULTILINE, 157 24, 158 32, 159 32, 160 40, 161 80, 162 8, 163 UINT_MAX 164}; 165 166const dns_master_style_t dns_master_style_cache = { 167 DNS_STYLEFLAG_OMIT_OWNER | DNS_STYLEFLAG_OMIT_CLASS | 168 DNS_STYLEFLAG_MULTILINE | DNS_STYLEFLAG_RRCOMMENT | 169 DNS_STYLEFLAG_TRUST | DNS_STYLEFLAG_NCACHE, 170 24, 171 32, 172 32, 173 40, 174 80, 175 8, 176 UINT_MAX 177}; 178 179const dns_master_style_t dns_master_style_cache_with_expired = { 180 DNS_STYLEFLAG_OMIT_OWNER | DNS_STYLEFLAG_OMIT_CLASS | 181 DNS_STYLEFLAG_MULTILINE | DNS_STYLEFLAG_RRCOMMENT | 182 DNS_STYLEFLAG_TRUST | DNS_STYLEFLAG_NCACHE | 183 DNS_STYLEFLAG_EXPIRED, 184 24, 185 32, 186 32, 187 40, 188 80, 189 8, 190 UINT_MAX 191}; 192 193const dns_master_style_t dns_master_style_simple = { 0, 24, 32, 32, 194 40, 80, 8, UINT_MAX }; 195 196/*% 197 * A style suitable for dns_rdataset_totext(). 198 */ 199const dns_master_style_t dns_master_style_debug = { 200 DNS_STYLEFLAG_REL_OWNER, 24, 32, 40, 48, 80, 8, UINT_MAX 201}; 202 203/*% 204 * Similar, but indented (i.e., prepended with indentctx.string). 205 */ 206const dns_master_style_t dns_master_style_indent = { 207 DNS_STYLEFLAG_REL_OWNER | DNS_STYLEFLAG_INDENT, 208 24, 209 32, 210 40, 211 48, 212 80, 213 8, 214 UINT_MAX 215}; 216 217/*% 218 * Similar, but with each line commented out. 219 */ 220const dns_master_style_t dns_master_style_comment = { 221 DNS_STYLEFLAG_REL_OWNER | DNS_STYLEFLAG_MULTILINE | 222 DNS_STYLEFLAG_RRCOMMENT | DNS_STYLEFLAG_COMMENTDATA, 223 24, 224 32, 225 40, 226 48, 227 80, 228 8, 229 UINT_MAX 230}; 231 232/*% 233 * YAML style 234 */ 235const dns_master_style_t dns_master_style_yaml = { 236 DNS_STYLEFLAG_YAML | DNS_STYLEFLAG_REL_OWNER | DNS_STYLEFLAG_INDENT, 237 24, 238 32, 239 40, 240 48, 241 80, 242 8, 243 UINT_MAX 244}; 245 246#define N_SPACES 10 247static char spaces[N_SPACES + 1] = " "; 248 249#define N_TABS 10 250static char tabs[N_TABS + 1] = "\t\t\t\t\t\t\t\t\t\t"; 251 252struct dns_dumpctx { 253 unsigned int magic; 254 isc_mem_t *mctx; 255 isc_mutex_t lock; 256 isc_refcount_t references; 257 atomic_bool canceled; 258 bool do_date; 259 isc_stdtime_t now; 260 FILE *f; 261 dns_db_t *db; 262 dns_dbversion_t *version; 263 dns_dbiterator_t *dbiter; 264 dns_totext_ctx_t tctx; 265 isc_task_t *task; 266 dns_dumpdonefunc_t done; 267 void *done_arg; 268 /* dns_master_dumpasync() */ 269 isc_result_t result; 270 char *file; 271 char *tmpfile; 272 dns_masterformat_t format; 273 dns_masterrawheader_t header; 274 isc_result_t (*dumpsets)(isc_mem_t *mctx, const dns_name_t *name, 275 dns_rdatasetiter_t *rdsiter, 276 dns_totext_ctx_t *ctx, isc_buffer_t *buffer, 277 FILE *f); 278}; 279 280#define NXDOMAIN(x) (((x)->attributes & DNS_RDATASETATTR_NXDOMAIN) != 0) 281 282static const dns_indent_t default_indent = { "\t", 1 }; 283static const dns_indent_t default_yamlindent = { " ", 1 }; 284 285/*% 286 * Output tabs and spaces to go from column '*current' to 287 * column 'to', and update '*current' to reflect the new 288 * current column. 289 */ 290static isc_result_t 291indent(unsigned int *current, unsigned int to, int tabwidth, 292 isc_buffer_t *target) { 293 isc_region_t r; 294 unsigned char *p; 295 unsigned int from; 296 int ntabs, nspaces, t; 297 298 from = *current; 299 300 if (to < from + 1) { 301 to = from + 1; 302 } 303 304 ntabs = to / tabwidth - from / tabwidth; 305 if (ntabs < 0) { 306 ntabs = 0; 307 } 308 309 if (ntabs > 0) { 310 isc_buffer_availableregion(target, &r); 311 if (r.length < (unsigned)ntabs) { 312 return (ISC_R_NOSPACE); 313 } 314 p = r.base; 315 316 t = ntabs; 317 while (t) { 318 int n = t; 319 if (n > N_TABS) { 320 n = N_TABS; 321 } 322 memmove(p, tabs, n); 323 p += n; 324 t -= n; 325 } 326 isc_buffer_add(target, ntabs); 327 from = (to / tabwidth) * tabwidth; 328 } 329 330 nspaces = to - from; 331 INSIST(nspaces >= 0); 332 333 isc_buffer_availableregion(target, &r); 334 if (r.length < (unsigned)nspaces) { 335 return (ISC_R_NOSPACE); 336 } 337 p = r.base; 338 339 t = nspaces; 340 while (t) { 341 int n = t; 342 if (n > N_SPACES) { 343 n = N_SPACES; 344 } 345 memmove(p, spaces, n); 346 p += n; 347 t -= n; 348 } 349 isc_buffer_add(target, nspaces); 350 351 *current = to; 352 return (ISC_R_SUCCESS); 353} 354 355static isc_result_t 356totext_ctx_init(const dns_master_style_t *style, const dns_indent_t *indentctx, 357 dns_totext_ctx_t *ctx) { 358 isc_result_t result; 359 360 REQUIRE(style->tab_width != 0); 361 362 if (indentctx == NULL) { 363 if ((style->flags & DNS_STYLEFLAG_YAML) != 0) { 364 indentctx = &default_yamlindent; 365 } else { 366 indentctx = &default_indent; 367 } 368 } 369 370 ctx->style = *style; 371 ctx->class_printed = false; 372 373 dns_fixedname_init(&ctx->origin_fixname); 374 375 /* 376 * Set up the line break string if needed. 377 */ 378 if ((ctx->style.flags & DNS_STYLEFLAG_MULTILINE) != 0) { 379 isc_buffer_t buf; 380 isc_region_t r; 381 unsigned int col = 0; 382 383 isc_buffer_init(&buf, ctx->linebreak_buf, 384 sizeof(ctx->linebreak_buf)); 385 386 isc_buffer_availableregion(&buf, &r); 387 if (r.length < 1) { 388 return (DNS_R_TEXTTOOLONG); 389 } 390 r.base[0] = '\n'; 391 isc_buffer_add(&buf, 1); 392 393 if ((ctx->style.flags & DNS_STYLEFLAG_INDENT) != 0 || 394 (ctx->style.flags & DNS_STYLEFLAG_YAML) != 0) 395 { 396 unsigned int i, len = strlen(indentctx->string); 397 for (i = 0; i < indentctx->count; i++) { 398 if (isc_buffer_availablelength(&buf) < len) { 399 return (DNS_R_TEXTTOOLONG); 400 } 401 isc_buffer_putstr(&buf, indentctx->string); 402 } 403 } 404 405 if ((ctx->style.flags & DNS_STYLEFLAG_COMMENTDATA) != 0) { 406 isc_buffer_availableregion(&buf, &r); 407 if (r.length < 1) { 408 return (DNS_R_TEXTTOOLONG); 409 } 410 r.base[0] = ';'; 411 isc_buffer_add(&buf, 1); 412 } 413 414 result = indent(&col, ctx->style.rdata_column, 415 ctx->style.tab_width, &buf); 416 /* 417 * Do not return ISC_R_NOSPACE if the line break string 418 * buffer is too small, because that would just make 419 * dump_rdataset() retry indefinitely with ever 420 * bigger target buffers. That's a different buffer, 421 * so it won't help. Use DNS_R_TEXTTOOLONG as a substitute. 422 */ 423 if (result == ISC_R_NOSPACE) { 424 return (DNS_R_TEXTTOOLONG); 425 } 426 if (result != ISC_R_SUCCESS) { 427 return (result); 428 } 429 430 isc_buffer_availableregion(&buf, &r); 431 if (r.length < 1) { 432 return (DNS_R_TEXTTOOLONG); 433 } 434 r.base[0] = '\0'; 435 isc_buffer_add(&buf, 1); 436 ctx->linebreak = ctx->linebreak_buf; 437 } else { 438 ctx->linebreak = NULL; 439 } 440 441 ctx->origin = NULL; 442 ctx->neworigin = NULL; 443 ctx->current_ttl = 0; 444 ctx->current_ttl_valid = false; 445 ctx->serve_stale_ttl = 0; 446 ctx->indent = *indentctx; 447 448 return (ISC_R_SUCCESS); 449} 450 451#define INDENT_TO(col) \ 452 do { \ 453 if ((ctx->style.flags & DNS_STYLEFLAG_YAML) != 0) { \ 454 if ((result = str_totext(" ", target)) != \ 455 ISC_R_SUCCESS) \ 456 return ((result)); \ 457 } else if ((result = indent(&column, ctx->style.col, \ 458 ctx->style.tab_width, target)) != \ 459 ISC_R_SUCCESS) \ 460 return ((result)); \ 461 } while (0) 462 463static isc_result_t 464str_totext(const char *source, isc_buffer_t *target) { 465 unsigned int l; 466 isc_region_t region; 467 468 isc_buffer_availableregion(target, ®ion); 469 l = strlen(source); 470 471 if (l > region.length) { 472 return (ISC_R_NOSPACE); 473 } 474 475 memmove(region.base, source, l); 476 isc_buffer_add(target, l); 477 return (ISC_R_SUCCESS); 478} 479 480static isc_result_t 481ncache_summary(dns_rdataset_t *rdataset, bool omit_final_dot, 482 dns_totext_ctx_t *ctx, isc_buffer_t *target) { 483 isc_result_t result = ISC_R_SUCCESS; 484 dns_rdataset_t rds; 485 dns_name_t name; 486 487 dns_rdataset_init(&rds); 488 dns_name_init(&name, NULL); 489 490 do { 491 dns_ncache_current(rdataset, &name, &rds); 492 for (result = dns_rdataset_first(&rds); result == ISC_R_SUCCESS; 493 result = dns_rdataset_next(&rds)) 494 { 495 if ((ctx->style.flags & DNS_STYLEFLAG_INDENT) != 0 || 496 (ctx->style.flags & DNS_STYLEFLAG_YAML) != 0) 497 { 498 unsigned int i; 499 for (i = 0; i < ctx->indent.count; i++) { 500 CHECK(str_totext(ctx->indent.string, 501 target)); 502 } 503 } 504 505 if ((ctx->style.flags & DNS_STYLEFLAG_YAML) != 0) { 506 CHECK(str_totext("- ", target)); 507 } else { 508 CHECK(str_totext("; ", target)); 509 } 510 511 CHECK(dns_name_totext(&name, omit_final_dot, target)); 512 CHECK(str_totext(" ", target)); 513 CHECK(dns_rdatatype_totext(rds.type, target)); 514 if (rds.type == dns_rdatatype_rrsig) { 515 CHECK(str_totext(" ", target)); 516 CHECK(dns_rdatatype_totext(rds.covers, target)); 517 CHECK(str_totext(" ...\n", target)); 518 } else { 519 dns_rdata_t rdata = DNS_RDATA_INIT; 520 dns_rdataset_current(&rds, &rdata); 521 CHECK(str_totext(" ", target)); 522 CHECK(dns_rdata_tofmttext(&rdata, dns_rootname, 523 0, 0, 0, " ", 524 target)); 525 CHECK(str_totext("\n", target)); 526 } 527 } 528 dns_rdataset_disassociate(&rds); 529 result = dns_rdataset_next(rdataset); 530 } while (result == ISC_R_SUCCESS); 531 532 if (result == ISC_R_NOMORE) { 533 result = ISC_R_SUCCESS; 534 } 535cleanup: 536 if (dns_rdataset_isassociated(&rds)) { 537 dns_rdataset_disassociate(&rds); 538 } 539 540 return (result); 541} 542 543/* 544 * Convert 'rdataset' to master file text format according to 'ctx', 545 * storing the result in 'target'. If 'owner_name' is NULL, it 546 * is omitted; otherwise 'owner_name' must be valid and have at least 547 * one label. 548 */ 549 550static isc_result_t 551rdataset_totext(dns_rdataset_t *rdataset, const dns_name_t *owner_name, 552 dns_totext_ctx_t *ctx, bool omit_final_dot, 553 isc_buffer_t *target) { 554 isc_result_t result; 555 unsigned int column; 556 bool first = true; 557 uint32_t current_ttl; 558 bool current_ttl_valid; 559 dns_rdatatype_t type; 560 unsigned int type_start; 561 dns_fixedname_t fixed; 562 dns_name_t *name = NULL; 563 unsigned int i; 564 565 REQUIRE(DNS_RDATASET_VALID(rdataset)); 566 567 rdataset->attributes |= DNS_RDATASETATTR_LOADORDER; 568 result = dns_rdataset_first(rdataset); 569 570 current_ttl = ctx->current_ttl; 571 current_ttl_valid = ctx->current_ttl_valid; 572 573 if (owner_name != NULL) { 574 name = dns_fixedname_initname(&fixed); 575 dns_name_copy(owner_name, name); 576 dns_rdataset_getownercase(rdataset, name); 577 } 578 579 while (result == ISC_R_SUCCESS) { 580 column = 0; 581 582 /* 583 * Indent? 584 */ 585 if ((ctx->style.flags & DNS_STYLEFLAG_INDENT) != 0 || 586 (ctx->style.flags & DNS_STYLEFLAG_YAML) != 0) 587 { 588 for (i = 0; i < ctx->indent.count; i++) { 589 RETERR(str_totext(ctx->indent.string, target)); 590 } 591 } 592 593 /* 594 * YAML or comment prefix? 595 */ 596 if ((ctx->style.flags & DNS_STYLEFLAG_YAML) != 0) { 597 RETERR(str_totext("- ", target)); 598 } else if ((ctx->style.flags & DNS_STYLEFLAG_COMMENTDATA) != 0) 599 { 600 RETERR(str_totext(";", target)); 601 } 602 603 /* 604 * Owner name. 605 */ 606 if (name != NULL && 607 !((ctx->style.flags & DNS_STYLEFLAG_OMIT_OWNER) != 0 && 608 !first)) 609 { 610 unsigned int name_start = target->used; 611 RETERR(dns_name_totext(name, omit_final_dot, target)); 612 column += target->used - name_start; 613 } 614 615 /* 616 * TTL. 617 */ 618 if ((ctx->style.flags & DNS_STYLEFLAG_NO_TTL) == 0 && 619 !((ctx->style.flags & DNS_STYLEFLAG_OMIT_TTL) != 0 && 620 current_ttl_valid && rdataset->ttl == current_ttl)) 621 { 622 char ttlbuf[64]; 623 isc_region_t r; 624 unsigned int length; 625 626 INDENT_TO(ttl_column); 627 if ((ctx->style.flags & DNS_STYLEFLAG_TTL_UNITS) != 0) { 628 length = target->used; 629 result = dns_ttl_totext(rdataset->ttl, false, 630 false, target); 631 if (result != ISC_R_SUCCESS) { 632 return (result); 633 } 634 column += target->used - length; 635 } else { 636 length = snprintf(ttlbuf, sizeof(ttlbuf), "%u", 637 rdataset->ttl); 638 INSIST(length <= sizeof(ttlbuf)); 639 isc_buffer_availableregion(target, &r); 640 if (r.length < length) { 641 return (ISC_R_NOSPACE); 642 } 643 memmove(r.base, ttlbuf, length); 644 isc_buffer_add(target, length); 645 column += length; 646 } 647 648 /* 649 * If the $TTL directive is not in use, the TTL we 650 * just printed becomes the default for subsequent RRs. 651 */ 652 if ((ctx->style.flags & DNS_STYLEFLAG_TTL) == 0) { 653 current_ttl = rdataset->ttl; 654 current_ttl_valid = true; 655 } 656 } 657 658 /* 659 * Class. 660 */ 661 if ((ctx->style.flags & DNS_STYLEFLAG_NO_CLASS) == 0 && 662 ((ctx->style.flags & DNS_STYLEFLAG_OMIT_CLASS) == 0 || 663 !ctx->class_printed)) 664 { 665 unsigned int class_start; 666 INDENT_TO(class_column); 667 class_start = target->used; 668 if ((ctx->style.flags & DNS_STYLEFLAG_UNKNOWNFORMAT) != 669 0) 670 { 671 result = dns_rdataclass_tounknowntext( 672 rdataset->rdclass, target); 673 } else { 674 result = dns_rdataclass_totext( 675 rdataset->rdclass, target); 676 } 677 if (result != ISC_R_SUCCESS) { 678 return (result); 679 } 680 column += (target->used - class_start); 681 } 682 683 /* 684 * Type. 685 */ 686 687 if ((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0) { 688 type = rdataset->covers; 689 } else { 690 type = rdataset->type; 691 } 692 693 INDENT_TO(type_column); 694 type_start = target->used; 695 if ((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0) { 696 RETERR(str_totext("\\-", target)); 697 } 698 switch (type) { 699 case dns_rdatatype_keydata: 700#define KEYDATA "KEYDATA" 701 if ((ctx->style.flags & DNS_STYLEFLAG_KEYDATA) != 0) { 702 if (isc_buffer_availablelength(target) < 703 (sizeof(KEYDATA) - 1)) 704 { 705 return (ISC_R_NOSPACE); 706 } 707 isc_buffer_putstr(target, KEYDATA); 708 break; 709 } 710 FALLTHROUGH; 711 default: 712 if ((ctx->style.flags & DNS_STYLEFLAG_UNKNOWNFORMAT) != 713 0) 714 { 715 result = dns_rdatatype_tounknowntext(type, 716 target); 717 } else { 718 result = dns_rdatatype_totext(type, target); 719 } 720 if (result != ISC_R_SUCCESS) { 721 return (result); 722 } 723 } 724 column += (target->used - type_start); 725 726 /* 727 * Rdata. 728 */ 729 INDENT_TO(rdata_column); 730 if ((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0) { 731 if (NXDOMAIN(rdataset)) { 732 RETERR(str_totext(";-$NXDOMAIN\n", target)); 733 } else { 734 RETERR(str_totext(";-$NXRRSET\n", target)); 735 } 736 /* 737 * Print a summary of the cached records which make 738 * up the negative response. 739 */ 740 RETERR(ncache_summary(rdataset, omit_final_dot, ctx, 741 target)); 742 break; 743 } else { 744 dns_rdata_t rdata = DNS_RDATA_INIT; 745 isc_region_t r; 746 747 dns_rdataset_current(rdataset, &rdata); 748 749 RETERR(dns_rdata_tofmttext( 750 &rdata, ctx->origin, ctx->style.flags, 751 ctx->style.line_length - 752 ctx->style.rdata_column, 753 ctx->style.split_width, ctx->linebreak, 754 target)); 755 756 isc_buffer_availableregion(target, &r); 757 if (r.length < 1) { 758 return (ISC_R_NOSPACE); 759 } 760 r.base[0] = '\n'; 761 isc_buffer_add(target, 1); 762 } 763 764 first = false; 765 result = dns_rdataset_next(rdataset); 766 } 767 768 if (result != ISC_R_NOMORE) { 769 return (result); 770 } 771 772 /* 773 * Update the ctx state to reflect what we just printed. 774 * This is done last, only when we are sure we will return 775 * success, because this function may be called multiple 776 * times with increasing buffer sizes until it succeeds, 777 * and failed attempts must not update the state prematurely. 778 */ 779 ctx->class_printed = true; 780 ctx->current_ttl = current_ttl; 781 ctx->current_ttl_valid = current_ttl_valid; 782 783 return (ISC_R_SUCCESS); 784} 785 786/* 787 * Print the name, type, and class of an empty rdataset, 788 * such as those used to represent the question section 789 * of a DNS message. 790 */ 791static isc_result_t 792question_totext(dns_rdataset_t *rdataset, const dns_name_t *owner_name, 793 dns_totext_ctx_t *ctx, bool omit_final_dot, 794 isc_buffer_t *target) { 795 unsigned int column; 796 isc_result_t result; 797 isc_region_t r; 798 799 REQUIRE(DNS_RDATASET_VALID(rdataset)); 800 result = dns_rdataset_first(rdataset); 801 REQUIRE(result == ISC_R_NOMORE); 802 803 column = 0; 804 805 /* Owner name */ 806 { 807 unsigned int name_start = target->used; 808 RETERR(dns_name_totext(owner_name, omit_final_dot, target)); 809 column += target->used - name_start; 810 } 811 812 /* Class */ 813 { 814 unsigned int class_start; 815 INDENT_TO(class_column); 816 class_start = target->used; 817 if ((ctx->style.flags & DNS_STYLEFLAG_UNKNOWNFORMAT) != 0) { 818 result = dns_rdataclass_tounknowntext(rdataset->rdclass, 819 target); 820 } else { 821 result = dns_rdataclass_totext(rdataset->rdclass, 822 target); 823 } 824 if (result != ISC_R_SUCCESS) { 825 return (result); 826 } 827 column += (target->used - class_start); 828 } 829 830 /* Type */ 831 { 832 unsigned int type_start; 833 INDENT_TO(type_column); 834 type_start = target->used; 835 if ((ctx->style.flags & DNS_STYLEFLAG_UNKNOWNFORMAT) != 0) { 836 result = dns_rdatatype_tounknowntext(rdataset->type, 837 target); 838 } else { 839 result = dns_rdatatype_totext(rdataset->type, target); 840 } 841 if (result != ISC_R_SUCCESS) { 842 return (result); 843 } 844 column += (target->used - type_start); 845 } 846 847 isc_buffer_availableregion(target, &r); 848 if (r.length < 1) { 849 return (ISC_R_NOSPACE); 850 } 851 r.base[0] = '\n'; 852 isc_buffer_add(target, 1); 853 854 return (ISC_R_SUCCESS); 855} 856 857isc_result_t 858dns_rdataset_totext(dns_rdataset_t *rdataset, const dns_name_t *owner_name, 859 bool omit_final_dot, bool question, isc_buffer_t *target) { 860 dns_totext_ctx_t ctx; 861 isc_result_t result; 862 result = totext_ctx_init(&dns_master_style_debug, NULL, &ctx); 863 if (result != ISC_R_SUCCESS) { 864 UNEXPECTED_ERROR("could not set master file style"); 865 return (ISC_R_UNEXPECTED); 866 } 867 868 /* 869 * The caller might want to give us an empty owner 870 * name (e.g. if they are outputting into a master 871 * file and this rdataset has the same name as the 872 * previous one.) 873 */ 874 if (dns_name_countlabels(owner_name) == 0) { 875 owner_name = NULL; 876 } 877 878 if (question) { 879 return (question_totext(rdataset, owner_name, &ctx, 880 omit_final_dot, target)); 881 } else { 882 return (rdataset_totext(rdataset, owner_name, &ctx, 883 omit_final_dot, target)); 884 } 885} 886 887isc_result_t 888dns_master_rdatasettotext(const dns_name_t *owner_name, 889 dns_rdataset_t *rdataset, 890 const dns_master_style_t *style, dns_indent_t *indent, 891 isc_buffer_t *target) { 892 dns_totext_ctx_t ctx; 893 isc_result_t result; 894 result = totext_ctx_init(style, indent, &ctx); 895 if (result != ISC_R_SUCCESS) { 896 UNEXPECTED_ERROR("could not set master file style"); 897 return (ISC_R_UNEXPECTED); 898 } 899 900 return (rdataset_totext(rdataset, owner_name, &ctx, false, target)); 901} 902 903isc_result_t 904dns_master_questiontotext(const dns_name_t *owner_name, 905 dns_rdataset_t *rdataset, 906 const dns_master_style_t *style, 907 isc_buffer_t *target) { 908 dns_totext_ctx_t ctx; 909 isc_result_t result; 910 result = totext_ctx_init(style, NULL, &ctx); 911 if (result != ISC_R_SUCCESS) { 912 UNEXPECTED_ERROR("could not set master file style"); 913 return (ISC_R_UNEXPECTED); 914 } 915 916 return (question_totext(rdataset, owner_name, &ctx, false, target)); 917} 918 919/* 920 * Print an rdataset. 'buffer' is a scratch buffer, which must have been 921 * dynamically allocated by the caller. It must be large enough to 922 * hold the result from dns_ttl_totext(). If more than that is needed, 923 * the buffer will be grown automatically. 924 */ 925 926static isc_result_t 927dump_rdataset(isc_mem_t *mctx, const dns_name_t *name, dns_rdataset_t *rdataset, 928 dns_totext_ctx_t *ctx, isc_buffer_t *buffer, FILE *f) { 929 isc_region_t r; 930 isc_result_t result; 931 932 REQUIRE(buffer->length > 0); 933 934 /* 935 * Output a $TTL directive if needed. 936 */ 937 938 if ((ctx->style.flags & DNS_STYLEFLAG_TTL) != 0) { 939 if (!ctx->current_ttl_valid || 940 ctx->current_ttl != rdataset->ttl) 941 { 942 if ((ctx->style.flags & DNS_STYLEFLAG_COMMENT) != 0) { 943 isc_buffer_clear(buffer); 944 result = dns_ttl_totext(rdataset->ttl, true, 945 true, buffer); 946 INSIST(result == ISC_R_SUCCESS); 947 isc_buffer_usedregion(buffer, &r); 948 fprintf(f, "$TTL %u\t; %.*s\n", rdataset->ttl, 949 (int)r.length, (char *)r.base); 950 } else { 951 fprintf(f, "$TTL %u\n", rdataset->ttl); 952 } 953 ctx->current_ttl = rdataset->ttl; 954 ctx->current_ttl_valid = true; 955 } 956 } 957 958 isc_buffer_clear(buffer); 959 960 /* 961 * Generate the text representation of the rdataset into 962 * the buffer. If the buffer is too small, grow it. 963 */ 964 for (;;) { 965 int newlength; 966 void *newmem; 967 result = rdataset_totext(rdataset, name, ctx, false, buffer); 968 if (result != ISC_R_NOSPACE) { 969 break; 970 } 971 972 newlength = buffer->length * 2; 973 newmem = isc_mem_get(mctx, newlength); 974 isc_mem_put(mctx, buffer->base, buffer->length); 975 isc_buffer_init(buffer, newmem, newlength); 976 } 977 if (result != ISC_R_SUCCESS) { 978 return (result); 979 } 980 981 /* 982 * Write the buffer contents to the master file. 983 */ 984 isc_buffer_usedregion(buffer, &r); 985 result = isc_stdio_write(r.base, 1, (size_t)r.length, f, NULL); 986 987 if (result != ISC_R_SUCCESS) { 988 UNEXPECTED_ERROR("master file write failed: %s", 989 isc_result_totext(result)); 990 return (result); 991 } 992 993 return (ISC_R_SUCCESS); 994} 995 996/* 997 * Define the order in which rdatasets should be printed in zone 998 * files. We will print SOA and NS records before others, SIGs 999 * immediately following the things they sign, and order everything 1000 * else by RR number. This is all just for aesthetics and 1001 * compatibility with buggy software that expects the SOA to be first; 1002 * the DNS specifications allow any order. 1003 */ 1004 1005static int 1006dump_order(const dns_rdataset_t *rds) { 1007 int t; 1008 int sig; 1009 if (rds->type == dns_rdatatype_rrsig) { 1010 t = rds->covers; 1011 sig = 1; 1012 } else { 1013 t = rds->type; 1014 sig = 0; 1015 } 1016 switch (t) { 1017 case dns_rdatatype_soa: 1018 t = 0; 1019 break; 1020 case dns_rdatatype_ns: 1021 t = 1; 1022 break; 1023 default: 1024 t += 2; 1025 break; 1026 } 1027 return ((t << 1) + sig); 1028} 1029 1030static int 1031dump_order_compare(const void *a, const void *b) { 1032 return (dump_order(*((const dns_rdataset_t *const *)a)) - 1033 dump_order(*((const dns_rdataset_t *const *)b))); 1034} 1035 1036/* 1037 * Dump all the rdatasets of a domain name to a master file. We make 1038 * a "best effort" attempt to sort the RRsets in a nice order, but if 1039 * there are more than MAXSORT RRsets, we punt and only sort them in 1040 * groups of MAXSORT. This is not expected to ever happen in practice 1041 * since much less than 64 RR types have been registered with the 1042 * IANA, so far, and the output will be correct (though not 1043 * aesthetically pleasing) even if it does happen. 1044 */ 1045 1046#define MAXSORT 64 1047 1048static isc_result_t 1049dump_rdatasets_text(isc_mem_t *mctx, const dns_name_t *name, 1050 dns_rdatasetiter_t *rdsiter, dns_totext_ctx_t *ctx, 1051 isc_buffer_t *buffer, FILE *f) { 1052 isc_result_t itresult, dumpresult; 1053 isc_region_t r; 1054 dns_rdataset_t rdatasets[MAXSORT]; 1055 dns_rdataset_t *sorted[MAXSORT]; 1056 int i, n; 1057 1058 itresult = dns_rdatasetiter_first(rdsiter); 1059 dumpresult = ISC_R_SUCCESS; 1060 1061 if (itresult == ISC_R_SUCCESS && ctx->neworigin != NULL) { 1062 isc_buffer_clear(buffer); 1063 itresult = dns_name_totext(ctx->neworigin, false, buffer); 1064 RUNTIME_CHECK(itresult == ISC_R_SUCCESS); 1065 isc_buffer_usedregion(buffer, &r); 1066 fprintf(f, "$ORIGIN %.*s\n", (int)r.length, (char *)r.base); 1067 ctx->neworigin = NULL; 1068 } 1069 1070again: 1071 for (i = 0; itresult == ISC_R_SUCCESS && i < MAXSORT; 1072 itresult = dns_rdatasetiter_next(rdsiter), i++) 1073 { 1074 dns_rdataset_init(&rdatasets[i]); 1075 dns_rdatasetiter_current(rdsiter, &rdatasets[i]); 1076 sorted[i] = &rdatasets[i]; 1077 } 1078 n = i; 1079 INSIST(n <= MAXSORT); 1080 1081 qsort(sorted, n, sizeof(sorted[0]), dump_order_compare); 1082 1083 for (i = 0; i < n; i++) { 1084 dns_rdataset_t *rds = sorted[i]; 1085 1086 if (ANCIENT(rds) && 1087 (ctx->style.flags & DNS_STYLEFLAG_EXPIRED) == 0) 1088 { 1089 /* Omit expired entries */ 1090 dns_rdataset_disassociate(rds); 1091 continue; 1092 } 1093 1094 if ((ctx->style.flags & DNS_STYLEFLAG_TRUST) != 0) { 1095 if ((ctx->style.flags & DNS_STYLEFLAG_INDENT) != 0 || 1096 (ctx->style.flags & DNS_STYLEFLAG_YAML) != 0) 1097 { 1098 unsigned int j; 1099 for (j = 0; j < ctx->indent.count; j++) { 1100 fprintf(f, "%s", ctx->indent.string); 1101 } 1102 } 1103 fprintf(f, "; %s\n", dns_trust_totext(rds->trust)); 1104 } 1105 if (((rds->attributes & DNS_RDATASETATTR_NEGATIVE) != 0) && 1106 (ctx->style.flags & DNS_STYLEFLAG_NCACHE) == 0) 1107 { 1108 /* Omit negative cache entries */ 1109 } else { 1110 isc_result_t result; 1111 if (STALE(rds)) { 1112 fprintf(f, "; stale\n"); 1113 } else if (ANCIENT(rds)) { 1114 isc_buffer_t b; 1115 char buf[sizeof("YYYYMMDDHHMMSS")]; 1116 memset(buf, 0, sizeof(buf)); 1117 isc_buffer_init(&b, buf, sizeof(buf) - 1); 1118 dns_time64_totext((uint64_t)rds->ttl, &b); 1119 fprintf(f, 1120 "; expired since %s " 1121 "(awaiting cleanup)\n", 1122 buf); 1123 } 1124 result = dump_rdataset(mctx, name, rds, ctx, buffer, f); 1125 if (result != ISC_R_SUCCESS) { 1126 dumpresult = result; 1127 } 1128 if ((ctx->style.flags & DNS_STYLEFLAG_OMIT_OWNER) != 0) 1129 { 1130 name = NULL; 1131 } 1132 } 1133 if (((ctx->style.flags & DNS_STYLEFLAG_RESIGN) != 0) && 1134 ((rds->attributes & DNS_RDATASETATTR_RESIGN) != 0)) 1135 { 1136 isc_buffer_t b; 1137 char buf[sizeof("YYYYMMDDHHMMSS")]; 1138 memset(buf, 0, sizeof(buf)); 1139 isc_buffer_init(&b, buf, sizeof(buf) - 1); 1140 dns_time64_totext((uint64_t)rds->resign, &b); 1141 if ((ctx->style.flags & DNS_STYLEFLAG_INDENT) != 0 || 1142 (ctx->style.flags & DNS_STYLEFLAG_YAML) != 0) 1143 { 1144 unsigned int j; 1145 for (j = 0; j < ctx->indent.count; j++) { 1146 fprintf(f, "%s", ctx->indent.string); 1147 } 1148 } 1149 fprintf(f, "; resign=%s\n", buf); 1150 } 1151 dns_rdataset_disassociate(rds); 1152 } 1153 1154 if (dumpresult != ISC_R_SUCCESS) { 1155 return (dumpresult); 1156 } 1157 1158 /* 1159 * If we got more data than could be sorted at once, 1160 * go handle the rest. 1161 */ 1162 if (itresult == ISC_R_SUCCESS) { 1163 goto again; 1164 } 1165 1166 if (itresult == ISC_R_NOMORE) { 1167 itresult = ISC_R_SUCCESS; 1168 } 1169 1170 return (itresult); 1171} 1172 1173/* 1174 * Dump given RRsets in the "raw" format. 1175 */ 1176static isc_result_t 1177dump_rdataset_raw(isc_mem_t *mctx, const dns_name_t *name, 1178 dns_rdataset_t *rdataset, isc_buffer_t *buffer, FILE *f) { 1179 isc_result_t result; 1180 uint32_t totallen; 1181 uint16_t dlen; 1182 isc_region_t r, r_hdr; 1183 1184 REQUIRE(buffer->length > 0); 1185 REQUIRE(DNS_RDATASET_VALID(rdataset)); 1186 1187 rdataset->attributes |= DNS_RDATASETATTR_LOADORDER; 1188restart: 1189 totallen = 0; 1190 result = dns_rdataset_first(rdataset); 1191 REQUIRE(result == ISC_R_SUCCESS); 1192 1193 isc_buffer_clear(buffer); 1194 1195 /* 1196 * Common header and owner name (length followed by name) 1197 * These fields should be in a moderate length, so we assume we 1198 * can store all of them in the initial buffer. 1199 */ 1200 isc_buffer_availableregion(buffer, &r_hdr); 1201 INSIST(r_hdr.length >= sizeof(dns_masterrawrdataset_t)); 1202 isc_buffer_putuint32(buffer, totallen); /* XXX: leave space */ 1203 isc_buffer_putuint16(buffer, rdataset->rdclass); /* 16-bit class */ 1204 isc_buffer_putuint16(buffer, rdataset->type); /* 16-bit type */ 1205 isc_buffer_putuint16(buffer, rdataset->covers); /* same as type */ 1206 isc_buffer_putuint32(buffer, rdataset->ttl); /* 32-bit TTL */ 1207 isc_buffer_putuint32(buffer, dns_rdataset_count(rdataset)); 1208 totallen = isc_buffer_usedlength(buffer); 1209 INSIST(totallen <= sizeof(dns_masterrawrdataset_t)); 1210 1211 dns_name_toregion(name, &r); 1212 INSIST(isc_buffer_availablelength(buffer) >= (sizeof(dlen) + r.length)); 1213 dlen = (uint16_t)r.length; 1214 isc_buffer_putuint16(buffer, dlen); 1215 isc_buffer_copyregion(buffer, &r); 1216 totallen += sizeof(dlen) + r.length; 1217 1218 do { 1219 dns_rdata_t rdata = DNS_RDATA_INIT; 1220 1221 dns_rdataset_current(rdataset, &rdata); 1222 dns_rdata_toregion(&rdata, &r); 1223 INSIST(r.length <= 0xffffU); 1224 dlen = (uint16_t)r.length; 1225 1226 /* 1227 * Copy the rdata into the buffer. If the buffer is too small, 1228 * grow it. This should be rare, so we'll simply restart the 1229 * entire procedure (or should we copy the old data and 1230 * continue?). 1231 */ 1232 if (isc_buffer_availablelength(buffer) < 1233 sizeof(dlen) + r.length) 1234 { 1235 int newlength; 1236 void *newmem; 1237 1238 newlength = buffer->length * 2; 1239 newmem = isc_mem_get(mctx, newlength); 1240 isc_mem_put(mctx, buffer->base, buffer->length); 1241 isc_buffer_init(buffer, newmem, newlength); 1242 goto restart; 1243 } 1244 isc_buffer_putuint16(buffer, dlen); 1245 isc_buffer_copyregion(buffer, &r); 1246 totallen += sizeof(dlen) + r.length; 1247 1248 result = dns_rdataset_next(rdataset); 1249 } while (result == ISC_R_SUCCESS); 1250 1251 if (result != ISC_R_NOMORE) { 1252 return (result); 1253 } 1254 1255 /* 1256 * Fill in the total length field. 1257 * XXX: this is a bit tricky. Since we have already "used" the space 1258 * for the total length in the buffer, we first remember the entire 1259 * buffer length in the region, "rewind", and then write the value. 1260 */ 1261 isc_buffer_usedregion(buffer, &r); 1262 isc_buffer_clear(buffer); 1263 isc_buffer_putuint32(buffer, totallen); 1264 INSIST(isc_buffer_usedlength(buffer) < totallen); 1265 1266 /* 1267 * Write the buffer contents to the raw master file. 1268 */ 1269 result = isc_stdio_write(r.base, 1, (size_t)r.length, f, NULL); 1270 1271 if (result != ISC_R_SUCCESS) { 1272 UNEXPECTED_ERROR("raw master file write failed: %s", 1273 isc_result_totext(result)); 1274 return (result); 1275 } 1276 1277 return (result); 1278} 1279 1280static isc_result_t 1281dump_rdatasets_raw(isc_mem_t *mctx, const dns_name_t *owner_name, 1282 dns_rdatasetiter_t *rdsiter, dns_totext_ctx_t *ctx, 1283 isc_buffer_t *buffer, FILE *f) { 1284 isc_result_t result; 1285 dns_rdataset_t rdataset; 1286 dns_fixedname_t fixed; 1287 dns_name_t *name; 1288 1289 name = dns_fixedname_initname(&fixed); 1290 dns_name_copy(owner_name, name); 1291 for (result = dns_rdatasetiter_first(rdsiter); result == ISC_R_SUCCESS; 1292 result = dns_rdatasetiter_next(rdsiter)) 1293 { 1294 dns_rdataset_init(&rdataset); 1295 dns_rdatasetiter_current(rdsiter, &rdataset); 1296 1297 dns_rdataset_getownercase(&rdataset, name); 1298 1299 if (((rdataset.attributes & DNS_RDATASETATTR_NEGATIVE) != 0) && 1300 (ctx->style.flags & DNS_STYLEFLAG_NCACHE) == 0) 1301 { 1302 /* Omit negative cache entries */ 1303 } else { 1304 result = dump_rdataset_raw(mctx, name, &rdataset, 1305 buffer, f); 1306 } 1307 dns_rdataset_disassociate(&rdataset); 1308 if (result != ISC_R_SUCCESS) { 1309 return (result); 1310 } 1311 } 1312 1313 if (result == ISC_R_NOMORE) { 1314 result = ISC_R_SUCCESS; 1315 } 1316 1317 return (result); 1318} 1319 1320/* 1321 * Initial size of text conversion buffer. The buffer is used 1322 * for several purposes: converting origin names, rdatasets, 1323 * $DATE timestamps, and comment strings for $TTL directives. 1324 * 1325 * When converting rdatasets, it is dynamically resized, but 1326 * when converting origins, timestamps, etc it is not. Therefore, 1327 * the initial size must large enough to hold the longest possible 1328 * text representation of any domain name (for $ORIGIN). 1329 */ 1330static const int initial_buffer_length = 1200; 1331 1332static isc_result_t 1333dumptostream(dns_dumpctx_t *dctx); 1334 1335static void 1336dumpctx_destroy(dns_dumpctx_t *dctx) { 1337 dctx->magic = 0; 1338 isc_mutex_destroy(&dctx->lock); 1339 dns_dbiterator_destroy(&dctx->dbiter); 1340 if (dctx->version != NULL) { 1341 dns_db_closeversion(dctx->db, &dctx->version, false); 1342 } 1343 dns_db_detach(&dctx->db); 1344 if (dctx->task != NULL) { 1345 isc_task_detach(&dctx->task); 1346 } 1347 if (dctx->file != NULL) { 1348 isc_mem_free(dctx->mctx, dctx->file); 1349 } 1350 if (dctx->tmpfile != NULL) { 1351 isc_mem_free(dctx->mctx, dctx->tmpfile); 1352 } 1353 isc_mem_putanddetach(&dctx->mctx, dctx, sizeof(*dctx)); 1354} 1355 1356void 1357dns_dumpctx_attach(dns_dumpctx_t *source, dns_dumpctx_t **target) { 1358 REQUIRE(DNS_DCTX_VALID(source)); 1359 REQUIRE(target != NULL && *target == NULL); 1360 1361 isc_refcount_increment(&source->references); 1362 1363 *target = source; 1364} 1365 1366void 1367dns_dumpctx_detach(dns_dumpctx_t **dctxp) { 1368 dns_dumpctx_t *dctx; 1369 1370 REQUIRE(dctxp != NULL); 1371 dctx = *dctxp; 1372 *dctxp = NULL; 1373 REQUIRE(DNS_DCTX_VALID(dctx)); 1374 1375 if (isc_refcount_decrement(&dctx->references) == 1) { 1376 dumpctx_destroy(dctx); 1377 } 1378} 1379 1380dns_dbversion_t * 1381dns_dumpctx_version(dns_dumpctx_t *dctx) { 1382 REQUIRE(DNS_DCTX_VALID(dctx)); 1383 return (dctx->version); 1384} 1385 1386dns_db_t * 1387dns_dumpctx_db(dns_dumpctx_t *dctx) { 1388 REQUIRE(DNS_DCTX_VALID(dctx)); 1389 return (dctx->db); 1390} 1391 1392void 1393dns_dumpctx_cancel(dns_dumpctx_t *dctx) { 1394 REQUIRE(DNS_DCTX_VALID(dctx)); 1395 1396 atomic_store_release(&dctx->canceled, true); 1397} 1398 1399static isc_result_t 1400flushandsync(FILE *f, isc_result_t result, const char *temp) { 1401 bool logit = (result == ISC_R_SUCCESS); 1402 1403 if (result == ISC_R_SUCCESS) { 1404 result = isc_stdio_flush(f); 1405 } 1406 if (result != ISC_R_SUCCESS && logit) { 1407 if (temp != NULL) { 1408 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL, 1409 DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR, 1410 "dumping to master file: %s: flush: %s", 1411 temp, isc_result_totext(result)); 1412 } else { 1413 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL, 1414 DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR, 1415 "dumping to stream: flush: %s", 1416 isc_result_totext(result)); 1417 } 1418 logit = false; 1419 } 1420 1421 if (result == ISC_R_SUCCESS) { 1422 result = isc_stdio_sync(f); 1423 } 1424 if (result != ISC_R_SUCCESS && logit) { 1425 if (temp != NULL) { 1426 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL, 1427 DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR, 1428 "dumping to master file: %s: fsync: %s", 1429 temp, isc_result_totext(result)); 1430 } else { 1431 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL, 1432 DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR, 1433 "dumping to stream: fsync: %s", 1434 isc_result_totext(result)); 1435 } 1436 } 1437 return (result); 1438} 1439 1440static isc_result_t 1441closeandrename(FILE *f, isc_result_t result, const char *temp, 1442 const char *file) { 1443 isc_result_t tresult; 1444 bool logit = (result == ISC_R_SUCCESS); 1445 1446 result = flushandsync(f, result, temp); 1447 if (result != ISC_R_SUCCESS) { 1448 logit = false; 1449 } 1450 1451 tresult = isc_stdio_close(f); 1452 if (result == ISC_R_SUCCESS) { 1453 result = tresult; 1454 } 1455 if (result != ISC_R_SUCCESS && logit) { 1456 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL, 1457 DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR, 1458 "dumping master file: %s: fclose: %s", temp, 1459 isc_result_totext(result)); 1460 logit = false; 1461 } 1462 if (result == ISC_R_SUCCESS) { 1463 result = isc_file_rename(temp, file); 1464 } else { 1465 (void)isc_file_remove(temp); 1466 } 1467 if (result != ISC_R_SUCCESS && logit) { 1468 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL, 1469 DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR, 1470 "dumping master file: rename: %s: %s", file, 1471 isc_result_totext(result)); 1472 } 1473 return (result); 1474} 1475 1476/* 1477 * This will run in a libuv threadpool thread. 1478 */ 1479static void 1480master_dump_cb(void *data) { 1481 isc_result_t result = ISC_R_UNSET; 1482 dns_dumpctx_t *dctx = data; 1483 REQUIRE(DNS_DCTX_VALID(dctx)); 1484 1485 if (atomic_load_acquire(&dctx->canceled)) { 1486 result = ISC_R_CANCELED; 1487 } else { 1488 result = dumptostream(dctx); 1489 } 1490 1491 if (dctx->file != NULL) { 1492 isc_result_t tresult = ISC_R_UNSET; 1493 tresult = closeandrename(dctx->f, result, dctx->tmpfile, 1494 dctx->file); 1495 if (tresult != ISC_R_SUCCESS && result == ISC_R_SUCCESS) { 1496 result = tresult; 1497 } 1498 } else { 1499 result = flushandsync(dctx->f, result, NULL); 1500 } 1501 1502 dctx->result = result; 1503} 1504 1505/* 1506 * This will run in a network/task manager thread when the dump is complete. 1507 */ 1508static void 1509master_dump_done_cb(void *data, isc_result_t result) { 1510 dns_dumpctx_t *dctx = data; 1511 1512 if (result == ISC_R_SUCCESS && dctx->result != ISC_R_SUCCESS) { 1513 result = dctx->result; 1514 } 1515 1516 (dctx->done)(dctx->done_arg, result); 1517 dns_dumpctx_detach(&dctx); 1518} 1519 1520/* 1521 * This must be run from a network/task manager thread. 1522 */ 1523static void 1524setup_dump(isc_task_t *task, isc_event_t *event) { 1525 dns_dumpctx_t *dctx = NULL; 1526 1527 REQUIRE(isc_nm_tid() >= 0); 1528 REQUIRE(event != NULL); 1529 1530 dctx = event->ev_arg; 1531 1532 REQUIRE(DNS_DCTX_VALID(dctx)); 1533 1534 isc_nm_work_offload(isc_task_getnetmgr(task), master_dump_cb, 1535 master_dump_done_cb, dctx); 1536 1537 isc_event_free(&event); 1538} 1539 1540static isc_result_t 1541task_send(dns_dumpctx_t *dctx) { 1542 isc_event_t *event; 1543 1544 event = isc_event_allocate(dctx->mctx, NULL, DNS_EVENT_DUMPQUANTUM, 1545 setup_dump, dctx, sizeof(*event)); 1546 isc_task_send(dctx->task, &event); 1547 return (ISC_R_SUCCESS); 1548} 1549 1550static isc_result_t 1551dumpctx_create(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version, 1552 const dns_master_style_t *style, FILE *f, dns_dumpctx_t **dctxp, 1553 dns_masterformat_t format, dns_masterrawheader_t *header) { 1554 dns_dumpctx_t *dctx; 1555 isc_result_t result; 1556 unsigned int options; 1557 1558 dctx = isc_mem_get(mctx, sizeof(*dctx)); 1559 1560 dctx->mctx = NULL; 1561 dctx->f = f; 1562 dctx->dbiter = NULL; 1563 dctx->db = NULL; 1564 dctx->version = NULL; 1565 dctx->done = NULL; 1566 dctx->done_arg = NULL; 1567 dctx->task = NULL; 1568 atomic_init(&dctx->canceled, false); 1569 dctx->file = NULL; 1570 dctx->tmpfile = NULL; 1571 dctx->format = format; 1572 if (header == NULL) { 1573 dns_master_initrawheader(&dctx->header); 1574 } else { 1575 dctx->header = *header; 1576 } 1577 1578 switch (format) { 1579 case dns_masterformat_text: 1580 dctx->dumpsets = dump_rdatasets_text; 1581 break; 1582 case dns_masterformat_raw: 1583 dctx->dumpsets = dump_rdatasets_raw; 1584 break; 1585 default: 1586 UNREACHABLE(); 1587 } 1588 1589 result = totext_ctx_init(style, NULL, &dctx->tctx); 1590 if (result != ISC_R_SUCCESS) { 1591 UNEXPECTED_ERROR("could not set master file style"); 1592 goto cleanup; 1593 } 1594 1595 isc_stdtime_get(&dctx->now); 1596 dns_db_attach(db, &dctx->db); 1597 1598 dctx->do_date = dns_db_iscache(dctx->db); 1599 if (dctx->do_date) { 1600 (void)dns_db_getservestalettl(dctx->db, 1601 &dctx->tctx.serve_stale_ttl); 1602 } 1603 1604 if (dctx->format == dns_masterformat_text && 1605 (dctx->tctx.style.flags & DNS_STYLEFLAG_REL_OWNER) != 0) 1606 { 1607 options = DNS_DB_RELATIVENAMES; 1608 } else { 1609 options = 0; 1610 } 1611 result = dns_db_createiterator(dctx->db, options, &dctx->dbiter); 1612 if (result != ISC_R_SUCCESS) { 1613 goto cleanup; 1614 } 1615 1616 isc_mutex_init(&dctx->lock); 1617 1618 if (version != NULL) { 1619 dns_db_attachversion(dctx->db, version, &dctx->version); 1620 } else if (!dns_db_iscache(db)) { 1621 dns_db_currentversion(dctx->db, &dctx->version); 1622 } 1623 isc_mem_attach(mctx, &dctx->mctx); 1624 1625 isc_refcount_init(&dctx->references, 1); 1626 dctx->magic = DNS_DCTX_MAGIC; 1627 *dctxp = dctx; 1628 return (ISC_R_SUCCESS); 1629 1630cleanup: 1631 if (dctx->dbiter != NULL) { 1632 dns_dbiterator_destroy(&dctx->dbiter); 1633 } 1634 if (dctx->db != NULL) { 1635 dns_db_detach(&dctx->db); 1636 } 1637 isc_mem_put(mctx, dctx, sizeof(*dctx)); 1638 return (result); 1639} 1640 1641static isc_result_t 1642writeheader(dns_dumpctx_t *dctx) { 1643 isc_result_t result = ISC_R_SUCCESS; 1644 isc_buffer_t buffer; 1645 char *bufmem; 1646 isc_region_t r; 1647 dns_masterrawheader_t rawheader; 1648 uint32_t rawversion, now32; 1649 1650 bufmem = isc_mem_get(dctx->mctx, initial_buffer_length); 1651 1652 isc_buffer_init(&buffer, bufmem, initial_buffer_length); 1653 1654 switch (dctx->format) { 1655 case dns_masterformat_text: 1656 /* 1657 * If the database has cache semantics, output an 1658 * RFC2540 $DATE directive so that the TTLs can be 1659 * adjusted when it is reloaded. For zones it is not 1660 * really needed, and it would make the file 1661 * incompatible with pre-RFC2540 software, so we omit 1662 * it in the zone case. 1663 */ 1664 if (dctx->do_date) { 1665 fprintf(dctx->f, "; using a %u second stale ttl\n", 1666 dctx->tctx.serve_stale_ttl); 1667 result = dns_time32_totext(dctx->now, &buffer); 1668 RUNTIME_CHECK(result == ISC_R_SUCCESS); 1669 isc_buffer_usedregion(&buffer, &r); 1670 fprintf(dctx->f, "$DATE %.*s\n", (int)r.length, 1671 (char *)r.base); 1672 } 1673 break; 1674 case dns_masterformat_raw: 1675 r.base = (unsigned char *)&rawheader; 1676 r.length = sizeof(rawheader); 1677 isc_buffer_region(&buffer, &r); 1678 now32 = dctx->now; 1679 rawversion = 1; 1680 if ((dctx->header.flags & DNS_MASTERRAW_COMPAT) != 0) { 1681 rawversion = 0; 1682 } 1683 1684 isc_buffer_putuint32(&buffer, dctx->format); 1685 isc_buffer_putuint32(&buffer, rawversion); 1686 isc_buffer_putuint32(&buffer, now32); 1687 1688 if (rawversion == 1) { 1689 isc_buffer_putuint32(&buffer, dctx->header.flags); 1690 isc_buffer_putuint32(&buffer, 1691 dctx->header.sourceserial); 1692 isc_buffer_putuint32(&buffer, dctx->header.lastxfrin); 1693 } 1694 1695 INSIST(isc_buffer_usedlength(&buffer) <= sizeof(rawheader)); 1696 result = isc_stdio_write(buffer.base, 1, 1697 isc_buffer_usedlength(&buffer), 1698 dctx->f, NULL); 1699 if (result != ISC_R_SUCCESS) { 1700 break; 1701 } 1702 1703 break; 1704 default: 1705 UNREACHABLE(); 1706 } 1707 1708 isc_mem_put(dctx->mctx, buffer.base, buffer.length); 1709 return (result); 1710} 1711 1712static isc_result_t 1713dumptostream(dns_dumpctx_t *dctx) { 1714 isc_result_t result = ISC_R_SUCCESS; 1715 isc_buffer_t buffer; 1716 char *bufmem; 1717 dns_name_t *name; 1718 dns_fixedname_t fixname; 1719 unsigned int options = DNS_DB_STALEOK; 1720 1721 if ((dctx->tctx.style.flags & DNS_STYLEFLAG_EXPIRED) != 0) { 1722 options |= DNS_DB_EXPIREDOK; 1723 } 1724 1725 bufmem = isc_mem_get(dctx->mctx, initial_buffer_length); 1726 1727 isc_buffer_init(&buffer, bufmem, initial_buffer_length); 1728 1729 name = dns_fixedname_initname(&fixname); 1730 1731 CHECK(writeheader(dctx)); 1732 1733 result = dns_dbiterator_first(dctx->dbiter); 1734 if (result != ISC_R_SUCCESS && result != ISC_R_NOMORE) { 1735 goto cleanup; 1736 } 1737 1738 while (result == ISC_R_SUCCESS) { 1739 dns_rdatasetiter_t *rdsiter = NULL; 1740 dns_dbnode_t *node = NULL; 1741 1742 result = dns_dbiterator_current(dctx->dbiter, &node, name); 1743 if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) { 1744 break; 1745 } 1746 if (result == DNS_R_NEWORIGIN) { 1747 dns_name_t *origin = 1748 dns_fixedname_name(&dctx->tctx.origin_fixname); 1749 result = dns_dbiterator_origin(dctx->dbiter, origin); 1750 RUNTIME_CHECK(result == ISC_R_SUCCESS); 1751 if ((dctx->tctx.style.flags & DNS_STYLEFLAG_REL_DATA) != 1752 0) 1753 { 1754 dctx->tctx.origin = origin; 1755 } 1756 dctx->tctx.neworigin = origin; 1757 } 1758 1759 result = dns_dbiterator_pause(dctx->dbiter); 1760 RUNTIME_CHECK(result == ISC_R_SUCCESS); 1761 1762 result = dns_db_allrdatasets(dctx->db, node, dctx->version, 1763 options, dctx->now, &rdsiter); 1764 if (result != ISC_R_SUCCESS) { 1765 dns_db_detachnode(dctx->db, &node); 1766 goto cleanup; 1767 } 1768 result = (dctx->dumpsets)(dctx->mctx, name, rdsiter, 1769 &dctx->tctx, &buffer, dctx->f); 1770 dns_rdatasetiter_destroy(&rdsiter); 1771 if (result != ISC_R_SUCCESS) { 1772 dns_db_detachnode(dctx->db, &node); 1773 goto cleanup; 1774 } 1775 dns_db_detachnode(dctx->db, &node); 1776 result = dns_dbiterator_next(dctx->dbiter); 1777 } 1778 1779 if (result == ISC_R_NOMORE) { 1780 result = ISC_R_SUCCESS; 1781 } 1782cleanup: 1783 RUNTIME_CHECK(dns_dbiterator_pause(dctx->dbiter) == ISC_R_SUCCESS); 1784 isc_mem_put(dctx->mctx, buffer.base, buffer.length); 1785 return (result); 1786} 1787 1788isc_result_t 1789dns_master_dumptostreamasync(isc_mem_t *mctx, dns_db_t *db, 1790 dns_dbversion_t *version, 1791 const dns_master_style_t *style, FILE *f, 1792 isc_task_t *task, dns_dumpdonefunc_t done, 1793 void *done_arg, dns_dumpctx_t **dctxp) { 1794 dns_dumpctx_t *dctx = NULL; 1795 isc_result_t result; 1796 1797 REQUIRE(task != NULL); 1798 REQUIRE(f != NULL); 1799 REQUIRE(done != NULL); 1800 1801 result = dumpctx_create(mctx, db, version, style, f, &dctx, 1802 dns_masterformat_text, NULL); 1803 if (result != ISC_R_SUCCESS) { 1804 return (result); 1805 } 1806 isc_task_attach(task, &dctx->task); 1807 dctx->done = done; 1808 dctx->done_arg = done_arg; 1809 1810 result = task_send(dctx); 1811 if (result == ISC_R_SUCCESS) { 1812 dns_dumpctx_attach(dctx, dctxp); 1813 return (DNS_R_CONTINUE); 1814 } 1815 1816 dns_dumpctx_detach(&dctx); 1817 return (result); 1818} 1819 1820isc_result_t 1821dns_master_dumptostream(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version, 1822 const dns_master_style_t *style, 1823 dns_masterformat_t format, 1824 dns_masterrawheader_t *header, FILE *f) { 1825 dns_dumpctx_t *dctx = NULL; 1826 isc_result_t result; 1827 1828 result = dumpctx_create(mctx, db, version, style, f, &dctx, format, 1829 header); 1830 if (result != ISC_R_SUCCESS) { 1831 return (result); 1832 } 1833 1834 result = dumptostream(dctx); 1835 INSIST(result != DNS_R_CONTINUE); 1836 dns_dumpctx_detach(&dctx); 1837 1838 result = flushandsync(f, result, NULL); 1839 return (result); 1840} 1841 1842static isc_result_t 1843opentmp(isc_mem_t *mctx, dns_masterformat_t format, const char *file, 1844 char **tempp, FILE **fp) { 1845 FILE *f = NULL; 1846 isc_result_t result; 1847 char *tempname = NULL; 1848 int tempnamelen; 1849 1850 tempnamelen = strlen(file) + 20; 1851 tempname = isc_mem_allocate(mctx, tempnamelen); 1852 1853 result = isc_file_mktemplate(file, tempname, tempnamelen); 1854 if (result != ISC_R_SUCCESS) { 1855 goto cleanup; 1856 } 1857 1858 if (format == dns_masterformat_text) { 1859 result = isc_file_openunique(tempname, &f); 1860 } else { 1861 result = isc_file_bopenunique(tempname, &f); 1862 } 1863 if (result != ISC_R_SUCCESS) { 1864 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL, 1865 DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR, 1866 "dumping master file: %s: open: %s", tempname, 1867 isc_result_totext(result)); 1868 goto cleanup; 1869 } 1870 1871#if defined(POSIX_FADV_DONTNEED) 1872 posix_fadvise(fileno(f), 0, 0, POSIX_FADV_DONTNEED); 1873#endif 1874 1875 *tempp = tempname; 1876 *fp = f; 1877 return (ISC_R_SUCCESS); 1878 1879cleanup: 1880 isc_mem_free(mctx, tempname); 1881 return (result); 1882} 1883 1884isc_result_t 1885dns_master_dumpasync(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version, 1886 const dns_master_style_t *style, const char *filename, 1887 isc_task_t *task, dns_dumpdonefunc_t done, void *done_arg, 1888 dns_dumpctx_t **dctxp, dns_masterformat_t format, 1889 dns_masterrawheader_t *header) { 1890 FILE *f = NULL; 1891 isc_result_t result; 1892 char *tempname = NULL; 1893 char *file = NULL; 1894 dns_dumpctx_t *dctx = NULL; 1895 1896 file = isc_mem_strdup(mctx, filename); 1897 1898 result = opentmp(mctx, format, filename, &tempname, &f); 1899 if (result != ISC_R_SUCCESS) { 1900 goto cleanup; 1901 } 1902 1903 result = dumpctx_create(mctx, db, version, style, f, &dctx, format, 1904 header); 1905 if (result != ISC_R_SUCCESS) { 1906 (void)isc_stdio_close(f); 1907 (void)isc_file_remove(tempname); 1908 goto cleanup; 1909 } 1910 1911 isc_task_attach(task, &dctx->task); 1912 dctx->done = done; 1913 dctx->done_arg = done_arg; 1914 dctx->file = file; 1915 file = NULL; 1916 dctx->tmpfile = tempname; 1917 tempname = NULL; 1918 1919 result = task_send(dctx); 1920 if (result == ISC_R_SUCCESS) { 1921 dns_dumpctx_attach(dctx, dctxp); 1922 return (DNS_R_CONTINUE); 1923 } 1924 1925cleanup: 1926 if (dctx != NULL) { 1927 dns_dumpctx_detach(&dctx); 1928 } 1929 if (file != NULL) { 1930 isc_mem_free(mctx, file); 1931 } 1932 if (tempname != NULL) { 1933 isc_mem_free(mctx, tempname); 1934 } 1935 return (result); 1936} 1937 1938isc_result_t 1939dns_master_dump(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version, 1940 const dns_master_style_t *style, const char *filename, 1941 dns_masterformat_t format, dns_masterrawheader_t *header) { 1942 FILE *f = NULL; 1943 isc_result_t result; 1944 char *tempname; 1945 dns_dumpctx_t *dctx = NULL; 1946 1947 result = opentmp(mctx, format, filename, &tempname, &f); 1948 if (result != ISC_R_SUCCESS) { 1949 return (result); 1950 } 1951 1952 result = dumpctx_create(mctx, db, version, style, f, &dctx, format, 1953 header); 1954 if (result != ISC_R_SUCCESS) { 1955 goto cleanup; 1956 } 1957 1958 result = dumptostream(dctx); 1959 INSIST(result != DNS_R_CONTINUE); 1960 dns_dumpctx_detach(&dctx); 1961 1962 result = closeandrename(f, result, tempname, filename); 1963 1964cleanup: 1965 isc_mem_free(mctx, tempname); 1966 return (result); 1967} 1968 1969/* 1970 * Dump a database node into a master file. 1971 * XXX: this function assumes the text format. 1972 */ 1973isc_result_t 1974dns_master_dumpnodetostream(isc_mem_t *mctx, dns_db_t *db, 1975 dns_dbversion_t *version, dns_dbnode_t *node, 1976 const dns_name_t *name, 1977 const dns_master_style_t *style, FILE *f) { 1978 isc_result_t result; 1979 isc_buffer_t buffer; 1980 char *bufmem; 1981 isc_stdtime_t now; 1982 dns_totext_ctx_t ctx; 1983 dns_rdatasetiter_t *rdsiter = NULL; 1984 unsigned int options = DNS_DB_STALEOK; 1985 1986 if ((style->flags & DNS_STYLEFLAG_EXPIRED) != 0) { 1987 options |= DNS_DB_EXPIREDOK; 1988 } 1989 1990 result = totext_ctx_init(style, NULL, &ctx); 1991 if (result != ISC_R_SUCCESS) { 1992 UNEXPECTED_ERROR("could not set master file style"); 1993 return (ISC_R_UNEXPECTED); 1994 } 1995 1996 isc_stdtime_get(&now); 1997 1998 bufmem = isc_mem_get(mctx, initial_buffer_length); 1999 2000 isc_buffer_init(&buffer, bufmem, initial_buffer_length); 2001 2002 result = dns_db_allrdatasets(db, node, version, options, now, &rdsiter); 2003 if (result != ISC_R_SUCCESS) { 2004 goto failure; 2005 } 2006 result = dump_rdatasets_text(mctx, name, rdsiter, &ctx, &buffer, f); 2007 if (result != ISC_R_SUCCESS) { 2008 goto failure; 2009 } 2010 dns_rdatasetiter_destroy(&rdsiter); 2011 2012 result = ISC_R_SUCCESS; 2013 2014failure: 2015 isc_mem_put(mctx, buffer.base, buffer.length); 2016 return (result); 2017} 2018 2019isc_result_t 2020dns_master_dumpnode(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version, 2021 dns_dbnode_t *node, const dns_name_t *name, 2022 const dns_master_style_t *style, const char *filename) { 2023 FILE *f = NULL; 2024 isc_result_t result; 2025 2026 result = isc_stdio_open(filename, "w", &f); 2027 if (result != ISC_R_SUCCESS) { 2028 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL, 2029 DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR, 2030 "dumping node to file: %s: open: %s", filename, 2031 isc_result_totext(result)); 2032 return (ISC_R_UNEXPECTED); 2033 } 2034 2035 result = dns_master_dumpnodetostream(mctx, db, version, node, name, 2036 style, f); 2037 if (result != ISC_R_SUCCESS) { 2038 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL, 2039 DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR, 2040 "dumping master file: %s: dump: %s", filename, 2041 isc_result_totext(result)); 2042 (void)isc_stdio_close(f); 2043 return (ISC_R_UNEXPECTED); 2044 } 2045 2046 result = isc_stdio_close(f); 2047 if (result != ISC_R_SUCCESS) { 2048 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL, 2049 DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR, 2050 "dumping master file: %s: close: %s", filename, 2051 isc_result_totext(result)); 2052 return (ISC_R_UNEXPECTED); 2053 } 2054 2055 return (result); 2056} 2057 2058dns_masterstyle_flags_t 2059dns_master_styleflags(const dns_master_style_t *style) { 2060 REQUIRE(style != NULL); 2061 return (style->flags); 2062} 2063 2064isc_result_t 2065dns_master_stylecreate(dns_master_style_t **stylep, 2066 dns_masterstyle_flags_t flags, unsigned int ttl_column, 2067 unsigned int class_column, unsigned int type_column, 2068 unsigned int rdata_column, unsigned int line_length, 2069 unsigned int tab_width, unsigned int split_width, 2070 isc_mem_t *mctx) { 2071 dns_master_style_t *style; 2072 2073 REQUIRE(stylep != NULL && *stylep == NULL); 2074 style = isc_mem_get(mctx, sizeof(*style)); 2075 2076 style->flags = flags; 2077 style->ttl_column = ttl_column; 2078 style->class_column = class_column; 2079 style->type_column = type_column; 2080 style->rdata_column = rdata_column; 2081 style->line_length = line_length; 2082 style->tab_width = tab_width; 2083 style->split_width = split_width; 2084 *stylep = style; 2085 return (ISC_R_SUCCESS); 2086} 2087 2088void 2089dns_master_styledestroy(dns_master_style_t **stylep, isc_mem_t *mctx) { 2090 dns_master_style_t *style; 2091 2092 REQUIRE(stylep != NULL && *stylep != NULL); 2093 style = *stylep; 2094 *stylep = NULL; 2095 isc_mem_put(mctx, style, sizeof(*style)); 2096} 2097