contents.c 19.4 KB
Newer Older
1
/*  Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
Lubos Slovak's avatar
Lubos Slovak committed
2 3 4 5 6 7 8 9 10 11 12 13

    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
14
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
Lubos Slovak's avatar
Lubos Slovak committed
15 16
 */

17 18
#include <assert.h>

19
#include "libdnssec/error.h"
Libor Peltan's avatar
Libor Peltan committed
20
#include "knot/zone/adjust.h"
21
#include "knot/zone/contents.h"
22
#include "knot/common/log.h"
23
#include "knot/dnssec/zone-nsec.h"
24
#include "libknot/libknot.h"
25
#include "contrib/qp-trie/trie.h"
26
#include "contrib/macros.h"
27

28
typedef struct {
29
	zone_contents_apply_cb_t func;
30
	void *data;
31
} zone_tree_func_t;
32

33
static int tree_apply_cb(zone_node_t **node, void *data)
34 35
{
	if (node == NULL || data == NULL) {
36
		return KNOT_EINVAL;
37 38
	}

39
	zone_tree_func_t *f = (zone_tree_func_t *)data;
40
	return f->func(*node, f->data);
41 42
}

43 44 45 46 47 48 49 50 51
/*!
 * \brief Checks if the given node can be inserted into the given zone.
 *
 * Checks if both the arguments are non-NULL and if the owner of the node
 * belongs to the zone (i.e. is a subdomain of the zone apex).
 *
 * \param zone Zone to which the node is going to be inserted.
 * \param node Node to check.
 *
Lubos Slovak's avatar
Lubos Slovak committed
52
 * \retval KNOT_EOK if both arguments are non-NULL and the node belongs to the
53
 *         zone.
Marek Vavrusa's avatar
Marek Vavrusa committed
54
 * \retval KNOT_EINVAL if either of the arguments is NULL.
55
 * \retval KNOT_EOUTOFZONE if the node does not belong to the zone.
56
 */
Daniel Salzman's avatar
Daniel Salzman committed
57
static int check_node(const zone_contents_t *contents, const zone_node_t *node)
58
{
Daniel Salzman's avatar
Daniel Salzman committed
59
	assert(contents);
60
	assert(contents->apex != NULL);
Daniel Salzman's avatar
Daniel Salzman committed
61 62
	assert(node);

63
	if (knot_dname_in_bailiwick(node->owner, contents->apex->owner) <= 0) {
64
		return KNOT_EOUTOFZONE;
65
	}
Daniel Salzman's avatar
Daniel Salzman committed
66

Lubos Slovak's avatar
Lubos Slovak committed
67
	return KNOT_EOK;
68 69 70 71 72 73 74 75 76 77
}

/*!
 * \brief Destroys all RRSets in a node.
 *
 * This function is designed to be used in the tree-iterating functions.
 *
 * \param node Node to destroy RRSets from.
 * \param data Unused parameter.
 */
Daniel Salzman's avatar
Daniel Salzman committed
78
static int destroy_node_rrsets_from_tree(zone_node_t **node, void *data)
79
{
Daniel Salzman's avatar
Daniel Salzman committed
80
	assert(node);
Daniel Salzman's avatar
Daniel Salzman committed
81 82
	UNUSED(data);

Daniel Salzman's avatar
Daniel Salzman committed
83 84
	if (*node != NULL) {
		node_free_rrsets(*node, NULL);
85
		node_free(*node, NULL);
86
	}
87

88
	return KNOT_EOK;
89 90
}

91 92
static int measure_size(zone_node_t *node, void *data){

Libor Peltan's avatar
Libor Peltan committed
93
	node_size(node, data);
94 95 96
	return KNOT_EOK;
}

97 98
static int measure_max_ttl(zone_node_t *node, void *data){

Libor Peltan's avatar
Libor Peltan committed
99
	node_max_ttl(node, data);
Daniel Salzman's avatar
Daniel Salzman committed
100
	return KNOT_EOK;
101 102
}

103 104 105 106 107 108 109 110 111 112
/*!
 * \brief Tries to find the given domain name in the zone tree.
 *
 * \param zone Zone to search in.
 * \param name Domain name to find.
 * \param node Found node.
 * \param previous Previous node in canonical order (i.e. the one directly
 *                 preceding \a name in canonical order, regardless if the name
 *                 is in the zone or not).
 *
113
 * \retval true if the domain name was found. In such case \a node holds the
114 115
 *              zone node with \a name as its owner. \a previous is set
 *              properly.
116 117
 * \retval false if the domain name was not found. \a node may hold any (or none)
 *               node. \a previous is set properly.
118
 */
119 120
static bool find_in_tree(zone_tree_t *tree, const knot_dname_t *name,
                         zone_node_t **node, zone_node_t **previous)
121
{
122
	assert(tree != NULL);
123 124 125 126
	assert(name != NULL);
	assert(node != NULL);
	assert(previous != NULL);

127
	zone_node_t *found = NULL, *prev = NULL;
128

129 130 131 132 133
	int match = zone_tree_get_less_or_equal(tree, name, &found, &prev);
	if (match < 0) {
		assert(0);
		return false;
	}
Daniel Salzman's avatar
Daniel Salzman committed
134

135
	*node = found;
Lubos Slovak's avatar
Lubos Slovak committed
136
	*previous = prev;
137

138
	return match > 0;
139 140
}

141
zone_contents_t *zone_contents_new(const knot_dname_t *apex_name)
142
{
143 144 145 146
	if (apex_name == NULL) {
		return NULL;
	}

147
	zone_contents_t *contents = calloc(1, sizeof(*contents));
148 149 150 151
	if (contents == NULL) {
		return NULL;
	}

152
	contents->apex = node_new(apex_name, NULL);
153 154 155
	if (contents->apex == NULL) {
		goto cleanup;
	}
156

157
	contents->nodes = zone_tree_create();
158 159 160 161
	if (contents->nodes == NULL) {
		goto cleanup;
	}

162
	if (zone_tree_insert(contents->nodes, contents->apex) != KNOT_EOK) {
163 164 165 166 167 168
		goto cleanup;
	}

	return contents;

cleanup:
169
	node_free(contents->apex, NULL);
170 171 172 173 174
	free(contents->nodes);
	free(contents);
	return NULL;
}

Daniel Salzman's avatar
Daniel Salzman committed
175
static zone_node_t *get_node(const zone_contents_t *zone, const knot_dname_t *name)
176
{
Daniel Salzman's avatar
Daniel Salzman committed
177 178
	assert(zone);
	assert(name);
179

Daniel Salzman's avatar
Daniel Salzman committed
180
	return zone_tree_get(zone->nodes, name);
181 182
}

Daniel Salzman's avatar
Daniel Salzman committed
183
static int add_node(zone_contents_t *zone, zone_node_t *node, bool create_parents)
184 185
{
	if (zone == NULL || node == NULL) {
Marek Vavrusa's avatar
Marek Vavrusa committed
186
		return KNOT_EINVAL;
187 188
	}

Daniel Salzman's avatar
Daniel Salzman committed
189 190
	int ret = check_node(zone, node);
	if (ret != KNOT_EOK) {
191 192 193
		return ret;
	}

194
	ret = zone_tree_insert(zone->nodes, node);
Lubos Slovak's avatar
Lubos Slovak committed
195
	if (ret != KNOT_EOK) {
196 197 198 199
		return ret;
	}

	if (!create_parents) {
Lubos Slovak's avatar
Lubos Slovak committed
200
		return KNOT_EOK;
201 202
	}

203
	/* No parents for root domain. */
Daniel Salzman's avatar
Daniel Salzman committed
204
	if (*node->owner == '\0') {
Jan Kadlec's avatar
Jan Kadlec committed
205
		return KNOT_EOK;
Daniel Salzman's avatar
Daniel Salzman committed
206
	}
Jan Včelák's avatar
Jan Včelák committed
207

208
	zone_node_t *next_node = NULL;
209
	const uint8_t *parent = knot_wire_next_label(node->owner, NULL);
210

211
	if (knot_dname_is_equal(zone->apex->owner, parent)) {
212
		node_set_parent(node, zone->apex);
Lubos Slovak's avatar
Lubos Slovak committed
213 214

		// check if the node is not wildcard child of the parent
215
		if (knot_dname_is_wildcard(node->owner)) {
216
			zone->apex->flags |= NODE_FLAGS_WILDCARD_CHILD;
Lubos Slovak's avatar
Lubos Slovak committed
217
		}
218
	} else {
Daniel Salzman's avatar
Daniel Salzman committed
219
		while (parent != NULL && !(next_node = get_node(zone, parent))) {
220 221

			/* Create a new node. */
222
			next_node = node_new(parent, NULL);
223
			if (next_node == NULL) {
Lubos Slovak's avatar
Lubos Slovak committed
224
				return KNOT_ENOMEM;
225 226
			}

227
			/* Insert node to a tree. */
228
			ret = zone_tree_insert(zone->nodes, next_node);
Lubos Slovak's avatar
Lubos Slovak committed
229
			if (ret != KNOT_EOK) {
230
				node_free(next_node, NULL);
231 232 233
				return ret;
			}

234
			/* Update node pointers. */
235
			node_set_parent(node, next_node);
236
			if (knot_dname_is_wildcard(node->owner)) {
237
				next_node->flags |= NODE_FLAGS_WILDCARD_CHILD;
238
			}
Lubos Slovak's avatar
Lubos Slovak committed
239

240
			node = next_node;
241
			parent = knot_wire_next_label(parent, NULL);
242
		}
243

244 245
		// set the found parent (in the zone) as the parent of the last
		// inserted node
246
		assert(node->parent == NULL);
247
		node_set_parent(node, next_node);
248
	}
249

Lubos Slovak's avatar
Lubos Slovak committed
250
	return KNOT_EOK;
251 252
}

253
static int add_nsec3_node(zone_contents_t *zone, zone_node_t *node)
Marek Vavrusa's avatar
Marek Vavrusa committed
254
{
255
	if (zone == NULL || node == NULL) {
Marek Vavrusa's avatar
Marek Vavrusa committed
256 257 258
		return KNOT_EINVAL;
	}

Daniel Salzman's avatar
Daniel Salzman committed
259 260
	int ret = check_node(zone, node);
	if (ret != KNOT_EOK) {
261
		return ret;
Marek Vavrusa's avatar
Marek Vavrusa committed
262 263
	}

264 265
	/* Create NSEC3 tree if not exists. */
	if (zone->nsec3_nodes == NULL) {
266
		zone->nsec3_nodes = zone_tree_create();
267 268 269
		if (zone->nsec3_nodes == NULL) {
			return KNOT_ENOMEM;
		}
Marek Vavrusa's avatar
Marek Vavrusa committed
270 271
	}

272
	// how to know if this is successful??
273
	ret = zone_tree_insert(zone->nsec3_nodes, node);
Marek Vavrusa's avatar
Marek Vavrusa committed
274 275 276 277
	if (ret != KNOT_EOK) {
		return ret;
	}

278 279
	// no parents to be created, the only parent is the zone apex
	// set the apex as the parent of the node
280
	node_set_parent(node, zone->apex);
281 282 283 284

	// cannot be wildcard child, so nothing to be done

	return KNOT_EOK;
Marek Vavrusa's avatar
Marek Vavrusa committed
285 286
}

287
static zone_node_t *get_nsec3_node(const zone_contents_t *zone,
Daniel Salzman's avatar
Daniel Salzman committed
288
                                   const knot_dname_t *name)
289
{
Daniel Salzman's avatar
Daniel Salzman committed
290 291
	assert(zone);
	assert(name);
292

Daniel Salzman's avatar
Daniel Salzman committed
293
	return zone_tree_get(zone->nsec3_nodes, name);
294
}
Marek Vavrusa's avatar
Marek Vavrusa committed
295

Daniel Salzman's avatar
Daniel Salzman committed
296 297
static int insert_rr(zone_contents_t *z, const knot_rrset_t *rr,
                     zone_node_t **n, bool nsec3)
Jan Kadlec's avatar
Jan Kadlec committed
298
{
299
	if (knot_rrset_empty(rr)) {
Jan Kadlec's avatar
Jan Kadlec committed
300 301 302 303
		return KNOT_EINVAL;
	}

	// check if the RRSet belongs to the zone
304
	if (knot_dname_in_bailiwick(rr->owner, z->apex->owner) < 0) {
Jan Kadlec's avatar
Jan Kadlec committed
305 306 307 308
		return KNOT_EOUTOFZONE;
	}

	if (*n == NULL) {
Daniel Salzman's avatar
Daniel Salzman committed
309
		*n = nsec3 ? get_nsec3_node(z, rr->owner) : get_node(z, rr->owner);
Jan Kadlec's avatar
Jan Kadlec committed
310 311
		if (*n == NULL) {
			// Create new, insert
312
			*n = node_new(rr->owner, NULL);
Jan Kadlec's avatar
Jan Kadlec committed
313 314 315
			if (*n == NULL) {
				return KNOT_ENOMEM;
			}
316
			int ret = nsec3 ? add_nsec3_node(z, *n) : add_node(z, *n, true);
Jan Kadlec's avatar
Jan Kadlec committed
317
			if (ret != KNOT_EOK) {
318
				node_free(*n, NULL);
319
				*n = NULL;
Jan Kadlec's avatar
Jan Kadlec committed
320 321 322 323
			}
		}
	}

324
	return node_add_rrset(*n, rr, NULL);
Jan Kadlec's avatar
Jan Kadlec committed
325 326
}

327 328 329 330 331 332 333 334
static int remove_rr(zone_contents_t *z, const knot_rrset_t *rr,
                     zone_node_t **n, bool nsec3)
{
	if (knot_rrset_empty(rr)) {
		return KNOT_EINVAL;
	}

	// check if the RRSet belongs to the zone
335
	if (knot_dname_in_bailiwick(rr->owner, z->apex->owner) < 0) {
336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
		return KNOT_EOUTOFZONE;
	}

	zone_node_t *node;
	if (*n == NULL) {
		node = nsec3 ? get_nsec3_node(z, rr->owner) : get_node(z, rr->owner);
		if (node == NULL) {
			return KNOT_ENONODE;
		}
	} else {
		node = *n;
	}

	knot_rdataset_t *node_rrs = node_rdataset(node, rr->type);
	// Subtract changeset RRS from node RRS.
351
	int ret = knot_rdataset_subtract(node_rrs, &rr->rrs, NULL);
352 353 354 355
	if (ret != KNOT_EOK) {
		return ret;
	}

356
	if (node_rrs->count == 0) {
357 358 359
		// RRSet is empty now, remove it from node, all data freed.
		node_remove_rdataset(node, rr->type);
		// If node is empty now, delete it from zone tree.
360
		if (node->rrset_count == 0 && node != z->apex) {
Daniel Salzman's avatar
Daniel Salzman committed
361
			zone_tree_delete_empty(nsec3 ? z->nsec3_nodes : z->nodes, node);
362 363 364 365 366 367 368
		}
	}

	*n = node;
	return KNOT_EOK;
}

369
static int recreate_normal_tree(const zone_contents_t *z, zone_contents_t *out)
370
{
371
	out->nodes = trie_dup(z->nodes, (trie_dup_cb)node_shallow_copy, NULL);
372 373
	if (out->nodes == NULL) {
		return KNOT_ENOMEM;
374 375
	}

376 377 378
	// everything done, now just update "parent" and "apex" pointers
	out->apex = NULL;
	trie_it_t *itt = trie_it_begin(out->nodes);
379 380
	if (itt == NULL) {
		return KNOT_ENOMEM;
381
	}
382
	while (!trie_it_finished(itt)) {
383 384 385 386 387
		zone_node_t *to_fix = (zone_node_t *)*trie_it_val(itt);
		if (out->apex == NULL && knot_dname_cmp(to_fix->owner, z->apex->owner) == 0) {
			out->apex = to_fix;
		} else {
			const knot_dname_t *parname = knot_wire_next_label(to_fix->owner, NULL);
Libor Peltan's avatar
Libor Peltan committed
388
			zone_node_t *parent = get_node(out, parname);
389 390
			assert(parent != NULL);
			node_set_parent(to_fix, parent);
391
		}
392
		trie_it_next(itt);
393
	}
394
	trie_it_free(itt);
395
	assert(out->apex != NULL);
396

397
	return KNOT_EOK;
398 399
}

400
static int recreate_nsec3_tree(const zone_contents_t *z, zone_contents_t *out)
401
{
402
	out->nsec3_nodes = trie_dup(z->nsec3_nodes, (trie_dup_cb)node_shallow_copy, NULL);
403 404
	if (out->nsec3_nodes == NULL) {
		return KNOT_ENOMEM;
405 406
	}

407
	trie_it_t *itt = trie_it_begin(z->nsec3_nodes);
408 409
	if (itt == NULL) {
		return KNOT_ENOMEM;
410
	}
411
	while (!trie_it_finished(itt)) {
412 413
		zone_node_t *to_fix = (zone_node_t *)*trie_it_val(itt);
		to_fix->parent = out->apex;
414
		trie_it_next(itt);
415
	}
416
	trie_it_free(itt);
Lubos Slovak's avatar
Lubos Slovak committed
417
	return KNOT_EOK;
418 419
}

420 421
// Public API

422 423
int zone_contents_add_rr(zone_contents_t *z, const knot_rrset_t *rr,
                         zone_node_t **n)
Jan Kadlec's avatar
Jan Kadlec committed
424
{
425
	if (z == NULL || rr == NULL || n == NULL) {
426 427 428
		return KNOT_EINVAL;
	}

429 430 431
	return insert_rr(z, rr, n, knot_rrset_is_nsec3rel(rr));
}

432 433 434 435 436 437 438 439 440 441
int zone_contents_remove_rr(zone_contents_t *z, const knot_rrset_t *rr,
                            zone_node_t **n)
{
	if (z == NULL || rr == NULL || n == NULL) {
		return KNOT_EINVAL;
	}

	return remove_rr(z, rr, n, knot_rrset_is_nsec3rel(rr));
}

442 443 444 445 446 447 448 449 450 451 452
zone_node_t *zone_contents_get_node_for_rr(zone_contents_t *zone, const knot_rrset_t *rrset)
{
	if (zone == NULL || rrset == NULL) {
		return NULL;
	}

	const bool nsec3 = knot_rrset_is_nsec3rel(rrset);
	zone_node_t *node = nsec3 ? get_nsec3_node(zone, rrset->owner) :
	                            get_node(zone, rrset->owner);
	if (node == NULL) {
		node = node_new(rrset->owner, NULL);
Dominik Taborsky's avatar
Dominik Taborsky committed
453
		int ret = nsec3 ? add_nsec3_node(zone, node) : add_node(zone, node, true);
454
		if (ret != KNOT_EOK) {
455
			node_free(node, NULL);
456 457 458 459 460 461 462 463 464 465
			return NULL;
		}

		return node;
	} else {
		return node;
	}
}

const zone_node_t *zone_contents_find_node(const zone_contents_t *zone, const knot_dname_t *name)
466
{
Daniel Salzman's avatar
Daniel Salzman committed
467 468 469 470
	if (zone == NULL || name == NULL) {
		return NULL;
	}

471
	return get_node(zone, name);
472 473
}

474 475 476 477 478 479 480 481 482 483 484
zone_node_t *zone_contents_find_node_for_rr(zone_contents_t *contents, const knot_rrset_t *rrset)
{
	if (contents == NULL || rrset == NULL) {
		return NULL;
	}

	const bool nsec3 = knot_rrset_is_nsec3rel(rrset);
	return nsec3 ? get_nsec3_node(contents, rrset->owner) :
	               get_node(contents, rrset->owner);
}

485
int zone_contents_find_dname(const zone_contents_t *zone,
486
                             const knot_dname_t *name,
487 488
                             const zone_node_t **match,
                             const zone_node_t **closest,
489
                             const zone_node_t **previous)
490
{
Libor Peltan's avatar
Libor Peltan committed
491
	if (!zone || !name || !match || !closest) {
Marek Vavrusa's avatar
Marek Vavrusa committed
492
		return KNOT_EINVAL;
493 494
	}

495
	if (knot_dname_in_bailiwick(name, zone->apex->owner) < 0) {
496
		return KNOT_EOUTOFZONE;
497 498
	}

499 500 501 502 503 504 505
	zone_node_t *node = NULL;
	zone_node_t *prev = NULL;

	int found = zone_tree_get_less_or_equal(zone->nodes, name, &node, &prev);
	if (found < 0) {
		// error
		return found;
Libor Peltan's avatar
Libor Peltan committed
506
	} else if (found == 1 && previous != NULL) {
507
		// exact match
Lubos Slovak's avatar
Lubos Slovak committed
508

509 510 511 512 513 514
		assert(node && prev);

		*match = node;
		*closest = node;
		*previous = prev;

Libor Peltan's avatar
Libor Peltan committed
515 516 517 518 519 520 521 522
		return ZONE_NAME_FOUND;
	} else if (found == 1 && previous == NULL) {
		// exact match, zone not adjusted yet

		assert(node);
		*match = node;
		*closest = node;

523
		return ZONE_NAME_FOUND;
Lubos Slovak's avatar
Lubos Slovak committed
524
	} else {
525
		// closest match
526

527
		assert(!node && prev);
Jan Kadlec's avatar
Jan Kadlec committed
528

529
		node = prev;
530
		size_t matched_labels = knot_dname_matched_labels(node->owner, name);
531 532 533
		while (matched_labels < knot_dname_labels(node->owner, NULL)) {
			node = node->parent;
			assert(node);
Jan Kadlec's avatar
Jan Kadlec committed
534
		}
535

536 537
		*match = NULL;
		*closest = node;
Libor Peltan's avatar
Libor Peltan committed
538 539 540
		if (previous != NULL) {
			*previous = prev;
		}
541 542 543

		return ZONE_NAME_NOT_FOUND;
	}
544 545
}

546
const zone_node_t *zone_contents_find_nsec3_node(const zone_contents_t *zone,
547
                                                 const knot_dname_t *name)
548
{
Daniel Salzman's avatar
Daniel Salzman committed
549 550 551 552
	if (zone == NULL || name == NULL) {
		return NULL;
	}

553
	return get_nsec3_node(zone, name);
554 555
}

556
int zone_contents_find_nsec3_for_name(const zone_contents_t *zone,
557 558 559
                                      const knot_dname_t *name,
                                      const zone_node_t **nsec3_node,
                                      const zone_node_t **nsec3_previous)
560
{
Daniel Salzman's avatar
Daniel Salzman committed
561 562
	if (zone == NULL || name == NULL || nsec3_node == NULL ||
	    nsec3_previous == NULL) {
Marek Vavrusa's avatar
Marek Vavrusa committed
563
		return KNOT_EINVAL;
564 565
	}

566
	// check if the NSEC3 tree is not empty
567
	if (zone_tree_is_empty(zone->nsec3_nodes)) {
568 569
		return KNOT_ENSEC3CHAIN;
	}
Libor Peltan's avatar
Libor Peltan committed
570 571 572
	if (!knot_is_nsec3_enabled(zone)) {
		return KNOT_ENSEC3PAR;
	}
573

574
	uint8_t nsec3_name[KNOT_DNAME_MAXLEN];
Libor Peltan's avatar
Libor Peltan committed
575 576
	int ret = knot_create_nsec3_owner(nsec3_name, sizeof(nsec3_name),
	                                  name, zone->apex->owner, &zone->nsec3_params);
Lubos Slovak's avatar
Lubos Slovak committed
577
	if (ret != KNOT_EOK) {
578 579 580
		return ret;
	}

581 582 583 584 585 586 587 588
	return zone_contents_find_nsec3(zone, nsec3_name, nsec3_node, nsec3_previous);
}

int zone_contents_find_nsec3(const zone_contents_t *zone,
                             const knot_dname_t *nsec3_name,
                             const zone_node_t **nsec3_node,
                             const zone_node_t **nsec3_previous)
{
589 590
	zone_node_t *found = NULL, *prev = NULL;
	bool match = find_in_tree(zone->nsec3_nodes, nsec3_name, &found, &prev);
Lubos Slovak's avatar
Lubos Slovak committed
591

592 593 594 595 596 597
	*nsec3_node = found;

	if (prev == NULL) {
		// either the returned node is the root of the tree, or it is
		// the leftmost node in the tree; in both cases node was found
		// set the previous node of the found node
598
		assert(match);
599
		assert(*nsec3_node != NULL);
600
		*nsec3_previous = (*nsec3_node)->prev;
601 602 603 604
	} else {
		*nsec3_previous = prev;
	}

605
	// The previous may be from wrong NSEC3 chain. Search for previous from the right chain.
606
	const zone_node_t *original_prev = *nsec3_previous;
607
	while (!((*nsec3_previous)->flags & NODE_FLAGS_IN_NSEC3_CHAIN)) {
608
		*nsec3_previous = (*nsec3_previous)->prev;
609
		if (*nsec3_previous == original_prev || *nsec3_previous == NULL) {
610 611 612 613
			// cycle
			*nsec3_previous = NULL;
			break;
		}
614
	}
Jan Včelák's avatar
Jan Včelák committed
615

616
	return (match ? ZONE_NAME_FOUND : ZONE_NAME_NOT_FOUND);
617 618
}

619 620
const zone_node_t *zone_contents_find_wildcard_child(const zone_contents_t *contents,
                                                     const zone_node_t *parent)
621
{
622
	if (contents == NULL || parent == NULL || parent->owner == NULL) {
623 624 625
		return NULL;
	}

626 627
	knot_dname_t wildcard[KNOT_DNAME_MAXLEN] = { 0x01, '*' };
	knot_dname_to_wire(wildcard + 2, parent->owner, KNOT_DNAME_MAXLEN - 2);
Daniel Salzman's avatar
Daniel Salzman committed
628

629
	return zone_contents_find_node(contents, wildcard);
630 631
}

632 633 634 635 636 637 638 639 640 641 642 643 644
bool zone_contents_find_node_or_wildcard(const zone_contents_t *contents,
                                         const knot_dname_t *find,
                                         const zone_node_t **found)
{
	const zone_node_t *encloser = NULL;
	zone_contents_find_dname(contents, find, found, &encloser, NULL);
	if (*found == NULL && encloser != NULL && (encloser->flags & NODE_FLAGS_WILDCARD_CHILD)) {
		*found = zone_contents_find_wildcard_child(contents, encloser);
		assert(*found != NULL);
	}
	return (*found != NULL);
}

645 646
int zone_contents_apply(zone_contents_t *contents,
                        zone_contents_apply_cb_t function, void *data)
647
{
648
	if (contents == NULL) {
Marek Vavrusa's avatar
Marek Vavrusa committed
649
		return KNOT_EINVAL;
650 651
	}

Daniel Salzman's avatar
Daniel Salzman committed
652 653 654 655
	zone_tree_func_t f = {
		.func = function,
		.data = data
	};
656

657
	return zone_tree_apply(contents->nodes, tree_apply_cb, &f);
658 659
}

660 661
int zone_contents_nsec3_apply(zone_contents_t *contents,
                              zone_contents_apply_cb_t function, void *data)
662
{
663
	if (contents == NULL) {
Marek Vavrusa's avatar
Marek Vavrusa committed
664
		return KNOT_EINVAL;
665 666
	}

Daniel Salzman's avatar
Daniel Salzman committed
667 668 669 670
	zone_tree_func_t f = {
		.func = function,
		.data = data
	};
671

672
	return zone_tree_apply(contents->nsec3_nodes, tree_apply_cb, &f);
673 674
}

675
int zone_contents_shallow_copy(const zone_contents_t *from, zone_contents_t **to)
676
{
677 678 679
	if (from == NULL || to == NULL) {
		return KNOT_EINVAL;
	}
680

681 682 683 684
	/* Copy to same destination as source. */
	if (from == *to) {
		return KNOT_EINVAL;
	}
685

686
	zone_contents_t *contents = calloc(1, sizeof(zone_contents_t));
687 688 689
	if (contents == NULL) {
		return KNOT_ENOMEM;
	}
690

691 692
	int ret = recreate_normal_tree(from, contents);
	if (ret != KNOT_EOK) {
693
		zone_tree_free(&contents->nodes);
694 695
		free(contents);
		return ret;
696
	}
697

698
	if (from->nsec3_nodes) {
699 700
		ret = recreate_nsec3_tree(from, contents);
		if (ret != KNOT_EOK) {
701 702
			zone_tree_free(&contents->nodes);
			zone_tree_free(&contents->nsec3_nodes);
703 704 705 706 707 708
			free(contents);
			return ret;
		}
	} else {
		contents->nsec3_nodes = NULL;
	}
709

710 711 712
	*to = contents;
	return KNOT_EOK;
}
713

714
void zone_contents_free(zone_contents_t *contents)
715
{
716
	if (contents == NULL) {
717 718 719 720
		return;
	}

	// free the zone tree, but only the structure
721 722
	zone_tree_free(&contents->nodes);
	zone_tree_free(&contents->nsec3_nodes);
723

724
	dnssec_nsec3_params_free(&contents->nsec3_params);
Jan Včelák's avatar
Jan Včelák committed
725

726
	free(contents);
727 728
}

729
void zone_contents_deep_free(zone_contents_t *contents)
730
{
731
	if (contents == NULL) {
732 733 734
		return;
	}

735
	if (contents != NULL) {
736
		// Delete NSEC3 tree.
737
		(void)zone_tree_apply(contents->nsec3_nodes,
Daniel Salzman's avatar
Daniel Salzman committed
738
		                      destroy_node_rrsets_from_tree, NULL);
739

740
		// Delete the normal tree.
741
		(void)zone_tree_apply(contents->nodes,
Daniel Salzman's avatar
Daniel Salzman committed
742
		                      destroy_node_rrsets_from_tree, NULL);
743 744
	}

745
	zone_contents_free(contents);
746 747
}

748
uint32_t zone_contents_serial(const zone_contents_t *zone)
749 750
{
	if (zone == NULL) {
751
		return 0;
752 753
	}

754
	const knot_rdataset_t *soa = node_rdataset(zone->apex, KNOT_RRTYPE_SOA);
755 756 757 758
	if (soa == NULL) {
		return 0;
	}

759
	return knot_soa_serial(soa->rdata);
760
}
761

762 763 764 765
void zone_contents_set_soa_serial(zone_contents_t *zone, uint32_t new_serial)
{
	knot_rdataset_t *soa;
	if (zone != NULL && (soa = node_rdataset(zone->apex, KNOT_RRTYPE_SOA)) != NULL) {
766
		knot_soa_serial_set(soa->rdata, new_serial);
767 768 769
	}
}

770 771
bool zone_contents_is_empty(const zone_contents_t *zone)
{
772 773 774 775 776 777 778 779 780
	if (zone == NULL) {
		return true;
	}

	bool apex_empty = (zone->apex == NULL || zone->apex->rrset_count == 0);
	bool no_non_apex = (zone_tree_count(zone->nodes) <= (zone->apex != NULL ? 1 : 0));
	bool no_nsec3 = zone_tree_is_empty(zone->nsec3_nodes);

	return (apex_empty && no_non_apex && no_nsec3);
781
}
782 783 784 785

size_t zone_contents_measure_size(zone_contents_t *zone)
{
	zone->size = 0;
786
	zone_contents_apply(zone, measure_size, &zone->size);
787 788
	return zone->size;
}
789 790 791 792 793 794 795

uint32_t zone_contents_max_ttl(zone_contents_t *zone)
{
	zone->max_ttl = 0;
	zone_contents_apply(zone, measure_max_ttl, &zone->size);
	return zone->max_ttl;
}