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