changesets.c 19.7 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
#include <assert.h>
Daniel Salzman's avatar
Daniel Salzman committed
18
#include <stdlib.h>
19
#include <stdarg.h>
20

21
#include "knot/updates/changesets.h"
22
#include "knot/updates/apply.h"
Daniel Salzman's avatar
Daniel Salzman committed
23
#include "libknot/libknot.h"
24
#include "knot/zone/zone-dump.h"
25
#include "contrib/macros.h"
26
#include "contrib/mempattern.h"
27

28
static int handle_soa(knot_rrset_t **soa, const knot_rrset_t *rrset)
29
{
30 31
	assert(soa);
	assert(rrset);
32

33
	if (*soa != NULL) {
34
		knot_rrset_free(*soa, NULL);
35 36
	}

37 38 39 40 41 42 43 44 45 46 47
	*soa = knot_rrset_copy(rrset, NULL);
	if (*soa == NULL) {
		return KNOT_ENOMEM;
	}

	return KNOT_EOK;
}

/*! \brief Adds RRSet to given zone. */
static int add_rr_to_contents(zone_contents_t *z, const knot_rrset_t *rrset)
{
48 49
	zone_node_t *n = NULL;
	int ret = zone_contents_add_rr(z, rrset, &n);
50
	UNUSED(n);
51 52 53

	// We don't care of TTLs.
	return ret == KNOT_ETTL ? KNOT_EOK : ret;
54 55
}

56
/*! \brief Cleans up trie iterations. */
57
static void cleanup_iter_list(list_t *l)
58
{
59 60
	ptrnode_t *n, *nxt;
	WALK_LIST_DELSAFE(n, nxt, *l) {
61 62
		trie_it_t *it = (trie_it_t *)n->d;
		trie_it_free(it);
63 64 65 66 67 68
		rem_node(&n->n);
		free(n);
	}
	init_list(l);
}

69
/*! \brief Inits changeset iterator with given tries. */
70
static int changeset_iter_init(changeset_iter_t *ch_it, size_t tries, ...)
71 72 73 74 75 76 77 78
{
	memset(ch_it, 0, sizeof(*ch_it));
	init_list(&ch_it->iters);

	va_list args;
	va_start(args, tries);

	for (size_t i = 0; i < tries; ++i) {
79
		trie_t *t = va_arg(args, trie_t *);
80 81 82 83
		if (t == NULL) {
			continue;
		}

84
		trie_it_t *it = trie_it_begin(t);
85 86 87 88 89 90 91 92 93 94
		if (it == NULL) {
			cleanup_iter_list(&ch_it->iters);
			va_end(args);
			return KNOT_ENOMEM;
		}

		if (ptrlist_add(&ch_it->iters, it, NULL) == NULL) {
			cleanup_iter_list(&ch_it->iters);
			va_end(args);
			return KNOT_ENOMEM;
95 96 97 98 99 100 101 102
		}
	}

	va_end(args);

	return KNOT_EOK;
}

103
/*! \brief Gets next node from trie iterators. */
104
static void iter_next_node(changeset_iter_t *ch_it, trie_it_t *t_it)
105
{
106
	assert(!trie_it_finished(t_it));
107 108
	// Get next node, but not for the very first call.
	if (ch_it->node) {
109
		trie_it_next(t_it);
110
	}
111
	if (trie_it_finished(t_it)) {
112 113 114 115
		ch_it->node = NULL;
		return;
	}

116
	ch_it->node = (zone_node_t *)*trie_it_val(t_it);
117 118 119
	assert(ch_it->node);
	while (ch_it->node && ch_it->node->rrset_count == 0) {
		// Skip empty non-terminals.
120 121
		trie_it_next(t_it);
		if (trie_it_finished(t_it)) {
122 123
			ch_it->node = NULL;
		} else {
124
			ch_it->node = (zone_node_t *)*trie_it_val(t_it);
125 126 127
			assert(ch_it->node);
		}
	}
128

129 130 131
	ch_it->node_pos = 0;
}

132
/*! \brief Gets next RRSet from trie iterators. */
133
static knot_rrset_t get_next_rr(changeset_iter_t *ch_it, trie_it_t *t_it)
134 135 136 137
{
	if (ch_it->node == NULL || ch_it->node_pos == ch_it->node->rrset_count) {
		iter_next_node(ch_it, t_it);
		if (ch_it->node == NULL) {
138
			assert(trie_it_finished(t_it));
139 140 141 142 143 144 145 146 147
			knot_rrset_t rr;
			knot_rrset_init_empty(&rr);
			return rr;
		}
	}

	return node_rrset_at(ch_it->node, ch_it->node_pos++);
}

148 149 150
// removes from counterpart what is in rr.
// fixed_rr is an output parameter, holding a copy of rr without what has been removed from counterpart
static void check_redundancy(zone_contents_t *counterpart, const knot_rrset_t *rr, knot_rrset_t **fixed_rr)
151
{
152 153 154 155
	if (fixed_rr != NULL) {
		*fixed_rr = knot_rrset_copy(rr, NULL);
	}

156 157
	zone_node_t *node = zone_contents_find_node_for_rr(counterpart, rr);
	if (node == NULL) {
158
		return;
159 160 161
	}

	if (!node_rrtype_exists(node, rr->type)) {
162
		return;
163 164 165
	}

	// Subtract the data from node's RRSet.
166
	knot_rdataset_t *rrs = node_rdataset(node, rr->type);
167
	uint32_t rrs_ttl = node_rrset(node, rr->type).ttl;
168

169 170
	if (fixed_rr != NULL && *fixed_rr != NULL &&
	    ((*fixed_rr)->ttl == rrs_ttl || rr->type == KNOT_RRTYPE_RRSIG)) {
171
		int ret = knot_rdataset_subtract(&(*fixed_rr)->rrs, rrs, NULL);
172 173 174 175 176
		if (ret != KNOT_EOK) {
			return;
		}
	}

177 178
	// TTL of RRSIGs is better determined by original_ttl field, which is compared as part of rdata anyway
	if (rr->ttl == rrs_ttl || rr->type == KNOT_RRTYPE_RRSIG) {
179 180 181 182
		int ret = knot_rdataset_subtract(rrs, &rr->rrs, NULL);
		if (ret != KNOT_EOK) {
			return;
		}
183 184
	}

185
	if (knot_rdataset_size(rrs) == 0) {
186 187 188
		// Remove empty type.
		node_remove_rdataset(node, rr->type);

189
		if (node->rrset_count == 0 && node != counterpart->apex) {
190 191
			// Remove empty node.
			zone_tree_t *t = knot_rrset_is_nsec3rel(rr) ?
Daniel Salzman's avatar
Daniel Salzman committed
192 193
			                 counterpart->nsec3_nodes : counterpart->nodes;
			zone_tree_delete_empty(t, node);
194
		}
195 196
	}

197
	return;
198 199
}

200 201 202
int changeset_init(changeset_t *ch, const knot_dname_t *apex)
{
	memset(ch, 0, sizeof(changeset_t));
203

204 205
	// Init local changes
	ch->add = zone_contents_new(apex);
206 207 208
	if (ch->add == NULL) {
		return KNOT_ENOMEM;
	}
209
	ch->remove = zone_contents_new(apex);
210
	if (ch->remove == NULL) {
211
		zone_contents_free(ch->add);
212 213
		return KNOT_ENOMEM;
	}
Jan Kadlec's avatar
Jan Kadlec committed
214

215
	return KNOT_EOK;
216 217
}

218
changeset_t *changeset_new(const knot_dname_t *apex)
219
{
220
	changeset_t *ret = malloc(sizeof(changeset_t));
221
	if (ret == NULL) {
222 223 224
		return NULL;
	}

225 226 227 228 229 230
	if (changeset_init(ret, apex) == KNOT_EOK) {
		return ret;
	} else {
		free(ret);
		return NULL;
	}
231 232
}

233
bool changeset_empty(const changeset_t *ch)
234
{
235
	if (ch == NULL || ch->add == NULL || ch->remove == NULL) {
236
		return true;
237 238
	}

239 240 241
	if (ch->soa_to) {
		return false;
	}
242

243
	changeset_iter_t itt;
244
	changeset_iter_all(&itt, ch);
245

246 247
	knot_rrset_t rr = changeset_iter_next(&itt);
	changeset_iter_clear(&itt);
248 249

	return knot_rrset_empty(&rr);
250 251
}

252
size_t changeset_size(const changeset_t *ch)
253
{
254
	if (ch == NULL) {
255 256
		return 0;
	}
257

258
	changeset_iter_t itt;
259
	changeset_iter_all(&itt, ch);
260 261

	size_t size = 0;
262
	knot_rrset_t rr = changeset_iter_next(&itt);
263 264
	while(!knot_rrset_empty(&rr)) {
		++size;
265
		rr = changeset_iter_next(&itt);
266
	}
267
	changeset_iter_clear(&itt);
268

Jan Kadlec's avatar
Jan Kadlec committed
269 270 271 272 273 274 275
	if (!knot_rrset_empty(ch->soa_from)) {
		size += 1;
	}
	if (!knot_rrset_empty(ch->soa_to)) {
		size += 1;
	}

276
	return size;
277 278
}

279
int changeset_add_addition(changeset_t *ch, const knot_rrset_t *rrset, changeset_flag_t flags)
280
{
281 282 283 284 285 286 287 288 289
	if (!ch || !rrset) {
		return KNOT_EINVAL;
	}

	if (rrset->type == KNOT_RRTYPE_SOA) {
		/* Do not add SOAs into actual contents. */
		return handle_soa(&ch->soa_to, rrset);
	}

290 291
	knot_rrset_t *rrset_cancelout = NULL;

292 293
	/* Check if there's any removal and remove that, then add this
	 * addition anyway. Required to change TTLs. */
294
	if (flags & CHANGESET_CHECK) {
295 296 297 298 299 300
		/* If we delete the rrset, we need to hold a copy to add it later */
		rrset = knot_rrset_copy(rrset, NULL);
		if (rrset == NULL) {
			return KNOT_ENOMEM;
		}

301 302
		check_redundancy(ch->remove, rrset,
				 ((flags & CHANGESET_CHECK_CANCELOUT) ? &rrset_cancelout : NULL));
303
	}
304

305 306
	const knot_rrset_t *to_add = (rrset_cancelout == NULL ? rrset : rrset_cancelout);
	int ret = knot_rrset_empty(to_add) ? KNOT_EOK : add_rr_to_contents(ch->add, to_add);
307

308
	if (flags & CHANGESET_CHECK) {
309
		knot_rrset_free((knot_rrset_t *)rrset, NULL);
310
	}
311
	knot_rrset_free(rrset_cancelout, NULL);
312 313

	return ret;
314 315
}

316
int changeset_add_removal(changeset_t *ch, const knot_rrset_t *rrset, changeset_flag_t flags)
317
{
318 319 320 321 322 323 324 325 326
	if (!ch || !rrset) {
		return KNOT_EINVAL;
	}

	if (rrset->type == KNOT_RRTYPE_SOA) {
		/* Do not add SOAs into actual contents. */
		return handle_soa(&ch->soa_from, rrset);
	}

327 328
	knot_rrset_t *rrset_cancelout = NULL;

329 330
	/* Check if there's any addition and remove that, then add this
	 * removal anyway. */
331
	if (flags & CHANGESET_CHECK) {
332 333 334 335 336 337
		/* If we delete the rrset, we need to hold a copy to add it later */
		rrset = knot_rrset_copy(rrset, NULL);
		if (rrset == NULL) {
			return KNOT_ENOMEM;
		}

338 339
		check_redundancy(ch->add, rrset,
				 ((flags & CHANGESET_CHECK_CANCELOUT) ? &rrset_cancelout : NULL));
340
	}
341

342
	const knot_rrset_t *to_remove = (rrset_cancelout == NULL ? rrset : rrset_cancelout);
343
	int ret = (knot_rrset_empty(to_remove) || ch->remove == NULL) ? KNOT_EOK : add_rr_to_contents(ch->remove, to_remove);
344

345
	if (flags & CHANGESET_CHECK) {
346
		knot_rrset_free((knot_rrset_t *)rrset, NULL);
347
	}
348
	knot_rrset_free(rrset_cancelout, NULL);
349

350
	return ret;
351 352
}

353 354 355 356 357
int changeset_remove_addition(changeset_t *ch, const knot_rrset_t *rrset)
{
	if (rrset->type == KNOT_RRTYPE_SOA) {
		/* Do not add SOAs into actual contents. */
		if (ch->soa_to != NULL) {
358
			knot_rrset_free(ch->soa_to, NULL);
359 360 361 362 363 364 365 366 367 368 369 370 371 372
			ch->soa_to = NULL;
		}
		return KNOT_EOK;
	}

	zone_node_t *n = NULL;
	return zone_contents_remove_rr(ch->add, rrset, &n);
}

int changeset_remove_removal(changeset_t *ch, const knot_rrset_t *rrset)
{
	if (rrset->type == KNOT_RRTYPE_SOA) {
		/* Do not add SOAs into actual contents. */
		if (ch->soa_from != NULL) {
373
			knot_rrset_free(ch->soa_from, NULL);
374 375 376 377 378 379 380 381 382
			ch->soa_from = NULL;
		}
		return KNOT_EOK;
	}

	zone_node_t *n = NULL;
	return zone_contents_remove_rr(ch->remove, rrset, &n);
}

383
int changeset_merge(changeset_t *ch1, const changeset_t *ch2, int flags)
384
{
385
	changeset_iter_t itt;
386
	changeset_iter_rem(&itt, ch2);
387

388
	knot_rrset_t rrset = changeset_iter_next(&itt);
389
	while (!knot_rrset_empty(&rrset)) {
390
		int ret = changeset_add_removal(ch1, &rrset, CHANGESET_CHECK | flags);
391
		if (ret != KNOT_EOK) {
392 393
			changeset_iter_clear(&itt);
			return ret;
394
		}
395
		rrset = changeset_iter_next(&itt);
396
	}
397
	changeset_iter_clear(&itt);
398

399
	changeset_iter_add(&itt, ch2);
400

401
	rrset = changeset_iter_next(&itt);
402
	while (!knot_rrset_empty(&rrset)) {
403
		int ret = changeset_add_addition(ch1, &rrset, CHANGESET_CHECK | flags);
404
		if (ret != KNOT_EOK) {
405 406
			changeset_iter_clear(&itt);
			return ret;
407
		}
408
		rrset = changeset_iter_next(&itt);
409
	}
410
	changeset_iter_clear(&itt);
411

412 413
	// Use soa_to and serial from the second changeset
	// soa_to from the first changeset is redundant, delete it
414 415 416 417
	if (ch2->soa_to == NULL && ch2->soa_from == NULL) {
		// but not if ch2 has no soa change
		return KNOT_EOK;
	}
418
	knot_rrset_t *soa_copy = knot_rrset_copy(ch2->soa_to, NULL);
Jan Kadlec's avatar
Jan Kadlec committed
419
	if (soa_copy == NULL && ch2->soa_to) {
420 421
		return KNOT_ENOMEM;
	}
422
	knot_rrset_free(ch1->soa_to, NULL);
423
	ch1->soa_to = soa_copy;
424 425

	return KNOT_EOK;
426 427
}

428 429 430 431 432 433 434 435 436 437
uint32_t changeset_from(const changeset_t *ch)
{
	return ch->soa_from == NULL ? 0 : knot_soa_serial(ch->soa_from->rrs.rdata);
}

uint32_t changeset_to(const changeset_t *ch)
{
	return ch->soa_to == NULL ? 0 : knot_soa_serial(ch->soa_to->rrs.rdata);
}

438 439 440 441 442 443 444 445 446 447 448 449 450 451
typedef struct {
	const zone_contents_t *zone;
	changeset_t *fixing;
	knot_mm_t *mm;
} preapply_fix_ctx;

static int preapply_fix_rrset(const knot_rrset_t *apply, bool adding, void *data)
{
	preapply_fix_ctx *ctx  = (preapply_fix_ctx *)data;
	const zone_node_t *znode = zone_contents_find_node(ctx->zone, apply->owner);
	const knot_rdataset_t *zrdataset = node_rdataset(znode, apply->type);
	if (adding && zrdataset == NULL) {
		return KNOT_EOK;
	}
452

453 454
	knot_rrset_t *fixrrset;
	if (adding) {
455 456
		fixrrset = knot_rrset_new(apply->owner, apply->type, apply->rclass,
		                          apply->ttl, ctx->mm);
457 458 459 460 461 462
	} else {
		fixrrset = knot_rrset_copy(apply, ctx->mm);
	}
	if (fixrrset == NULL) {
		return KNOT_ENOMEM;
	}
463 464

	int ret = KNOT_EOK;
465 466 467
	if (adding) {
		ret = knot_rdataset_intersect(zrdataset, &apply->rrs, &fixrrset->rrs, ctx->mm);
	} else {
468 469 470
		uint32_t zrrset_ttl = node_rrset(znode, apply->type).ttl;
		if (zrdataset != NULL && fixrrset->ttl == zrrset_ttl) {
			ret = knot_rdataset_subtract(&fixrrset->rrs, zrdataset, ctx->mm);
471 472 473 474 475 476 477 478 479
		}
	}
	if (ret == KNOT_EOK && !knot_rrset_empty(fixrrset)) {
		if (adding) {
			ret = changeset_add_removal(ctx->fixing, fixrrset, 0);
		} else {
			ret = changeset_add_addition(ctx->fixing, fixrrset, 0);
		}
	}
480

481
	knot_rrset_free(fixrrset, ctx->mm);
482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499
	return ret;
}

static int subtract_callback(const knot_rrset_t *rrset, bool addition, void *subtractfrom)
{
	changeset_t *chsf = (changeset_t *)subtractfrom;
	if (addition) {
		return changeset_remove_removal(chsf, rrset);
	} else {
		return changeset_remove_addition(chsf, rrset);
	}
}

static int subtract(changeset_t *from, const changeset_t *what)
{
	return changeset_walk(what, subtract_callback, (void *)from);
}

500
int changeset_preapply_fix(const zone_contents_t *zone, changeset_t *ch)
501
{
502
	if (zone == NULL || ch == NULL) {
503 504 505 506 507 508 509 510 511 512
		return KNOT_EINVAL;
	}

	changeset_t fixing;
	int ret = changeset_init(&fixing, zone->apex->owner);
	if (ret != KNOT_EOK) {
		return ret;
	}

	preapply_fix_ctx ctx = { .zone = zone, .fixing = &fixing, .mm = NULL };
513
	ret = changeset_walk(ch, preapply_fix_rrset, (void *)&ctx);
514
	if (ret == KNOT_EOK) {
515
		ret = subtract(ch, &fixing);
516 517 518 519 520
	}
	changeset_clear(&fixing);
	return ret;
}

521
int changeset_cancelout(changeset_t *ch)
522
{
523
	if (ch == NULL) {
524 525 526 527
		return KNOT_EINVAL;
	}

	changeset_t fixing;
528
	int ret = changeset_init(&fixing, ch->add->apex->owner);
529 530 531 532
	if (ret != KNOT_EOK) {
		return ret;
	}

533 534
	preapply_fix_ctx ctx = { .zone = ch->remove, .fixing = &fixing, .mm = NULL };
	ret = changeset_walk(ch, preapply_fix_rrset, (void *)&ctx);
535 536 537 538
	if (ret == KNOT_EOK) {
		assert(zone_contents_is_empty(fixing.add));
		zone_contents_t *fixing_add_bck = fixing.add;
		fixing.add = fixing.remove;
539
		ret = subtract(ch, &fixing);
540 541 542 543 544 545
		fixing.add = fixing_add_bck;
	}
	changeset_clear(&fixing);
	return ret;
}

546 547 548 549 550 551 552
bool changeset_differs_just_serial(const changeset_t *ch)
{
	if (ch == NULL || ch->soa_from == NULL || ch->soa_to == NULL) {
		return false;
	}

	knot_rrset_t *soa_to_cpy = knot_rrset_copy(ch->soa_to, NULL);
553
	knot_soa_serial_set(soa_to_cpy->rrs.rdata, knot_soa_serial(ch->soa_from->rrs.rdata));
554

555
	bool ret = knot_rrset_equal(ch->soa_from, soa_to_cpy, KNOT_RRSET_COMPARE_WHOLE);
556 557
	knot_rrset_free(soa_to_cpy, NULL);

558 559 560 561 562 563 564 565 566 567 568 569 570 571
	changeset_iter_t itt;
	changeset_iter_all(&itt, ch);

	knot_rrset_t rrset = changeset_iter_next(&itt);
	while (!knot_rrset_empty(&rrset) && ret) {
		if (rrset.type != KNOT_RRTYPE_RRSIG || rrset.rrs.count != 1 ||
		    knot_rrsig_type_covered(rrset.rrs.rdata) != KNOT_RRTYPE_SOA) {
			ret = false;
		}
		rrset = changeset_iter_next(&itt);
	}
	changeset_iter_clear(&itt);

	return ret;
572 573
}

574
int changeset_to_contents(changeset_t *ch, zone_contents_t **out)
575 576 577
{
	assert(ch->soa_from == NULL);
	assert(zone_contents_is_empty(ch->remove));
578
	assert(out != NULL);
579

580 581
	*out = ch->add;
	int ret = add_rr_to_contents(*out, ch->soa_to);
582
	knot_rrset_free(ch->soa_to, NULL);
583
	if (ret != KNOT_EOK) {
584
		zone_contents_deep_free(*out);
585
	}
586

587
	zone_contents_deep_free(ch->remove);
588
	return ret;
589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604
}

changeset_t *changeset_from_contents(const zone_contents_t *contents)
{
	zone_contents_t *copy = NULL;
	if (zone_contents_shallow_copy(contents, &copy) != KNOT_EOK) {
		return NULL;
	}

	changeset_t *res = changeset_new(copy->apex->owner);

	knot_rrset_t soa_rr = node_rrset(copy->apex, KNOT_RRTYPE_SOA);;
	res->soa_to = knot_rrset_copy(&soa_rr, NULL);

	node_remove_rdataset(copy->apex, KNOT_RRTYPE_SOA);

605
	zone_contents_deep_free(res->add);
606
	res->add = copy;
607 608
	zone_contents_deep_free(res->remove);
	res->remove = NULL;
609 610 611 612 613 614 615
	return res;
}

void changeset_from_contents_free(changeset_t *ch)
{
	assert(ch);
	assert(ch->soa_from == NULL);
616
	assert(ch->remove == NULL);
617

618
	update_free_zone(ch->add);
619

620
	zone_contents_deep_free(ch->remove);
621 622
	knot_rrset_free(ch->soa_from, NULL);
	knot_rrset_free(ch->soa_to, NULL);
623 624 625 626
	free(ch->data);
	free(ch);
}

627 628 629 630 631 632 633 634 635 636 637 638 639
void changesets_clear(list_t *chgs)
{
	if (chgs) {
		changeset_t *chg, *nxt;
		WALK_LIST_DELSAFE(chg, nxt, *chgs) {
			changeset_clear(chg);
			rem_node(&chg->n);
		}
		init_list(chgs);
	}
}

void changesets_free(list_t *chgs)
640
{
641
	if (chgs) {
642 643
		changeset_t *chg, *nxt;
		WALK_LIST_DELSAFE(chg, nxt, *chgs) {
644
			rem_node(&chg->n);
645
			changeset_free(chg);
646
		}
647
		init_list(chgs);
648
	}
649 650
}

651
void changeset_clear(changeset_t *ch)
652
{
653 654
	if (ch == NULL) {
		return;
655
	}
656

657
	// Delete RRSets in lists, in case there are any left
658 659 660 661
	zone_contents_deep_free(ch->add);
	zone_contents_deep_free(ch->remove);
	ch->add = NULL;
	ch->remove = NULL;
662

663 664 665 666
	knot_rrset_free(ch->soa_from, NULL);
	knot_rrset_free(ch->soa_to, NULL);
	ch->soa_from = NULL;
	ch->soa_to = NULL;
667

668 669 670
	// Delete binary data
	free(ch->data);
}
671

672 673 674 675 676 677
changeset_t *changeset_clone(const changeset_t *ch)
{
	if (ch == NULL) {
		return NULL;
	}

678
	changeset_t *res = changeset_new(ch->add->apex->owner);
679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714
	if (res == NULL) {
		return NULL;
	}

	res->soa_from = knot_rrset_copy(ch->soa_from, NULL);
	res->soa_to = knot_rrset_copy(ch->soa_to, NULL);

	int ret = KNOT_EOK;
	changeset_iter_t itt;

	changeset_iter_rem(&itt, ch);
	knot_rrset_t rr = changeset_iter_next(&itt);
	while (!knot_rrset_empty(&rr) && ret == KNOT_EOK) {
		ret = changeset_add_removal(res, &rr, 0);
		rr = changeset_iter_next(&itt);
	}
	changeset_iter_clear(&itt);

	changeset_iter_add(&itt, ch);
	rr = changeset_iter_next(&itt);
	while (!knot_rrset_empty(&rr) && ret == KNOT_EOK) {
		ret = changeset_add_addition(res, &rr, 0);
		rr = changeset_iter_next(&itt);
	}
	changeset_iter_clear(&itt);

	if ((ch->soa_from != NULL && res->soa_from == NULL) ||
	    (ch->soa_to != NULL && res->soa_to == NULL) ||
	    ret != KNOT_EOK) {
		changeset_free(res);
		return NULL;
	}

	return res;
}

715 716 717 718
void changeset_free(changeset_t *ch)
{
	changeset_clear(ch);
	free(ch);
719 720
}

721
int changeset_iter_add(changeset_iter_t *itt, const changeset_t *ch)
722
{
723
	return changeset_iter_init(itt, 2, ch->add->nodes, ch->add->nsec3_nodes);
724 725
}

726
int changeset_iter_rem(changeset_iter_t *itt, const changeset_t *ch)
727
{
728
	return changeset_iter_init(itt, 2, ch->remove->nodes, ch->remove->nsec3_nodes);
729 730
}

731
int changeset_iter_all(changeset_iter_t *itt, const changeset_t *ch)
732
{
733
	return changeset_iter_init(itt, 4, ch->add->nodes, ch->add->nsec3_nodes,
734
	                           ch->remove->nodes, ch->remove->nsec3_nodes);
735 736
}

737 738
knot_rrset_t changeset_iter_next(changeset_iter_t *it)
{
739
	assert(it);
740 741 742 743
	ptrnode_t *n = NULL;
	knot_rrset_t rr;
	knot_rrset_init_empty(&rr);
	WALK_LIST(n, it->iters) {
744 745
		trie_it_t *t_it = (trie_it_t *)n->d;
		if (trie_it_finished(t_it)) {
746 747 748 749 750 751 752 753
			continue;
		}

		rr = get_next_rr(it, t_it);
		if (!knot_rrset_empty(&rr)) {
			// Got valid RRSet.
			return rr;
		}
754 755
	}

756
	return rr;
757 758
}

759
void changeset_iter_clear(changeset_iter_t *it)
760
{
761
	if (it) {
762
		cleanup_iter_list(&it->iters);
763 764
		it->node = NULL;
		it->node_pos = 0;
765
	}
766
}
767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804

int changeset_walk(const changeset_t *changeset, changeset_walk_callback callback, void *ctx)
{
	changeset_iter_t it;
	int ret = changeset_iter_rem(&it, changeset);
	if (ret != KNOT_EOK) {
		return ret;
	}

	knot_rrset_t rrset = changeset_iter_next(&it);
	while (!knot_rrset_empty(&rrset)) {
		ret = callback(&rrset, false, ctx);
		if (ret != KNOT_EOK) {
			changeset_iter_clear(&it);
			return ret;
		}
		rrset = changeset_iter_next(&it);
	}
	changeset_iter_clear(&it);

	ret = changeset_iter_add(&it, changeset);
	if (ret != KNOT_EOK) {
		return ret;
	}

	rrset = changeset_iter_next(&it);
	while (!knot_rrset_empty(&rrset)) {
		ret = callback(&rrset, true, ctx);
		if (ret != KNOT_EOK) {
			changeset_iter_clear(&it);
			return ret;
		}
		rrset = changeset_iter_next(&it);
	}
	changeset_iter_clear(&it);

	return KNOT_EOK;
}
805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834

void changeset_print(const changeset_t *changeset, FILE *outfile, bool color)
{
	const char * RED = "\x1B[31m", * GRN = "\x1B[32m", * RESET = "\x1B[0m";
	size_t buflen = 1024;
	char *buff = malloc(buflen);

	if (changeset->soa_from != NULL || !zone_contents_is_empty(changeset->remove)) {
		fprintf(outfile, "%s;;Removed\n", color ? RED : "");
	}
	if (changeset->soa_from != NULL && buff != NULL) {
		(void)knot_rrset_txt_dump(changeset->soa_from, &buff, &buflen, &KNOT_DUMP_STYLE_DEFAULT);
		fprintf(outfile, "%s", buff);
	}
	(void)zone_dump_text(changeset->remove, outfile, false);

	if (changeset->soa_to != NULL || !zone_contents_is_empty(changeset->add)) {
		fprintf(outfile, "%s;;Added\n", color ? GRN : "");
	}
	if (changeset->soa_to != NULL && buff != NULL) {
		(void)knot_rrset_txt_dump(changeset->soa_to, &buff, &buflen, &KNOT_DUMP_STYLE_DEFAULT);
		fprintf(outfile, "%s", buff);
	}
	(void)zone_dump_text(changeset->add, outfile, false);

	if (color) {
		printf("%s", RESET);
	}
	free(buff);
}