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