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