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