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