zonecut.c 17.3 KB
Newer Older
1
/*  Copyright (C) 2014-2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
Marek Vavruša's avatar
Marek Vavruša 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/>.
Marek Vavruša's avatar
Marek Vavruša committed
15 16
 */

17
#include "lib/zonecut.h"
18

19
#include "contrib/cleanup.h"
20
#include "lib/defines.h"
21
#include "lib/generic/pack.h"
22
#include "lib/layer.h"
23
#include "lib/resolve.h"
24 25 26 27 28
#include "lib/rplan.h"

#include <libknot/descriptor.h>
#include <libknot/packet/wire.h>
#include <libknot/rrtype/rdname.h>
29

30
#define VERBOSE_MSG(qry, ...) QRVERBOSE(qry, "zcut", __VA_ARGS__)
31

32 33 34 35 36 37 38 39 40 41 42 43 44
/** Information for one NS name + address type. */
typedef enum {
	AI_UNINITED = 0,
	AI_REPUT,	/**< Don't use this addrset, due to: cache_rep, NO_IPV6, ...
			 * cache_rep approximates various problems when fetching the RRset. */
	AI_CYCLED,	/**< Skipped due to cycle detection; see implementation for details. */
	AI_LAST_BAD = AI_CYCLED, /** bad states: <= AI_LAST_BAD */
	AI_UNKNOWN,	/**< Don't know status of this RRset; various reasons. */
	AI_EMPTY,	/**< No usable address (may mean e.g. just NODATA). */
	AI_OK,		/**< At least one usable address.
			 * LATER: we might be interested whether it's only glue. */
} addrset_info_t;

45

46 47 48 49 50 51 52 53 54 55
static void update_cut_name(struct kr_zonecut *cut, const knot_dname_t *name)
{
	if (knot_dname_is_equal(name, cut->name)) {
		return;
	}
	knot_dname_t *next_name = knot_dname_copy(name, cut->pool);
	mm_free(cut->pool, cut->name);
	cut->name = next_name;
}

56
int kr_zonecut_init(struct kr_zonecut *cut, const knot_dname_t *name, knot_mm_t *pool)
57
{
58
	if (!cut || !name) {
59
		return kr_error(EINVAL);
60
	}
61

62
	memset(cut, 0, sizeof(*cut));
63 64
	cut->name = knot_dname_copy(name, pool);
	cut->pool = pool;
65 66
	cut->nsset = trie_create(pool);
	return cut->name && cut->nsset ? kr_ok() : kr_error(ENOMEM);
67 68
}

69 70 71 72 73 74 75 76 77 78 79 80 81
/** Completely free a pack_t. */
static inline void free_addr_set(pack_t *pack, knot_mm_t *pool)
{
	if (unlikely(!pack)) {
		/* promised we don't store NULL packs */
		assert(false);
		return;
	}
	pack_clear_mm(*pack, mm_free, pool);
	mm_free(pool, pack);
}
/** Trivial wrapper for use in trie_apply, due to ugly casting. */
static int free_addr_set_cb(trie_val_t *v, void *pool)
82
{
83
	free_addr_set(*v, pool);
84 85
	return kr_ok();
}
86

87 88
void kr_zonecut_deinit(struct kr_zonecut *cut)
{
89
	if (!cut) {
90 91 92
		return;
	}
	mm_free(cut->pool, cut->name);
93 94 95 96
	if (cut->nsset) {
		trie_apply(cut->nsset, free_addr_set_cb, cut->pool);
		trie_free(cut->nsset);
	}
Vladimír Čunát's avatar
Vladimír Čunát committed
97 98
	knot_rrset_free(cut->key, cut->pool);
	knot_rrset_free(cut->trust_anchor, cut->pool);
99
}
100

101 102 103 104 105 106 107
void kr_zonecut_move(struct kr_zonecut *to, const struct kr_zonecut *from)
{
	if (!to || !from) abort();
	kr_zonecut_deinit(to);
	memcpy(to, from, sizeof(*to));
}

108 109
void kr_zonecut_set(struct kr_zonecut *cut, const knot_dname_t *name)
{
110
	if (!cut || !name) {
111 112
		return;
	}
113 114 115
	knot_rrset_t *key, *ta;
	key = cut->key; cut->key = NULL;
	ta = cut->trust_anchor; cut->trust_anchor = NULL;
116 117
	kr_zonecut_deinit(cut);
	kr_zonecut_init(cut, name, cut->pool);
118 119
	cut->key = key;
	cut->trust_anchor = ta;
120 121
}

122 123 124 125 126
int kr_zonecut_copy(struct kr_zonecut *dst, const struct kr_zonecut *src)
{
	if (!dst || !src) {
		return kr_error(EINVAL);
	}
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
	if (!dst->nsset) {
		dst->nsset = trie_create(dst->pool);
	}
	/* Copy the contents, one by one. */
	int ret = kr_ok();
	trie_it_t *it;
	for (it = trie_it_begin(src->nsset); !trie_it_finished(it); trie_it_next(it)) {
		size_t klen;
		const char * const k = trie_it_key(it, &klen);
		pack_t **new_pack = (pack_t **)trie_get_ins(dst->nsset, k, klen);
		if (!new_pack) {
			ret = kr_error(ENOMEM);
			break;
		}
		const pack_t *old_pack = *trie_it_val(it);
		ret = pack_clone(new_pack, old_pack, dst->pool);
		if (ret) break;
	}
	trie_it_free(it);
	return ret;
147 148
}

149
int kr_zonecut_copy_trust(struct kr_zonecut *dst, const struct kr_zonecut *src)
150
{
151 152
	knot_rrset_t *key_copy = NULL;
	knot_rrset_t *ta_copy = NULL;
153 154

	if (src->key) {
155 156 157
		key_copy = knot_rrset_copy(src->key, dst->pool);
		if (!key_copy) {
			return kr_error(ENOMEM);
158 159 160 161
		}
	}

	if (src->trust_anchor) {
162 163
		ta_copy = knot_rrset_copy(src->trust_anchor, dst->pool);
		if (!ta_copy) {
Vladimír Čunát's avatar
Vladimír Čunát committed
164
			knot_rrset_free(key_copy, dst->pool);
165
			return kr_error(ENOMEM);
166 167 168
		}
	}

Vladimír Čunát's avatar
Vladimír Čunát committed
169
	knot_rrset_free(dst->key, dst->pool);
170
	dst->key = key_copy;
Vladimír Čunát's avatar
Vladimír Čunát committed
171
	knot_rrset_free(dst->trust_anchor, dst->pool);
172
	dst->trust_anchor = ta_copy;
173

174
	return kr_ok();
175 176
}

177
int kr_zonecut_add(struct kr_zonecut *cut, const knot_dname_t *ns, const void *data, int len)
178
{
179
	if (!cut || !ns || !cut->nsset || (data && len <= 0)) {
180
		assert(!EINVAL);
181
		return kr_error(EINVAL);
182
	}
183
	/* Disabled; add_reverse_pair() misuses this for domain name in rdata. */
184 185
	if (false && data && len != sizeof(struct in_addr)
		  && len != sizeof(struct in6_addr)) {
186 187 188 189
		assert(!EINVAL);
		return kr_error(EINVAL);
	}

190 191 192 193 194 195 196
	/* Get a pack_t for the ns. */
	pack_t **pack = (pack_t **)trie_get_ins(cut->nsset, (const char *)ns, knot_dname_size(ns));
	if (!pack) return kr_error(ENOMEM);
	if (*pack == NULL) {
		*pack = mm_alloc(cut->pool, sizeof(pack_t));
		if (*pack == NULL) return kr_error(ENOMEM);
		pack_init(**pack);
197 198
	}
	/* Insert data (if has any) */
199
	if (data == NULL) {
200 201
		return kr_ok();
	}
202
	/* Check for duplicates */
203
	if (pack_obj_find(*pack, data, len)) {
204 205 206
		return kr_ok();
	}
	/* Push new address */
207
	int ret = pack_reserve_mm(**pack, 1, len, kr_memreserve, cut->pool);
208 209 210
	if (ret != 0) {
		return kr_error(ENOMEM);
	}
211
	return pack_obj_push(*pack, data, len);
212 213
}

214
int kr_zonecut_del(struct kr_zonecut *cut, const knot_dname_t *ns, const void *data, int len)
215
{
216
	if (!cut || !ns || (data && len <= 0)) {
217 218
		return kr_error(EINVAL);
	}
219

220
	/* Find the address list. */
221
	int ret = kr_ok();
222
	pack_t *pack = kr_zonecut_find(cut, ns);
223 224
	if (pack == NULL) {
		return kr_error(ENOENT);
225
	}
226
	/* Remove address from the pack. */
227 228
	if (data) {
		ret = pack_obj_del(pack, data, len);
229 230
	}
	/* No servers left, remove NS from the set. */
231
	if (pack->len == 0) {
232 233 234 235
		free_addr_set(pack, cut->pool);
		ret = trie_del(cut->nsset, (const char *)ns, knot_dname_size(ns), NULL);
		assert(ret == 0); /* only KNOT_ENOENT and that *can't* happen */
		return (ret == 0) ? kr_ok() : kr_error(ret);
236
	}
237 238 239

	return ret;
}
240

241 242 243 244 245 246 247
int kr_zonecut_del_all(struct kr_zonecut *cut, const knot_dname_t *ns)
{
	if (!cut || !ns) {
		return kr_error(EINVAL);
	}

	/* Find the address list; then free and remove it. */
248 249 250 251 252
	pack_t *pack;
	int ret = trie_del(cut->nsset, (const char *)ns, knot_dname_size(ns),
			   (trie_val_t *)&pack);
	if (ret) { /* deletion failed */
		assert(ret == KNOT_ENOENT);
253 254
		return kr_error(ENOENT);
	}
255 256
	free_addr_set(pack, cut->pool);
	return kr_ok();
257 258
}

259 260
pack_t *kr_zonecut_find(struct kr_zonecut *cut, const knot_dname_t *ns)
{
261
	if (!cut || !ns) {
262 263
		return NULL;
	}
264 265 266
	trie_val_t *val = trie_get_try(cut->nsset, (const char *)ns, knot_dname_size(ns));
	/* we get pointer to the pack_t pointer */
	return val ? (pack_t *)*val : NULL;
267 268
}

269
static int has_address(trie_val_t *v, void *baton_)
270
{
271 272 273
	const pack_t *pack = *v;
	const bool found = pack != NULL && pack->len != 0;
	return found;
274 275
}

276
bool kr_zonecut_is_empty(struct kr_zonecut *cut)
277
{
278 279 280
	if (!cut || !cut->nsset) {
		assert(false);
		return true;
281
	}
282
	return !trie_apply(cut->nsset, has_address, NULL);
283 284
}

285
int kr_zonecut_set_sbelt(struct kr_context *ctx, struct kr_zonecut *cut)
286
{
287
	if (!ctx || !cut || !ctx->root_hints.nsset) {
288 289 290
		return kr_error(EINVAL);
	}

291 292
	trie_apply(cut->nsset, free_addr_set_cb, cut->pool);
	trie_clear(cut->nsset);
293

294 295
	const uint8_t *const dname_root = (const uint8_t *)/*sign-cast*/("");
	update_cut_name(cut, dname_root);
296
	/* Copy root hints from resolution context. */
297
	return kr_zonecut_copy(cut, &ctx->root_hints);
298 299
}

Vladimír Čunát's avatar
.  
Vladimír Čunát committed
300
/** Fetch address for zone cut.  Any rank is accepted (i.e. glue as well). */
301 302
static addrset_info_t fetch_addr(pack_t *addrs, const knot_dname_t *ns, uint16_t rrtype,
				 knot_mm_t *mm_pool, const struct kr_query *qry)
Vladimír Čunát's avatar
.  
Vladimír Čunát committed
303
// LATER(optim.): excessive data copying
304
{
305 306 307 308 309 310 311 312 313 314 315 316 317 318
	int rdlen;
	switch (rrtype) {
	case KNOT_RRTYPE_A:
		rdlen = 4;
		break;
	case KNOT_RRTYPE_AAAA:
		rdlen = 16;
		break;
	default:
		assert(!EINVAL);
		return AI_UNKNOWN;
	}

	struct kr_context *ctx = qry->request->ctx;
319
	struct kr_cache_p peek;
320 321
	if (kr_cache_peek_exact(&ctx->cache, ns, rrtype, &peek) != 0) {
		return AI_UNKNOWN;
Vladimír Čunát's avatar
.  
Vladimír Čunát committed
322
	}
323
	int32_t new_ttl = kr_cache_ttl(&peek, qry, ns, rrtype);
Vladimír Čunát's avatar
.  
Vladimír Čunát committed
324
	if (new_ttl < 0) {
325
		return AI_UNKNOWN;
326 327
	}

Vladimír Čunát's avatar
.  
Vladimír Čunát committed
328
	knot_rrset_t cached_rr;
329 330
	knot_rrset_init(&cached_rr, /*const-cast*/(knot_dname_t *)ns, rrtype,
			KNOT_CLASS_IN, new_ttl);
331 332
	if (kr_cache_materialize(&cached_rr.rrs, &peek, mm_pool) < 0) {
		return AI_UNKNOWN;
Vladimír Čunát's avatar
.  
Vladimír Čunát committed
333
	}
334 335 336 337 338 339 340 341 342

	/* Reserve memory in *addrs.  Implementation detail:
	 * pack_t cares for lengths, so we don't store those in the data. */
	const size_t pack_extra_size = knot_rdataset_size(&cached_rr.rrs)
		- cached_rr.rrs.count * offsetof(knot_rdata_t, len);
	int ret = pack_reserve_mm(*addrs, cached_rr.rrs.count, pack_extra_size,
				  kr_memreserve, mm_pool);
	if (ret) abort(); /* ENOMEM "probably" */

343
	int usable_cnt = 0;
344
	addrset_info_t result = AI_EMPTY;
345
	knot_rdata_t *rd = cached_rr.rrs.rdata;
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360
	for (uint16_t i = 0; i < cached_rr.rrs.count; ++i, rd = knot_rdataset_next(rd)) {
		if (unlikely(rd->len != rdlen)) {
			VERBOSE_MSG(qry, "bad NS address length %d for rrtype %d, skipping\n",
					(int)rd->len, (int)rrtype);
			continue;
		}
		/* Check RTT cache - whether the IP is usable or not. */
		kr_nsrep_rtt_lru_entry_t *rtt_e = ctx->cache_rtt
			? lru_get_try(ctx->cache_rtt, (const char *)rd->data, rd->len)
			: NULL;
		const bool unusable = rtt_e && rtt_e->score >= KR_NS_TIMEOUT
			&& qry->creation_time_mono
			   < rtt_e->tout_timestamp + ctx->cache_rtt_tout_retry_interval;
		if (!unusable) {
			result = AI_OK;
361
			++usable_cnt;
362 363 364 365 366 367 368 369 370 371 372 373 374
		}

		ret = pack_obj_push(addrs, rd->data, rd->len);
		assert(!ret); /* didn't fit because of incorrectly reserved memory */
		/* LATER: for now we lose quite some information here,
		 * as keeping it would need substantial changes on other places,
		 * and it turned out to be premature optimization (most likely).
		 * We might e.g. skip adding unusable addresses,
		 * and either keep some rtt information associated
		 * or even finish up choosing the set to send packets to.
		 * Overall there's some overlap with nsrep.c functionality.
		 */
	}
375 376 377 378
	if (usable_cnt != cached_rr.rrs.count) {
		VERBOSE_MSG(qry, "usable NS addresses: %d/%d\n",
				usable_cnt, cached_rr.rrs.count);
	}
379
	return result;
380 381
}

382
/** Fetch best NS for zone cut. */
383
static int fetch_ns(struct kr_context *ctx, struct kr_zonecut *cut,
384
		    const knot_dname_t *name, const struct kr_query *qry,
Vladimír Čunát's avatar
Vladimír Čunát committed
385
		    uint8_t * restrict rank)
386
{
387
	struct kr_cache_p peek;
Vladimír Čunát's avatar
.  
Vladimír Čunát committed
388
	int ret = kr_cache_peek_exact(&ctx->cache, name, KNOT_RRTYPE_NS, &peek);
389 390 391
	if (ret != 0) {
		return ret;
	}
392 393 394 395
	/* Note: we accept *any* rank from the cache.  We assume that nothing
	 * completely untrustworthy could get into the cache, e.g out-of-bailiwick
	 * records that weren't validated.
	 */
396 397 398 399 400 401
	*rank = peek.rank;

	int32_t new_ttl = kr_cache_ttl(&peek, qry, name, KNOT_RRTYPE_NS);
	if (new_ttl < 0) {
		return kr_error(ESTALE);
	}
Vladimír Čunát's avatar
.  
Vladimír Čunát committed
402
	/* Materialize the rdataset temporarily, for simplicity. */
403
	knot_rdataset_t ns_rds = { 0 };
404
	ret = kr_cache_materialize(&ns_rds, &peek, cut->pool);
Vladimír Čunát's avatar
.  
Vladimír Čunát committed
405
	if (ret < 0) {
406 407
		return ret;
	}
408

409 410
	/* Insert name servers for this zone cut, addresses will be looked up
	 * on-demand (either from cache or iteratively) */
411
	bool all_bad = true; /**< All NSs (seen so far) are in a bad state. */
412 413 414 415
	knot_rdata_t *rdata_i = ns_rds.rdata;
	for (unsigned i = 0; i < ns_rds.count;
			++i, rdata_i = knot_rdataset_next(rdata_i)) {
		const knot_dname_t *ns_name = knot_ns_name(rdata_i);
416 417 418 419 420 421 422 423 424 425 426 427
		const size_t ns_size = knot_dname_size(ns_name);

		/* Get a new pack within the nsset. */
		pack_t **pack = (pack_t **)trie_get_ins(cut->nsset,
					(const char *)ns_name, ns_size);
		if (!pack) return kr_error(ENOMEM);
		assert(!*pack); /* not critical, really */
		*pack = mm_alloc(cut->pool, sizeof(pack_t));
		if (!*pack) return kr_error(ENOMEM);
		pack_init(**pack);

		addrset_info_t infos[2];
428
		/* Fetch NS reputation and decide whether to prefetch A/AAAA records. */
429
		unsigned *cached = lru_get_try(ctx->cache_rep,
430
					(const char *)ns_name, ns_size);
431
		unsigned reputation = (cached) ? *cached : 0;
432 433 434 435 436 437 438
		infos[0] = (reputation & KR_NS_NOIP4) || qry->flags.NO_IPV4
			? AI_REPUT
			: fetch_addr(*pack, ns_name, KNOT_RRTYPE_A, cut->pool, qry);
		infos[1] = (reputation & KR_NS_NOIP6) || qry->flags.NO_IPV6
			? AI_REPUT
			: fetch_addr(*pack, ns_name, KNOT_RRTYPE_AAAA, cut->pool, qry);

439 440 441 442 443 444 445 446
		#if 0 /* rather unlikely to be useful unless changing some zcut code */
		WITH_VERBOSE(qry) {
			auto_free char *ns_name_txt = kr_dname_text(ns_name);
			VERBOSE_MSG(qry, "NS %s infos: %d, %d\n",
					ns_name_txt, (int)infos[0], (int)infos[1]);
		}
		#endif

447 448 449 450 451 452 453 454 455
		/* AI_CYCLED checks.
		 * If an ancestor query has its zone cut in the state that
		 * it's looking for name or address(es) of some NS(s),
		 * we want to avoid doing so with a NS that lies under its cut.
		 * Instead we need to consider such names unusable in the cut (for now). */
		if (infos[0] != AI_UNKNOWN && infos[1] != AI_UNKNOWN) {
			/* Optimization: the following loop would be pointless. */
			all_bad = false;
			continue;
456
		}
457 458 459 460 461 462 463
		for (const struct kr_query *aq = qry; aq->parent; aq = aq->parent) {
			const struct kr_qflags *aqpf = &aq->parent->flags;
			if (   (aqpf->AWAIT_CUT  && aq->stype == KNOT_RRTYPE_NS)
			    || (aqpf->AWAIT_IPV4 && aq->stype == KNOT_RRTYPE_A)
			    || (aqpf->AWAIT_IPV6 && aq->stype == KNOT_RRTYPE_AAAA)) {
				if (knot_dname_in_bailiwick(ns_name,
							aq->parent->zone_cut.name)) {
464 465 466
					for (int j = 0; j < 2; ++j)
						if (infos[j] == AI_UNKNOWN)
							infos[j] = AI_CYCLED;
467 468 469 470 471 472 473
					break;
				}
			} else {
				/* This ancestor waits for other reason that
				 * NS name or address, so we're out of a direct cycle. */
				break;
			}
474
		}
475
		all_bad = all_bad && infos[0] <= AI_LAST_BAD && infos[1] <= AI_LAST_BAD;
476 477
	}

478 479 480 481 482
	if (all_bad) { WITH_VERBOSE(qry) {
		auto_free char *name_txt = kr_dname_text(name);
		VERBOSE_MSG(qry, "cut %s: all NSs bad, count = %d\n",
				name_txt, (int)ns_rds.count);
	} }
Vladimír Čunát's avatar
.  
Vladimír Čunát committed
483
	knot_rdataset_clear(&ns_rds, cut->pool);
484
	return all_bad ? ELOOP : kr_ok();
485 486
}

487
/**
488
 * Fetch secure RRSet of given type.
489
 */
490
static int fetch_secure_rrset(knot_rrset_t **rr, struct kr_cache *cache,
491 492
	const knot_dname_t *owner, uint16_t type, knot_mm_t *pool,
	const struct kr_query *qry)
493
{
494
	if (!rr) {
495 496
		assert(!EINVAL);
		return kr_error(EINVAL);
497
	}
Vladimír Čunát's avatar
.  
Vladimír Čunát committed
498
	/* peek, check rank and TTL */
499
	struct kr_cache_p peek;
Vladimír Čunát's avatar
.  
Vladimír Čunát committed
500
	int ret = kr_cache_peek_exact(cache, owner, type, &peek);
501 502 503
	if (ret != 0) {
		return ret;
	}
Vladimír Čunát's avatar
.  
Vladimír Čunát committed
504
	if (!kr_rank_test(peek.rank, KR_RANK_SECURE)) {
505 506
		return kr_error(ENOENT);
	}
507
	int32_t new_ttl = kr_cache_ttl(&peek, qry, owner, type);
Vladimír Čunát's avatar
.  
Vladimír Čunát committed
508 509 510 511
	if (new_ttl < 0) {
		return kr_error(ESTALE);
	}
	/* materialize a new RRset */
Vladimír Čunát's avatar
Vladimír Čunát committed
512
	knot_rrset_free(*rr, pool);
513
	*rr = mm_alloc(pool, sizeof(knot_rrset_t));
514
	if (*rr == NULL) {
515 516
		return kr_error(ENOMEM);
	}
Vladimír Čunát's avatar
.  
Vladimír Čunát committed
517 518 519 520 521 522
	owner = knot_dname_copy(/*const-cast*/(knot_dname_t *)owner, pool);
	if (!owner) {
		mm_free(pool, *rr);
		*rr = NULL;
		return kr_error(ENOMEM);
	}
523 524 525
	knot_rrset_init(*rr, /*const-cast*/(knot_dname_t *)owner, type,
			KNOT_CLASS_IN, new_ttl);
	ret = kr_cache_materialize(&(*rr)->rrs, &peek, pool);
Vladimír Čunát's avatar
.  
Vladimír Čunát committed
526
	if (ret < 0) {
Vladimír Čunát's avatar
Vladimír Čunát committed
527 528
		knot_rrset_free(*rr, pool);
		*rr = NULL;
529 530 531 532 533 534
		return ret;
	}

	return kr_ok();
}

535 536 537
int kr_zonecut_find_cached(struct kr_context *ctx, struct kr_zonecut *cut,
			   const knot_dname_t *name, const struct kr_query *qry,
			   bool * restrict secured)
538
{
539
	if (!ctx || !cut || !name) {
540
		//assert(false);
541
		return kr_error(EINVAL);
542
	}
543 544 545 546
	/* I'm not sure whether the caller always passes a clean state;
	 * mixing doesn't seem to make sense in any case, so let's clear it.
	 * We don't bother freeing the packs, as they're on mempool. */
	trie_clear(cut->nsset);
547 548
	/* Copy name as it may overlap with cut name that is to be replaced. */
	knot_dname_t *qname = knot_dname_copy(name, cut->pool);
549
	if (!qname) {
550 551
		return kr_error(ENOMEM);
	}
552 553
	/* Start at QNAME. */
	int ret;
554
	const knot_dname_t *label = qname;
555
	while (true) {
556
		/* Fetch NS first and see if it's insecure. */
557
		uint8_t rank = 0;
558
		const bool is_root = (label[0] == '\0');
559 560
		ret = fetch_ns(ctx, cut, label, qry, &rank);
		if (ret == 0) {
561
			/* Flag as insecure if cached as this */
Vladimír Čunát's avatar
Vladimír Čunát committed
562
			if (kr_rank_test(rank, KR_RANK_INSECURE)) {
563
				*secured = false;
564
			}
565
			/* Fetch DS and DNSKEY if caller wants secure zone cut */
566
			int ret_ds = 1, ret_dnskey = 1;
567
			if (*secured || is_root) {
568 569 570 571
				ret_ds = fetch_secure_rrset(&cut->trust_anchor, &ctx->cache,
						label, KNOT_RRTYPE_DS, cut->pool, qry);
				ret_dnskey = fetch_secure_rrset(&cut->key, &ctx->cache,
						label, KNOT_RRTYPE_DNSKEY, cut->pool, qry);
572
			}
573
			update_cut_name(cut, label);
574 575
			WITH_VERBOSE(qry) {
				auto_free char *label_str = kr_dname_text(label);
576
				VERBOSE_MSG(qry,
Marek Vavruša's avatar
Marek Vavruša committed
577 578
					"found cut: %s (rank 0%.2o return codes: DS %d, DNSKEY %d)\n",
					label_str, rank, ret_ds, ret_dnskey);
Vladimír Čunát's avatar
.  
Vladimír Čunát committed
579
			}
580 581 582 583 584
			ret = kr_ok();
			break;
		} /* else */

		trie_clear(cut->nsset);
585 586 587 588
		/* Subtract label from QNAME. */
		if (!is_root) {
			label = knot_wire_next_label(label, NULL);
		} else {
589
			ret = kr_error(ENOENT);
590 591
			break;
		}
592
	}
593

594
	kr_cache_commit(&ctx->cache);
595
	mm_free(cut->pool, qname);
596
	return ret;
597
}