Coverage Report

Created: 2026-01-24 06:23

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