Coverage Report

Created: 2025-11-24 06:17

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