changesets.c 17.3 KB
Newer Older
1
/*  Copyright (C) 2017 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
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 <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
34
	if (*soa != NULL) {
		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
	return ret;
52
53
}

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

67
/*! \brief Inits changeset iterator with given tries. */
68
static int changeset_iter_init(changeset_iter_t *ch_it, size_t tries, ...)
Jan Kadlec's avatar
Jan Kadlec committed
69
70
71
72
73
74
75
76
{
	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) {
77
		trie_t *t = va_arg(args, trie_t *);
78
79
80
81
		if (t == NULL) {
			continue;
		}

82
		trie_it_t *it = trie_it_begin(t);
83
84
85
86
87
88
89
90
91
92
		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;
Jan Kadlec's avatar
Jan Kadlec committed
93
94
95
96
97
98
99
100
		}
	}

	va_end(args);

	return KNOT_EOK;
}

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

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

Jan Kadlec's avatar
Jan Kadlec committed
127
128
129
	ch_it->node_pos = 0;
}

130
/*! \brief Gets next RRSet from trie iterators. */
131
static knot_rrset_t get_next_rr(changeset_iter_t *ch_it, trie_it_t *t_it)
Jan Kadlec's avatar
Jan Kadlec committed
132
133
134
135
{
	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) {
136
			assert(trie_it_finished(t_it));
Jan Kadlec's avatar
Jan Kadlec committed
137
138
139
140
141
142
143
144
145
			knot_rrset_t rr;
			knot_rrset_init_empty(&rr);
			return rr;
		}
	}

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

146
147
148
// 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)
149
{
150
151
152
153
	if (fixed_rr != NULL) {
		*fixed_rr = knot_rrset_copy(rr, NULL);
	}

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

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

	// Subtract the data from node's RRSet.
164
	knot_rdataset_t *rrs = node_rdataset(node, rr->type);
165
166
167
168
169
170
171
172

	if (fixed_rr != NULL && *fixed_rr != NULL) {
		int ret = knot_rdataset_subtract(&(*fixed_rr)->rrs, rrs, true, NULL);
		if (ret != KNOT_EOK) {
			return;
		}
	}

173
	int ret = knot_rdataset_subtract(rrs, &rr->rrs, true, NULL);
174
	if (ret != KNOT_EOK) {
175
		return;
176
177
	}

178
	if (knot_rdataset_size(rrs) == 0) {
179
180
181
		// Remove empty type.
		node_remove_rdataset(node, rr->type);

182
		if (node->rrset_count == 0 && node != counterpart->apex) {
183
184
185
			// Remove empty node.
			zone_tree_t *t = knot_rrset_is_nsec3rel(rr) ?
								 counterpart->nsec3_nodes : counterpart->nodes;
186
			zone_tree_delete_empty_node(t, node);
187
		}
188
189
	}

190
	return;
191
192
}

Jan Kadlec's avatar
Jan Kadlec committed
193
194
195
int changeset_init(changeset_t *ch, const knot_dname_t *apex)
{
	memset(ch, 0, sizeof(changeset_t));
196

197
198
	// Init local changes
	ch->add = zone_contents_new(apex);
Jan Kadlec's avatar
Jan Kadlec committed
199
200
201
	if (ch->add == NULL) {
		return KNOT_ENOMEM;
	}
202
	ch->remove = zone_contents_new(apex);
Jan Kadlec's avatar
Jan Kadlec committed
203
204
205
206
	if (ch->remove == NULL) {
		zone_contents_free(&ch->add);
		return KNOT_ENOMEM;
	}
Jan Kadlec's avatar
Jan Kadlec committed
207

Jan Kadlec's avatar
Jan Kadlec committed
208
	return KNOT_EOK;
209
210
}

Jan Kadlec's avatar
Jan Kadlec committed
211
changeset_t *changeset_new(const knot_dname_t *apex)
Jan Kadlec's avatar
Jan Kadlec committed
212
{
Jan Kadlec's avatar
Jan Kadlec committed
213
	changeset_t *ret = malloc(sizeof(changeset_t));
214
	if (ret == NULL) {
Jan Kadlec's avatar
Jan Kadlec committed
215
216
217
		return NULL;
	}

Jan Kadlec's avatar
Jan Kadlec committed
218
219
220
221
222
223
	if (changeset_init(ret, apex) == KNOT_EOK) {
		return ret;
	} else {
		free(ret);
		return NULL;
	}
224
225
}

226
bool changeset_empty(const changeset_t *ch)
227
{
228
	if (ch == NULL || ch->add == NULL || ch->remove == NULL) {
229
		return true;
230
231
	}

232
233
234
	if (ch->soa_to) {
		return false;
	}
235

236
	changeset_iter_t itt;
237
	changeset_iter_all(&itt, ch);
238

239
240
	knot_rrset_t rr = changeset_iter_next(&itt);
	changeset_iter_clear(&itt);
241
242

	return knot_rrset_empty(&rr);
243
244
}

Lubos Slovak's avatar
Lubos Slovak committed
245
size_t changeset_size(const changeset_t *ch)
246
{
Jan Kadlec's avatar
Jan Kadlec committed
247
	if (ch == NULL) {
248
249
		return 0;
	}
250

251
	changeset_iter_t itt;
252
	changeset_iter_all(&itt, ch);
253
254

	size_t size = 0;
255
	knot_rrset_t rr = changeset_iter_next(&itt);
256
257
	while(!knot_rrset_empty(&rr)) {
		++size;
258
		rr = changeset_iter_next(&itt);
259
	}
260
	changeset_iter_clear(&itt);
261

Jan Kadlec's avatar
Jan Kadlec committed
262
263
264
265
266
267
268
	if (!knot_rrset_empty(ch->soa_from)) {
		size += 1;
	}
	if (!knot_rrset_empty(ch->soa_to)) {
		size += 1;
	}

269
	return size;
270
271
}

272
int changeset_add_addition(changeset_t *ch, const knot_rrset_t *rrset, changeset_flag_t flags)
Jan Kadlec's avatar
Jan Kadlec committed
273
{
274
275
276
277
278
279
280
281
282
	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);
	}

283
284
	knot_rrset_t *rrset_cancelout = NULL;

285
286
	/* Check if there's any removal and remove that, then add this
	 * addition anyway. Required to change TTLs. */
287
	if (flags & CHANGESET_CHECK) {
288
289
290
291
292
293
		/* 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;
		}

294
295
		check_redundancy(ch->remove, rrset,
				 ((flags & CHANGESET_CHECK_CANCELOUT) ? &rrset_cancelout : NULL));
296
	}
297

298
299
	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);
300

301
	if (flags & CHANGESET_CHECK) {
302
303
		knot_rrset_free((knot_rrset_t **)&rrset, NULL);
	}
304
	knot_rrset_free(&rrset_cancelout, NULL);
305
306

	return ret;
Jan Kadlec's avatar
Jan Kadlec committed
307
308
}

309
int changeset_add_removal(changeset_t *ch, const knot_rrset_t *rrset, changeset_flag_t flags)
Jan Kadlec's avatar
Jan Kadlec committed
310
{
311
312
313
314
315
316
317
318
319
	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);
	}

320
321
	knot_rrset_t *rrset_cancelout = NULL;

322
323
	/* Check if there's any addition and remove that, then add this
	 * removal anyway. */
324
	if (flags & CHANGESET_CHECK) {
325
326
327
328
329
330
		/* 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;
		}

331
332
		check_redundancy(ch->add, rrset,
				 ((flags & CHANGESET_CHECK_CANCELOUT) ? &rrset_cancelout : NULL));
333
	}
334

335
336
	const knot_rrset_t *to_remove = (rrset_cancelout == NULL ? rrset : rrset_cancelout);
	int ret = knot_rrset_empty(to_remove) ? KNOT_EOK : add_rr_to_contents(ch->remove, to_remove);
337

338
	if (flags & CHANGESET_CHECK) {
339
340
341
		knot_rrset_free((knot_rrset_t **)&rrset, NULL);
	}

342
343
	knot_rrset_free(&rrset_cancelout, NULL);

344
345
346
	// we don't care of TTLs at removals anyway (updates/apply.c/can_remove()/compare_ttls)
	// in practice, this happens at merging changesets
	return (ret == KNOT_ETTL ? KNOT_EOK : ret);
Jan Kadlec's avatar
Jan Kadlec committed
347
348
}

349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
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) {
			knot_rrset_free(&ch->soa_to, NULL);
			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) {
			knot_rrset_free(&ch->soa_from, NULL);
			ch->soa_from = NULL;
		}
		return KNOT_EOK;
	}

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

379
int changeset_merge(changeset_t *ch1, const changeset_t *ch2, int flags)
380
{
381
	changeset_iter_t itt;
382
	changeset_iter_rem(&itt, ch2);
383

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

395
	changeset_iter_add(&itt, ch2);
396

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

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

	return KNOT_EOK;
422
423
}

424
425
426
427
428
429
430
431
432
433
434
435
436
437
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;
	}
	knot_rrset_t *fixrrset;
	int ret = KNOT_EOK;
	if (adding) {
		fixrrset = knot_rrset_new(apply->owner, apply->type, apply->rclass, ctx->mm);
	} else {
		fixrrset = knot_rrset_copy(apply, ctx->mm);
	}
	if (fixrrset == NULL) {
		return KNOT_ENOMEM;
	}
	if (adding) {
		ret = knot_rdataset_intersect(zrdataset, &apply->rrs, &fixrrset->rrs, ctx->mm);
	} else {
		if (zrdataset != NULL) {
452
			ret = knot_rdataset_subtract(&fixrrset->rrs, zrdataset, true, ctx->mm);
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
		}
	}
	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);
		}
	}
	knot_rrset_free(&fixrrset, ctx->mm);
	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);
}

481
int changeset_preapply_fix(const zone_contents_t *zone, changeset_t *ch)
482
{
483
	if (zone == NULL || ch == NULL) {
484
485
486
487
488
489
490
491
492
493
		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 };
494
	ret = changeset_walk(ch, preapply_fix_rrset, (void *)&ctx);
495
	if (ret == KNOT_EOK) {
496
		ret = subtract(ch, &fixing);
497
498
499
500
501
	}
	changeset_clear(&fixing);
	return ret;
}

502
int changeset_cancelout(changeset_t *ch)
503
{
504
	if (ch == NULL) {
505
506
507
508
		return KNOT_EINVAL;
	}

	changeset_t fixing;
509
	int ret = changeset_init(&fixing, ch->add->apex->owner);
510
511
512
513
	if (ret != KNOT_EOK) {
		return ret;
	}

514
515
	preapply_fix_ctx ctx = { .zone = ch->remove, .fixing = &fixing, .mm = NULL };
	ret = changeset_walk(ch, preapply_fix_rrset, (void *)&ctx);
516
517
518
519
	if (ret == KNOT_EOK) {
		assert(zone_contents_is_empty(fixing.add));
		zone_contents_t *fixing_add_bck = fixing.add;
		fixing.add = fixing.remove;
520
		ret = subtract(ch, &fixing);
521
522
523
524
525
526
		fixing.add = fixing_add_bck;
	}
	changeset_clear(&fixing);
	return ret;
}

527
int changeset_to_contents(changeset_t *ch, zone_contents_t **out)
528
529
530
{
	assert(ch->soa_from == NULL);
	assert(zone_contents_is_empty(ch->remove));
531
	assert(out != NULL);
532

533
534
	*out = ch->add;
	int ret = add_rr_to_contents(*out, ch->soa_to);
535
	knot_rrset_free(&ch->soa_to, NULL);
536
537
538
	if (ret != KNOT_EOK) {
		zone_contents_deep_free(out);
	}
539
540
541
542

	zone_contents_deep_free(&ch->remove);
	free(ch->data);
	free(ch);
543
	return ret;
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
}

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);

	zone_contents_deep_free(&res->add);
	res->add = copy;
	return res;
}

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

	update_free_zone(&ch->add);

	zone_contents_deep_free(&ch->remove);
	knot_rrset_free(&ch->soa_from, NULL);
	knot_rrset_free(&ch->soa_to, NULL);
	free(ch->data);
	free(ch);
}

580
581
582
583
584
585
586
587
588
589
590
591
592
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)
593
{
Jan Kadlec's avatar
Jan Kadlec committed
594
	if (chgs) {
595
596
		changeset_t *chg, *nxt;
		WALK_LIST_DELSAFE(chg, nxt, *chgs) {
597
			rem_node(&chg->n);
598
			changeset_free(chg);
599
		}
600
		init_list(chgs);
601
	}
602
603
}

Jan Kadlec's avatar
Jan Kadlec committed
604
void changeset_clear(changeset_t *ch)
605
{
Jan Kadlec's avatar
Jan Kadlec committed
606
607
	if (ch == NULL) {
		return;
608
	}
609

Jan Kadlec's avatar
Jan Kadlec committed
610
611
612
	// Delete RRSets in lists, in case there are any left
	zone_contents_deep_free(&ch->add);
	zone_contents_deep_free(&ch->remove);
613

Jan Kadlec's avatar
Jan Kadlec committed
614
615
	knot_rrset_free(&ch->soa_from, NULL);
	knot_rrset_free(&ch->soa_to, NULL);
616

Jan Kadlec's avatar
Jan Kadlec committed
617
618
619
	// Delete binary data
	free(ch->data);
}
620

Jan Kadlec's avatar
Jan Kadlec committed
621
622
623
624
void changeset_free(changeset_t *ch)
{
	changeset_clear(ch);
	free(ch);
625
626
}

627
int changeset_iter_add(changeset_iter_t *itt, const changeset_t *ch)
628
{
629
	return changeset_iter_init(itt, 2, ch->add->nodes, ch->add->nsec3_nodes);
630
631
}

632
int changeset_iter_rem(changeset_iter_t *itt, const changeset_t *ch)
633
{
634
	return changeset_iter_init(itt, 2, ch->remove->nodes, ch->remove->nsec3_nodes);
635
636
}

637
int changeset_iter_all(changeset_iter_t *itt, const changeset_t *ch)
638
{
639
	return changeset_iter_init(itt, 4, ch->add->nodes, ch->add->nsec3_nodes,
640
	                           ch->remove->nodes, ch->remove->nsec3_nodes);
641
642
}

643
644
knot_rrset_t changeset_iter_next(changeset_iter_t *it)
{
645
	assert(it);
646
647
648
649
	ptrnode_t *n = NULL;
	knot_rrset_t rr;
	knot_rrset_init_empty(&rr);
	WALK_LIST(n, it->iters) {
650
651
		trie_it_t *t_it = (trie_it_t *)n->d;
		if (trie_it_finished(t_it)) {
652
653
654
655
656
657
658
659
			continue;
		}

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

662
	return rr;
663
664
}

665
void changeset_iter_clear(changeset_iter_t *it)
666
{
Jan Kadlec's avatar
Jan Kadlec committed
667
	if (it) {
668
		cleanup_iter_list(&it->iters);
669
670
		it->node = NULL;
		it->node_pos = 0;
Jan Kadlec's avatar
Jan Kadlec committed
671
	}
672
}
673
674
675
676
677
678
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

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;
}
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740

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);
}