1 /*  Copyright (C) 2021 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 <assert.h>
18 
19 #include "knot/common/log.h"
20 #include "knot/updates/apply.h"
21 #include "libknot/libknot.h"
22 #include "contrib/macros.h"
23 #include "contrib/mempattern.h"
24 
25 /*! \brief Replaces rdataset of given type with a copy. */
replace_rdataset_with_copy(zone_node_t * node,uint16_t type)26 static int replace_rdataset_with_copy(zone_node_t *node, uint16_t type)
27 {
28 	int ret = binode_prepare_change(node, NULL);
29 	if (ret != KNOT_EOK) {
30 		return ret;
31 	}
32 
33 	// Find data to copy.
34 	struct rr_data *data = NULL;
35 	for (uint16_t i = 0; i < node->rrset_count; ++i) {
36 		if (node->rrs[i].type == type) {
37 			data = &node->rrs[i];
38 			break;
39 		}
40 	}
41 	if (data == NULL) {
42 		return KNOT_EOK;
43 	}
44 
45 	// Create new data.
46 	knot_rdataset_t *rrs = &data->rrs;
47 	void *copy = malloc(rrs->size);
48 	if (copy == NULL) {
49 		return KNOT_ENOMEM;
50 	}
51 
52 	memcpy(copy, rrs->rdata, rrs->size);
53 
54 	// Store new data into node RRS.
55 	rrs->rdata = copy;
56 
57 	return KNOT_EOK;
58 }
59 
60 /*! \brief Frees RR dataset. For use when a copy was made. */
clear_new_rrs(zone_node_t * node,uint16_t type)61 static void clear_new_rrs(zone_node_t *node, uint16_t type)
62 {
63 	knot_rdataset_t *new_rrs = node_rdataset(node, type);
64 	if (new_rrs) {
65 		knot_rdataset_clear(new_rrs, NULL);
66 	}
67 }
68 
69 /*! \brief Logs redundant rrset operation. */
can_log_rrset(const knot_rrset_t * rrset,int pos,apply_ctx_t * ctx,bool remove)70 static void can_log_rrset(const knot_rrset_t *rrset, int pos, apply_ctx_t *ctx, bool remove)
71 {
72 	if (!(ctx->flags & APPLY_STRICT)) {
73 		return;
74 	}
75 
76 	char type[16];
77 	char data[1024];
78 	const char *msg = remove ? "cannot remove nonexisting RR" :
79 	                           "cannot add existing RR";
80 
81 	char *owner = knot_dname_to_str_alloc(rrset->owner);
82 	if (owner != NULL && knot_rrtype_to_string(rrset->type, type, sizeof(type)) > 0 &&
83 	    knot_rrset_txt_dump_data(rrset, pos, data, sizeof(data), &KNOT_DUMP_STYLE_DEFAULT) > 0) {
84 		log_zone_debug(ctx->contents->apex->owner,
85 		               "node %s, type %s, data '%s', %s", owner, type, data, msg);
86 	}
87 	free(owner);
88 }
89 
90 /*! \brief Returns true if given RR is present in node and can be removed. */
can_remove(const zone_node_t * node,const knot_rrset_t * rrset,apply_ctx_t * ctx)91 static bool can_remove(const zone_node_t *node, const knot_rrset_t *rrset, apply_ctx_t *ctx)
92 {
93 	if (node == NULL) {
94 		// Node does not exist, cannot remove anything.
95 		can_log_rrset(rrset, 0, ctx, true);
96 		return false;
97 	}
98 
99 	const knot_rdataset_t *node_rrs = node_rdataset(node, rrset->type);
100 	if (node_rrs == NULL) {
101 		// Node does not have this type at all.
102 		can_log_rrset(rrset, 0, ctx, true);
103 		return false;
104 	}
105 
106 	knot_rdata_t *rr_cmp = rrset->rrs.rdata;
107 	for (uint16_t i = 0; i < rrset->rrs.count; ++i) {
108 		if (!knot_rdataset_member(node_rrs, rr_cmp)) {
109 			// At least one RR doesnt' match.
110 			can_log_rrset(rrset, i, ctx, true);
111 			return false;
112 		}
113 		rr_cmp = knot_rdataset_next(rr_cmp);
114 	}
115 
116 	return true;
117 }
118 
119 /*! \brief Returns true if given RR is not present in node and can be added. */
can_add(const zone_node_t * node,const knot_rrset_t * rrset,apply_ctx_t * ctx)120 static bool can_add(const zone_node_t *node, const knot_rrset_t *rrset, apply_ctx_t *ctx)
121 {
122 	if (node == NULL) {
123 		// Node does not exist, can add anything.
124 		return true;
125 	}
126 	const knot_rdataset_t *node_rrs = node_rdataset(node, rrset->type);
127 	if (node_rrs == NULL) {
128 		// Node does not have this type at all.
129 		return true;
130 	}
131 
132 	knot_rdata_t *rr_cmp = rrset->rrs.rdata;
133 	for (uint16_t i = 0; i < rrset->rrs.count; ++i) {
134 		if (knot_rdataset_member(node_rrs, rr_cmp)) {
135 			// No RR must match.
136 			can_log_rrset(rrset, i, ctx, false);
137 			return false;
138 		}
139 		rr_cmp = knot_rdataset_next(rr_cmp);
140 	}
141 
142 	return true;
143 }
144 
apply_init_ctx(apply_ctx_t * ctx,zone_contents_t * contents,uint32_t flags)145 int apply_init_ctx(apply_ctx_t *ctx, zone_contents_t *contents, uint32_t flags)
146 {
147 	if (ctx == NULL) {
148 		return KNOT_EINVAL;
149 	}
150 
151 	ctx->contents = contents;
152 
153 	ctx->node_ptrs = zone_tree_create(true);
154 	if (ctx->node_ptrs == NULL) {
155 		return KNOT_ENOMEM;
156 	}
157 	ctx->node_ptrs->flags = contents->nodes->flags;
158 
159 	ctx->nsec3_ptrs = zone_tree_create(true);
160 	if (ctx->nsec3_ptrs == NULL) {
161 		zone_tree_free(&ctx->node_ptrs);
162 		return KNOT_ENOMEM;
163 	}
164 	ctx->nsec3_ptrs->flags = contents->nodes->flags;
165 
166 	ctx->adjust_ptrs = zone_tree_create(true);
167 	if (ctx->adjust_ptrs == NULL) {
168 		zone_tree_free(&ctx->nsec3_ptrs);
169 		zone_tree_free(&ctx->node_ptrs);
170 		return KNOT_ENOMEM;
171 	}
172 	ctx->adjust_ptrs->flags = contents->nodes->flags;
173 
174 	ctx->flags = flags;
175 
176 	return KNOT_EOK;
177 }
178 
add_node_cb(const knot_dname_t * owner,void * ctx)179 static zone_node_t *add_node_cb(const knot_dname_t *owner, void *ctx)
180 {
181 	zone_tree_t *tree = ctx;
182 	zone_node_t *node = zone_tree_get(tree, owner);
183 	if (node == NULL) {
184 		node = node_new_for_tree(owner, tree, NULL);
185 	} else {
186 		node->flags &= ~NODE_FLAGS_DELETED;
187 	}
188 	return node;
189 }
190 
apply_add_rr(apply_ctx_t * ctx,const knot_rrset_t * rr)191 int apply_add_rr(apply_ctx_t *ctx, const knot_rrset_t *rr)
192 {
193 	zone_contents_t *contents = ctx->contents;
194 	bool nsec3rel = knot_rrset_is_nsec3rel(rr);
195 	zone_tree_t *ptrs = nsec3rel ? ctx->nsec3_ptrs : ctx->node_ptrs;
196 	zone_tree_t *tree = zone_contents_tree_for_rr(contents, rr);
197 	if (tree == NULL) {
198 		return KNOT_ENOMEM;
199 	}
200 
201 	// Get or create node with this owner, search changes first
202 	zone_node_t *node = NULL;
203 	int ret = zone_tree_add_node(tree, contents->apex, rr->owner, add_node_cb, ptrs, &node);
204 	if (ret != KNOT_EOK) {
205 		return ret;
206 	}
207 
208 	if (!can_add(node, rr, ctx)) {
209 		return (ctx->flags & APPLY_STRICT) ? KNOT_EISRECORD : KNOT_EOK;
210 	}
211 
212 	ret = zone_tree_insert_with_parents(ptrs, node, nsec3rel);
213 	if (ret != KNOT_EOK) {
214 		return ret;
215 	}
216 
217 	if (binode_rdata_shared(node, rr->type)) {
218 		// Modifying existing RRSet.
219 		ret = replace_rdataset_with_copy(node, rr->type);
220 		if (ret != KNOT_EOK) {
221 			return ret;
222 		}
223 	}
224 
225 	// Insert new RR to RRSet, data will be copied.
226 	ret = node_add_rrset(node, rr, NULL);
227 	if (ret == KNOT_ETTL) {
228 		// this shall not happen except applying journal created before this bugfix
229 		return KNOT_EOK;
230 	}
231 	return ret;
232 }
233 
apply_remove_rr(apply_ctx_t * ctx,const knot_rrset_t * rr)234 int apply_remove_rr(apply_ctx_t *ctx, const knot_rrset_t *rr)
235 {
236 	zone_contents_t *contents = ctx->contents;
237 	bool nsec3rel = knot_rrset_is_nsec3rel(rr);
238 	zone_tree_t *ptrs = nsec3rel ? ctx->nsec3_ptrs : ctx->node_ptrs;
239 	zone_tree_t *tree = zone_contents_tree_for_rr(contents, rr);
240 	if (tree == NULL) {
241 		return KNOT_ENOMEM;
242 	}
243 
244 	// Find node for this owner
245 	zone_node_t *node = zone_contents_find_node_for_rr(contents, rr);
246 	if (!can_remove(node, rr, ctx)) {
247 		return (ctx->flags & APPLY_STRICT) ? KNOT_ENORECORD : KNOT_EOK;
248 	}
249 
250 	int ret = zone_tree_insert_with_parents(ptrs, node, nsec3rel);
251 	if (ret != KNOT_EOK) {
252 		return ret;
253 	}
254 
255 	if (binode_rdata_shared(node, rr->type)) {
256 		ret = replace_rdataset_with_copy(node, rr->type);
257 		if (ret != KNOT_EOK) {
258 			return ret;
259 		}
260 	}
261 
262 	ret = node_remove_rrset(node, rr, NULL);
263 	if (ret != KNOT_EOK) {
264 		clear_new_rrs(node, rr->type);
265 		return ret;
266 	}
267 
268 	if (node->rrset_count == 0 && node->children == 0 && node != contents->apex) {
269 		zone_tree_del_node(tree, node, false);
270 	}
271 
272 	return KNOT_EOK;
273 }
274 
apply_replace_soa(apply_ctx_t * ctx,const knot_rrset_t * rr)275 int apply_replace_soa(apply_ctx_t *ctx, const knot_rrset_t *rr)
276 {
277 	zone_contents_t *contents = ctx->contents;
278 
279 	if (!knot_dname_is_equal(rr->owner, contents->apex->owner)) {
280 		return KNOT_EDENIED;
281 	}
282 
283 	knot_rrset_t old_soa = node_rrset(contents->apex, KNOT_RRTYPE_SOA);
284 
285 	int ret = apply_remove_rr(ctx, &old_soa);
286 	if (ret != KNOT_EOK) {
287 		return ret;
288 	}
289 
290 	// Check for SOA with proper serial but different rdata.
291 	if (node_rrtype_exists(contents->apex, KNOT_RRTYPE_SOA)) {
292 		return KNOT_ESOAINVAL;
293 	}
294 
295 	return apply_add_rr(ctx, rr);
296 }
297 
apply_cleanup(apply_ctx_t * ctx)298 void apply_cleanup(apply_ctx_t *ctx)
299 {
300 	if (ctx == NULL) {
301 		return;
302 	}
303 
304 	if (ctx->flags & APPLY_UNIFY_FULL) {
305 		zone_trees_unify_binodes(ctx->contents->nodes, ctx->contents->nsec3_nodes, true);
306 	} else {
307 		zone_trees_unify_binodes(ctx->adjust_ptrs, NULL, false); // beware there might be duplicities in ctx->adjust_ptrs and ctx->node_ptrs, so we don't free here
308 		zone_trees_unify_binodes(ctx->node_ptrs, ctx->nsec3_ptrs, true);
309 	}
310 
311 	zone_tree_free(&ctx->node_ptrs);
312 	zone_tree_free(&ctx->nsec3_ptrs);
313 	zone_tree_free(&ctx->adjust_ptrs);
314 
315 	if (ctx->cow_mutex != NULL) {
316 		knot_sem_post(ctx->cow_mutex);
317 	}
318 }
319 
apply_rollback(apply_ctx_t * ctx)320 void apply_rollback(apply_ctx_t *ctx)
321 {
322 	if (ctx == NULL) {
323 		return;
324 	}
325 
326 	if (ctx->node_ptrs != NULL) {
327 		ctx->node_ptrs->flags ^= ZONE_TREE_BINO_SECOND;
328 	}
329 	if (ctx->nsec3_ptrs != NULL) {
330 		ctx->nsec3_ptrs->flags ^= ZONE_TREE_BINO_SECOND;
331 	}
332 	zone_trees_unify_binodes(ctx->node_ptrs, ctx->nsec3_ptrs, true);
333 
334 	zone_tree_free(&ctx->node_ptrs);
335 	zone_tree_free(&ctx->nsec3_ptrs);
336 	zone_tree_free(&ctx->adjust_ptrs);
337 
338 	trie_cow_rollback(ctx->contents->nodes->cow, NULL, NULL);
339 	ctx->contents->nodes->cow = NULL;
340 	if (ctx->contents->nsec3_nodes != NULL && ctx->contents->nsec3_nodes->cow != NULL) {
341 		trie_cow_rollback(ctx->contents->nsec3_nodes->cow, NULL, NULL);
342 		ctx->contents->nsec3_nodes->cow = NULL;
343 	} else if (ctx->contents->nsec3_nodes != NULL) {
344 		zone_tree_free(&ctx->contents->nsec3_nodes);
345 		ctx->contents->nsec3_nodes = NULL;
346 	}
347 
348 	free(ctx->contents->nodes);
349 	free(ctx->contents->nsec3_nodes);
350 
351 	dnssec_nsec3_params_free(&ctx->contents->nsec3_params);
352 
353 	free(ctx->contents);
354 
355 	if (ctx->cow_mutex != NULL) {
356 		knot_sem_post(ctx->cow_mutex);
357 	}
358 }
359 
update_free_zone(zone_contents_t * contents)360 void update_free_zone(zone_contents_t *contents)
361 {
362 	if (contents == NULL) {
363 		return;
364 	}
365 
366 	trie_cow_commit(contents->nodes->cow, NULL, NULL);
367 	contents->nodes->cow = NULL;
368 	if (contents->nsec3_nodes != NULL && contents->nsec3_nodes->cow != NULL) {
369 		trie_cow_commit(contents->nsec3_nodes->cow, NULL, NULL);
370 		contents->nsec3_nodes->cow = NULL;
371 	}
372 
373 	free(contents->nodes);
374 	free(contents->nsec3_nodes);
375 
376 	dnssec_nsec3_params_free(&contents->nsec3_params);
377 
378 	free(contents);
379 }
380