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