1 /*  Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
2 
3     This program is free software: you can redistribute it and/or modify
4     it under the terms of the GNU General Public License as published by
5     the Free Software Foundation, either version 3 of the License, or
6     (at your option) any later version.
7 
8     This program is distributed in the hope that it will be useful,
9     but WITHOUT ANY WARRANTY; without even the implied warranty of
10     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11     GNU General Public License for more details.
12 
13     You should have received a copy of the GNU General Public License
14     along with this program.  If not, see <https://www.gnu.org/licenses/>.
15  */
16 
17 #include <inttypes.h>
18 
19 #include "knot/dnssec/zone-nsec.h"
20 #include "knot/zone/zone-dump.h"
21 #include "libknot/libknot.h"
22 
23 /*! \brief Size of auxiliary buffer. */
24 #define DUMP_BUF_LEN (70 * 1024)
25 
26 /*! \brief Dump parameters. */
27 typedef struct {
28 	FILE     *file;
29 	char     *buf;
30 	size_t   buflen;
31 	uint64_t rr_count;
32 	bool     dump_rrsig;
33 	bool     dump_nsec;
34 	const knot_dname_t *origin;
35 	const knot_dump_style_t *style;
36 	const char *first_comment;
37 } dump_params_t;
38 
apex_node_dump_text(zone_node_t * node,dump_params_t * params)39 static int apex_node_dump_text(zone_node_t *node, dump_params_t *params)
40 {
41 	knot_rrset_t soa = node_rrset(node, KNOT_RRTYPE_SOA);
42 	knot_dump_style_t soa_style = *params->style;
43 
44 	// Dump SOA record as a first.
45 	if (!params->dump_nsec) {
46 		int ret = knot_rrset_txt_dump(&soa, &params->buf, &params->buflen,
47 		                              &soa_style);
48 		if (ret < 0) {
49 			return ret;
50 		}
51 		params->rr_count += soa.rrs.count;
52 		fprintf(params->file, "%s", params->buf);
53 		params->buf[0] = '\0';
54 	}
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 		case KNOT_RRTYPE_RRSIG:
63 			continue;
64 		case KNOT_RRTYPE_SOA:
65 			continue;
66 		default:
67 			break;
68 		}
69 
70 		int ret = knot_rrset_txt_dump(&rrset, &params->buf, &params->buflen,
71 		                              params->style);
72 		if (ret < 0) {
73 			return ret;
74 		}
75 		params->rr_count +=  rrset.rrs.count;
76 		fprintf(params->file, "%s", params->buf);
77 		params->buf[0] = '\0';
78 	}
79 
80 	return KNOT_EOK;
81 }
82 
node_dump_text(zone_node_t * node,void * data)83 static int node_dump_text(zone_node_t *node, void *data)
84 {
85 	dump_params_t *params = (dump_params_t *)data;
86 
87 	// Zone apex rrsets.
88 	if (node->owner == params->origin && !params->dump_rrsig &&
89 	    !params->dump_nsec) {
90 		apex_node_dump_text(node, params);
91 		return KNOT_EOK;
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 		case KNOT_RRTYPE_RRSIG:
99 			if (params->dump_rrsig) {
100 				break;
101 			}
102 			continue;
103 		case KNOT_RRTYPE_NSEC:
104 			if (params->dump_nsec) {
105 				break;
106 			}
107 			continue;
108 		case KNOT_RRTYPE_NSEC3:
109 			if (params->dump_nsec) {
110 				break;
111 			}
112 			continue;
113 		default:
114 			if (params->dump_nsec || params->dump_rrsig) {
115 				continue;
116 			}
117 			break;
118 		}
119 
120 		// Dump block comment if available.
121 		if (params->first_comment != NULL) {
122 			fprintf(params->file, "%s", params->first_comment);
123 			params->first_comment = NULL;
124 		}
125 
126 		int ret = knot_rrset_txt_dump(&rrset, &params->buf, &params->buflen,
127 		                              params->style);
128 		if (ret < 0) {
129 			return ret;
130 		}
131 		params->rr_count += rrset.rrs.count;
132 		fprintf(params->file, "%s", params->buf);
133 		params->buf[0] = '\0';
134 	}
135 
136 	return KNOT_EOK;
137 }
138 
zone_dump_text(zone_contents_t * zone,FILE * file,bool comments)139 int zone_dump_text(zone_contents_t *zone, FILE *file, bool comments)
140 {
141 	if (file == NULL) {
142 		return KNOT_EINVAL;
143 	}
144 
145 	if (zone == NULL) {
146 		return KNOT_EEMPTYZONE;
147 	}
148 
149 	// Allocate auxiliary buffer for dumping operations.
150 	char *buf = malloc(DUMP_BUF_LEN);
151 	if (buf == NULL) {
152 		return KNOT_ENOMEM;
153 	}
154 
155 	if (comments) {
156 		fprintf(file, ";; Zone dump (Knot DNS %s)\n", PACKAGE_VERSION);
157 	}
158 
159 	// Set structure with parameters.
160 	zone_node_t *apex = zone->apex;
161 	dump_params_t params = {
162 		.file = file,
163 		.buf = buf,
164 		.buflen = DUMP_BUF_LEN,
165 		.rr_count = 0,
166 		.origin = apex->owner,
167 		.style = &KNOT_DUMP_STYLE_DEFAULT,
168 		.dump_rrsig = false,
169 		.dump_nsec = false
170 	};
171 
172 	// Dump standard zone records without RRSIGS.
173 	int ret = zone_contents_apply(zone, node_dump_text, &params);
174 	if (ret != KNOT_EOK) {
175 		free(params.buf);
176 		return ret;
177 	}
178 
179 	// Dump RRSIG records if available.
180 	params.dump_rrsig = true;
181 	params.dump_nsec = false;
182 	params.first_comment = comments ? ";; DNSSEC signatures\n" : NULL;
183 	ret = zone_contents_apply(zone, node_dump_text, &params);
184 	if (ret != KNOT_EOK) {
185 		free(params.buf);
186 		return ret;
187 	}
188 
189 	// Dump NSEC chain if available.
190 	params.dump_rrsig = false;
191 	params.dump_nsec = true;
192 	params.first_comment = comments ? ";; DNSSEC NSEC chain\n" : NULL;
193 	ret = zone_contents_apply(zone, node_dump_text, &params);
194 	if (ret != KNOT_EOK) {
195 		free(params.buf);
196 		return ret;
197 	}
198 
199 	// Dump NSEC3 chain if available.
200 	params.dump_rrsig = false;
201 	params.dump_nsec = true;
202 	params.first_comment = comments ? ";; DNSSEC NSEC3 chain\n" : NULL;
203 	ret = zone_contents_nsec3_apply(zone, node_dump_text, &params);
204 	if (ret != KNOT_EOK) {
205 		free(params.buf);
206 		return ret;
207 	}
208 
209 	params.dump_rrsig = true;
210 	params.dump_nsec = false;
211 	params.first_comment = comments ? ";; DNSSEC NSEC3 signatures\n" : NULL;
212 	ret = zone_contents_nsec3_apply(zone, node_dump_text, &params);
213 	if (ret != KNOT_EOK) {
214 		free(params.buf);
215 		return ret;
216 	}
217 
218 	if (comments) {
219 		// Create formatted date-time string.
220 		time_t now = time(NULL);
221 		struct tm tm;
222 		localtime_r(&now, &tm);
223 		char date[64];
224 		strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S %Z", &tm);
225 
226 		// Dump trailing statistics.
227 		fprintf(file, ";; Written %"PRIu64" records\n"
228 		              ";; Time %s\n",
229 	        	params.rr_count, date);
230 	}
231 
232 	free(params.buf); // params.buf may be != buf because of knot_rrset_txt_dump_dynamic()
233 
234 	return KNOT_EOK;
235 }
236