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