zone-dump.c 5.62 KB
Newer Older
1
/*  Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
Daniel Salzman's avatar
Daniel Salzman committed
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

17
#include <inttypes.h>
Daniel Salzman's avatar
Daniel Salzman committed
18

19
#include "knot/dnssec/zone-nsec.h"
20
#include "knot/zone/zone-dump.h"
Daniel Salzman's avatar
Daniel Salzman committed
21 22
#include "libknot/libknot.h"

23
/*! \brief Size of auxiliary buffer. */
Daniel Salzman's avatar
Daniel Salzman committed
24 25
#define DUMP_BUF_LEN (70 * 1024)

26
/*! \brief Dump parameters. */
Daniel Salzman's avatar
Daniel Salzman committed
27
typedef struct {
28 29 30 31
	FILE     *file;
	char     *buf;
	size_t   buflen;
	uint64_t rr_count;
32
	bool     dump_rrsig;
33
	bool     dump_nsec;
Daniel Salzman's avatar
Daniel Salzman committed
34
	const knot_dname_t *origin;
35
	const knot_dump_style_t *style;
36
	const char *first_comment;
Daniel Salzman's avatar
Daniel Salzman committed
37 38
} dump_params_t;

39
static int apex_node_dump_text(zone_node_t *node, dump_params_t *params)
Daniel Salzman's avatar
Daniel Salzman committed
40
{
41
	knot_rrset_t soa = node_rrset(node, KNOT_RRTYPE_SOA);
42 43
	knot_dump_style_t soa_style = *params->style;

44
	// Dump SOA record as a first.
45
	if (!params->dump_nsec) {
46 47
		int ret = knot_rrset_txt_dump(&soa, &params->buf, &params->buflen,
		                              &soa_style);
48 49
		if (ret < 0) {
			return ret;
50
		}
51
		params->rr_count += soa.rrs.count;
52 53
		fprintf(params->file, "%s", params->buf);
		params->buf[0] = '\0';
54
	}
Daniel Salzman's avatar
Daniel Salzman committed
55

56
	// Dump other records.
57
	for (uint16_t i = 0; i < node->rrset_count; i++) {
58
		knot_rrset_t rrset = node_rrset_at(node, i);
59
		switch (rrset.type) {
60
		case KNOT_RRTYPE_NSEC:
61
			continue;
62 63
		case KNOT_RRTYPE_RRSIG:
			continue;
64 65 66 67
		case KNOT_RRTYPE_SOA:
			continue;
		default:
			break;
68 69
		}

70 71
		int ret = knot_rrset_txt_dump(&rrset, &params->buf, &params->buflen,
		                              params->style);
72 73
		if (ret < 0) {
			return ret;
74
		}
75
		params->rr_count +=  rrset.rrs.count;
76 77
		fprintf(params->file, "%s", params->buf);
		params->buf[0] = '\0';
Daniel Salzman's avatar
Daniel Salzman committed
78 79
	}

80
	return KNOT_EOK;
Daniel Salzman's avatar
Daniel Salzman committed
81 82
}

83
static int node_dump_text(zone_node_t *node, void *data)
Daniel Salzman's avatar
Daniel Salzman committed
84 85 86
{
	dump_params_t *params = (dump_params_t *)data;

87
	// Zone apex rrsets.
Jan Kadlec's avatar
Jan Kadlec committed
88 89
	if (node->owner == params->origin && !params->dump_rrsig &&
	    !params->dump_nsec) {
Daniel Salzman's avatar
Daniel Salzman committed
90
		apex_node_dump_text(node, params);
91
		return KNOT_EOK;
Daniel Salzman's avatar
Daniel Salzman committed
92 93
	}

94
	// Dump non-apex rrsets.
95
	for (uint16_t i = 0; i < node->rrset_count; i++) {
96
		knot_rrset_t rrset = node_rrset_at(node, i);
97
		switch (rrset.type) {
98 99 100 101 102
		case KNOT_RRTYPE_RRSIG:
			if (params->dump_rrsig) {
				break;
			}
			continue;
103 104 105 106 107
		case KNOT_RRTYPE_NSEC:
			if (params->dump_nsec) {
				break;
			}
			continue;
108 109 110 111 112
		case KNOT_RRTYPE_NSEC3:
			if (params->dump_nsec) {
				break;
			}
			continue;
113
		default:
Jan Kadlec's avatar
Jan Kadlec committed
114
			if (params->dump_nsec || params->dump_rrsig) {
115 116 117 118 119
				continue;
			}
			break;
		}

120 121 122 123 124 125
		// Dump block comment if available.
		if (params->first_comment != NULL) {
			fprintf(params->file, "%s", params->first_comment);
			params->first_comment = NULL;
		}

126 127
		int ret = knot_rrset_txt_dump(&rrset, &params->buf, &params->buflen,
		                              params->style);
128 129
		if (ret < 0) {
			return ret;
Daniel Salzman's avatar
Daniel Salzman committed
130
		}
131
		params->rr_count += rrset.rrs.count;
Daniel Salzman's avatar
Daniel Salzman committed
132
		fprintf(params->file, "%s", params->buf);
133
		params->buf[0] = '\0';
Daniel Salzman's avatar
Daniel Salzman committed
134 135
	}

136
	return KNOT_EOK;
Daniel Salzman's avatar
Daniel Salzman committed
137 138
}

139
int zone_dump_text(zone_contents_t *zone, FILE *file, bool comments)
Daniel Salzman's avatar
Daniel Salzman committed
140 141 142 143 144
{
	if (zone == NULL || file == NULL) {
		return KNOT_EINVAL;
	}

145
	// Allocate auxiliary buffer for dumping operations.
Daniel Salzman's avatar
Daniel Salzman committed
146 147 148 149 150
	char *buf = malloc(DUMP_BUF_LEN);
	if (buf == NULL) {
		return KNOT_ENOMEM;
	}

151 152 153
	if (comments) {
		fprintf(file, ";; Zone dump (Knot DNS %s)\n", PACKAGE_VERSION);
	}
Daniel Salzman's avatar
Daniel Salzman committed
154

155
	// Set structure with parameters.
156
	zone_node_t *apex = zone->apex;
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
	dump_params_t params = {
		.file = file,
		.buf = buf,
		.buflen = DUMP_BUF_LEN,
		.rr_count = 0,
		.origin = apex->owner,
		.style = &KNOT_DUMP_STYLE_DEFAULT,
		.dump_rrsig = false,
		.dump_nsec = false
	};

	// Dump standard zone records without RRSIGS.
	int ret = zone_contents_apply(zone, node_dump_text, &params);
	if (ret != KNOT_EOK) {
		return ret;
	}
173

174 175
	// Dump RRSIG records if available.
	params.dump_rrsig = true;
176
	params.dump_nsec = false;
177
	params.first_comment = comments ? ";; DNSSEC signatures\n" : NULL;
178
	ret = zone_contents_apply(zone, node_dump_text, &params);
179 180
	if (ret != KNOT_EOK) {
		return ret;
Daniel Salzman's avatar
Daniel Salzman committed
181 182
	}

183 184 185
	// Dump NSEC chain if available.
	params.dump_rrsig = false;
	params.dump_nsec = true;
186
	params.first_comment = comments ? ";; DNSSEC NSEC chain\n" : NULL;
187 188 189
	ret = zone_contents_apply(zone, node_dump_text, &params);
	if (ret != KNOT_EOK) {
		return ret;
190 191 192
	}

	// Dump NSEC3 chain if available.
193 194
	params.dump_rrsig = false;
	params.dump_nsec = true;
195
	params.first_comment = comments ? ";; DNSSEC NSEC3 chain\n" : NULL;
196 197 198 199
	ret = zone_contents_nsec3_apply(zone, node_dump_text, &params);
	if (ret != KNOT_EOK) {
		return ret;
	}
200

201 202
	params.dump_rrsig = true;
	params.dump_nsec = false;
203
	params.first_comment = comments ? ";; DNSSEC NSEC3 signatures\n" : NULL;
204 205 206
	ret = zone_contents_nsec3_apply(zone, node_dump_text, &params);
	if (ret != KNOT_EOK) {
		return ret;
Daniel Salzman's avatar
Daniel Salzman committed
207
	}
208

209 210 211 212 213 214 215 216 217 218 219 220 221
	if (comments) {
		// Create formatted date-time string.
		time_t now = time(NULL);
		struct tm tm;
		localtime_r(&now, &tm);
		char date[64];
		strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S %Z", &tm);

		// Dump trailing statistics.
		fprintf(file, ";; Written %"PRIu64" records\n"
		              ";; Time %s\n",
	        	params.rr_count, date);
	}
222

223
	free(params.buf); // params.buf may be != buf because of knot_rrset_txt_dump_dynamic()
Daniel Salzman's avatar
Daniel Salzman committed
224 225 226

	return KNOT_EOK;
}