nsec3.c 17.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/*  Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>

    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 <https://www.gnu.org/licenses/>.
 */

/** @file
 * Implementation of NSEC3 handling.  Prototypes in ./impl.h
 */

#include "lib/cache/impl.h"

23
#include "contrib/base32hex.h"
Vladimír Čunát's avatar
Vladimír Čunát committed
24 25 26
#include "lib/dnssec/nsec.h"
#include "lib/layer/iterate.h"

27
#include <libknot/rrtype/nsec3.h>
28 29

static const knot_db_val_t VAL_EMPTY = { NULL, 0 };
Vladimír Čunát's avatar
Vladimír Čunát committed
30

31 32 33 34
/** Common part: write all but the NSEC3 hash. */
static knot_db_val_t key_NSEC3_common(struct key *k, const knot_dname_t *zname,
					const nsec_p_hash_t nsec_p_hash)
{
Vladimír Čunát's avatar
Vladimír Čunát committed
35
	int ret;
36 37
	const bool ok = k && zname
		&& !(ret = kr_dname_lf(k->buf, zname, false));
Vladimír Čunát's avatar
Vladimír Čunát committed
38 39
	if (!ok) {
		assert(false);
40
		return VAL_EMPTY;
Vladimír Čunát's avatar
Vladimír Čunát committed
41 42
	}

43
	/* CACHE_KEY_DEF: key == zone's dname_lf + '\0' + '3' + nsec_p hash (4B)
Vladimír Čunát's avatar
Vladimír Čunát committed
44
	 * 			+ NSEC3 hash (20B == NSEC3_HASH_LEN binary!)
45 46
	 * LATER(optim.) nsec_p hash: perhaps 2B would give a sufficient probability
	 * of avoiding collisions.
Vladimír Čunát's avatar
Vladimír Čunát committed
47
	 */
48
	uint8_t *begin = k->buf + 1 + k->zlf_len; /* one byte after zone's zero */
Vladimír Čunát's avatar
Vladimír Čunát committed
49 50 51
	begin[0] = 0;
	begin[1] = '3'; /* tag for NSEC3 */
	k->type = KNOT_RRTYPE_NSEC3;
52 53 54 55 56 57
	memcpy(begin + 2, &nsec_p_hash, sizeof(nsec_p_hash));
	return (knot_db_val_t){
		.data = k->buf + 1,
		.len = begin + 2 + sizeof(nsec_p_hash) - (k->buf + 1),
	};
}
Vladimír Čunát's avatar
Vladimír Čunát committed
58

59 60 61 62 63 64
knot_db_val_t key_NSEC3(struct key *k, const knot_dname_t *nsec3_name,
			const nsec_p_hash_t nsec_p_hash)
{
	knot_db_val_t val = key_NSEC3_common(k, nsec3_name /*only zname required*/,
						nsec_p_hash);
	if (!val.data) return val;
65 66
	int len = base32hex_decode(nsec3_name + 1, nsec3_name[0],
			knot_db_val_bound(val), KR_CACHE_KEY_MAXLEN - val.len);
Vladimír Čunát's avatar
Vladimír Čunát committed
67
	if (len != NSEC3_HASH_LEN) {
68 69 70 71 72
		return VAL_EMPTY;
	}
	val.len += len;
	return val;
}
Vladimír Čunát's avatar
Vladimír Čunát committed
73

74 75 76
/** Construct a string key for for NSEC3 predecessor-search, from an non-NSEC3 name.
 * \note k->zlf_len and k->zname are assumed to have been correctly set */
static knot_db_val_t key_NSEC3_name(struct key *k, const knot_dname_t *name,
Vladimír Čunát's avatar
.  
Vladimír Čunát committed
77
		const bool add_wildcard, const struct nsec_p *nsec_p)
78
{
Vladimír Čunát's avatar
.  
Vladimír Čunát committed
79
	bool ok = k && name && nsec_p && nsec_p->raw;
80
	if (!ok) return VAL_EMPTY;
Vladimír Čunát's avatar
.  
Vladimír Čunát committed
81 82
	knot_db_val_t val = key_NSEC3_common(k, k->zname, nsec_p->hash);
	if (!val.data) return val;
83 84 85 86 87 88 89 90 91 92

	/* Make `name` point to correctly wildcarded owner name. */
	uint8_t buf[KNOT_DNAME_MAXLEN];
	int name_len;
	if (add_wildcard) {
		buf[0] = '\1';
		buf[1] = '*';
		name_len = knot_dname_to_wire(buf + 2, name, sizeof(buf) - 2);
		if (name_len < 0) return VAL_EMPTY; /* wants wildcard but doesn't fit */
		name = buf;
93
		name_len += 2;
94 95 96 97 98
	} else {
		name_len = knot_dname_size(name);
	}
	/* Append the NSEC3 hash. */
	const dnssec_binary_t dname = {
99
		.size = name_len,
100 101
		.data = (uint8_t *)/*const-cast*/name,
	};
102 103

	#if 0 // LATER(optim.): this requires a patched libdnssec - tries to realloc()
104 105 106 107
	dnssec_binary_t hash = {
		.size = KR_CACHE_KEY_MAXLEN - val.len,
		.data = val.data + val.len,
	};
Vladimír Čunát's avatar
.  
Vladimír Čunát committed
108
	int ret = dnssec_nsec3_hash(&dname, &nsec_p->libknot, &hash);
109
	if (ret != DNSSEC_EOK) return VAL_EMPTY;
110
	assert(hash.size == NSEC3_HASH_LEN);
111 112 113 114 115 116 117 118 119

	#else
	dnssec_binary_t hash = { .size = 0, .data = NULL };
	int ret = dnssec_nsec3_hash(&dname, &nsec_p->libknot, &hash);
	if (ret != DNSSEC_EOK) return VAL_EMPTY;
	if (hash.size != NSEC3_HASH_LEN || !hash.data) {
		assert(false);
		return VAL_EMPTY;
	}
120
	memcpy(knot_db_val_bound(val), hash.data, NSEC3_HASH_LEN);
121 122 123
	free(hash.data);
	#endif

124 125
	val.len += hash.size;
	return val;
Vladimír Čunát's avatar
Vladimír Čunát committed
126 127
}

Vladimír Čunát's avatar
Vladimír Čunát committed
128 129 130 131 132 133 134 135 136
/** Return h1 < h2, semantically on NSEC3 hashes. */
static inline bool nsec3_hash_ordered(const uint8_t *h1, const uint8_t *h2)
{
	return memcmp(h1, h2, NSEC3_HASH_LEN) < 0;
}

/** NSEC3 range search.
 *
 * \param key Pass output of key_NSEC3(k, ...)
137
 * \param nsec_p Restrict to this NSEC3 parameter-set.
138
 * \param value[out] The raw data of the NSEC3 cache record (optional; consistency checked).
Vladimír Čunát's avatar
Vladimír Čunát committed
139 140 141 142 143 144 145
 * \param exact_match[out] Whether the key was matched exactly or just covered (optional).
 * \param hash_low[out] Output the low end hash of covering NSEC3, pointing within DB (optional).
 * \param new_ttl[out] New TTL of the NSEC3 (optional).
 * \return Error message or NULL.
 * \note The function itself does *no* bitmap checks, e.g. RFC 6840 sec. 4.
 */
static const char * find_leq_NSEC3(struct kr_cache *cache, const struct kr_query *qry,
146 147 148
		const knot_db_val_t key, const struct key *k, const struct nsec_p *nsec_p,
		knot_db_val_t *value, bool *exact_match, const uint8_t **hash_low,
		uint32_t *new_ttl)
Vladimír Čunát's avatar
Vladimír Čunát committed
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
{
	/* Do the cache operation. */
	const size_t hash_off = key_nsec3_hash_off(k);
	if (!key.data || key.len < hash_off) {
		assert(false);
		return "range search ERROR";
	}
	knot_db_val_t key_found = key;
	knot_db_val_t val = { NULL, 0 };
	int ret = cache_op(cache, read_leq, &key_found, &val);
		/* ^^ LATER(optim.): incrementing key and doing less-than search
		 * would probably be slightly more efficient with LMDB,
		 * but the code complexity would grow considerably. */
	if (ret < 0) {
		if (ret == kr_error(ENOENT)) {
			return "range search miss";
		} else {
			assert(false);
			return "range search ERROR";
		}
	}
	if (value) {
		*value = val;
	}
	/* Check consistency, TTL, rank. */
	const bool is_exact = (ret == 0);
	if (exact_match) {
		*exact_match = is_exact;
	}
	const struct entry_h *eh = entry_h_consistent_NSEC(val);
	if (!eh) {
		/* This might be just finding something else than NSEC3 entry,
		 * in case we searched before the very first one in the zone. */
		return "range search found inconsistent entry";
	}
184 185 186
	/* Passing just zone name instead of owner. */
	int32_t new_ttl_ = get_new_ttl(eh, qry, k->zname, KNOT_RRTYPE_NSEC3,
					qry->timestamp.tv_sec);
187
	if (new_ttl_ < 0 || !kr_rank_test(eh->rank, KR_RANK_SECURE)) {
Vladimír Čunát's avatar
Vladimír Čunát committed
188 189 190 191 192 193 194 195
		return "range search found stale or insecure entry";
		/* TODO: remove the stale record *and* retry,
		 * in case we haven't run off.  Perhaps start by in_zone check. */
	}
	if (new_ttl) {
		*new_ttl = new_ttl_;
	}
	if (hash_low) {
196
		*hash_low = (uint8_t *)key_found.data + hash_off;
Vladimír Čunát's avatar
Vladimír Čunát committed
197 198 199 200 201 202 203
	}
	if (is_exact) {
		/* Nothing else to do. */
		return NULL;
	}
	/* The NSEC3 starts strictly before our target name;
	 * now check that it still belongs into that zone and chain. */
204
	const uint8_t *nsec_p_raw = eh->data + KR_CACHE_RR_COUNT_SIZE
205 206
					+ 2 /* RDLENGTH from rfc1034 */;
	const int nsec_p_len = nsec_p_rdlen(nsec_p_raw);
Vladimír Čunát's avatar
Vladimír Čunát committed
207 208
	const bool same_chain = key_found.len == hash_off + NSEC3_HASH_LEN
		/* CACHE_KEY_DEF */
209 210 211 212
		&& memcmp(key.data, key_found.data, hash_off) == 0
		/* exact comparison of NSEC3 parameters */
		&& nsec_p_len == nsec_p_rdlen(nsec_p->raw)
		&& memcmp(nsec_p_raw, nsec_p->raw, nsec_p_len) == 0;
Vladimír Čunát's avatar
Vladimír Čunát committed
213 214 215 216 217 218 219 220 221 222
	if (!same_chain) {
		return "range search miss (!same_chain)";
	}
	/* We know it starts before sname, so let's check the other end.
	 * A. find the next hash and check its length. */
	if (KR_CACHE_RR_COUNT_SIZE != 2 || get_uint16(eh->data) == 0) {
		assert(false);
		return "ERROR";
		/* TODO: more checks?  Also, `next` computation is kinda messy. */
	}
223
	const uint8_t *hash_next = nsec_p_raw + nsec_p_len
224 225
				 + sizeof(uint8_t) /* hash length from rfc5155 */;
	if (hash_next[-1] != NSEC3_HASH_LEN) {
Vladimír Čunát's avatar
Vladimír Čunát committed
226 227 228
		return "unexpected next hash length";
	}
	/* B. do the actual range check. */
229
	const uint8_t * const hash_searched = (uint8_t *)key.data + hash_off;
Vladimír Čunát's avatar
Vladimír Čunát committed
230 231 232
	bool covers = /* we know for sure that the low end is before the searched name */
		nsec3_hash_ordered(hash_searched, hash_next)
		/* and the wrap-around case */
233
		|| nsec3_hash_ordered(hash_next, (const uint8_t *)key_found.data + hash_off);
Vladimír Čunát's avatar
Vladimír Čunát committed
234 235 236 237 238 239
	if (!covers) {
		return "range search miss (!covers)";
	}
	return NULL;
}

240 241 242
/** Extract textual representation of NSEC3 hash from a cache key.
 * \param text must have length at least NSEC3_HASH_TXT_LEN+1 (will get 0-terminated). */
static void key_NSEC3_hash2text(const knot_db_val_t key, char *text)
243
{
244
	assert(key.data && key.len > NSEC3_HASH_LEN);
245
	const uint8_t *hash_raw = knot_db_val_bound(key) - NSEC3_HASH_LEN;
246
			/* CACHE_KEY_DEF ^^ */
247 248
	int len = base32hex_encode(hash_raw, NSEC3_HASH_LEN, (uint8_t *)text,
				   NSEC3_HASH_TXT_LEN);
249
	assert(len == NSEC3_HASH_TXT_LEN); (void)len;
250 251 252 253 254
	text[NSEC3_HASH_TXT_LEN] = '\0';
}

/** Reconstruct a name into a buffer (assuming length at least KNOT_DNAME_MAXLEN).
 * \return kr_ok() or error code (<0). */
Vladimír Čunát's avatar
Vladimír Čunát committed
255
static int dname_wire_reconstruct(knot_dname_t *buf, const knot_dname_t *zname,
256
				  const uint8_t *hash_raw)
Vladimír Čunát's avatar
Vladimír Čunát committed
257
{
258
	int len = base32hex_encode(hash_raw, NSEC3_HASH_LEN, buf + 1, NSEC3_HASH_TXT_LEN);
Vladimír Čunát's avatar
Vladimír Čunát committed
259 260 261 262 263
	if (len != NSEC3_HASH_TXT_LEN) {
		assert(false);
		return kr_error(EINVAL);
	}
	buf[0] = len;
264 265
	int ret = knot_dname_to_wire(buf + 1 + len, zname, KNOT_DNAME_MAXLEN - 1 - len);
	return ret < 0 ? kr_error(ret) : kr_ok();
Vladimír Čunát's avatar
Vladimír Čunát committed
266 267 268 269 270 271 272 273 274
}

static void nsec3_hash2text(const knot_dname_t *owner, char *text)
{
	assert(owner[0] == NSEC3_HASH_TXT_LEN);
	memcpy(text, owner + 1, MIN(owner[0], NSEC3_HASH_TXT_LEN));
	text[NSEC3_HASH_TXT_LEN] = '\0';
}

275 276 277 278
int nsec3_encloser(struct key *k, struct answer *ans,
		   const int sname_labels, int *clencl_labels,
		   const struct kr_query *qry, struct kr_cache *cache)
{
Vladimír Čunát's avatar
Vladimír Čunát committed
279
	static const int ESKIP = ABS(ENOENT);
280
	/* Basic sanity check. */
Vladimír Čunát's avatar
Vladimír Čunát committed
281
	const bool ok = k && k->zname && ans && clencl_labels
282 283 284 285 286
			&& qry && cache;
	if (!ok) {
		assert(!EINVAL);
		return kr_error(EINVAL);
	}
Vladimír Čunát's avatar
Vladimír Čunát committed
287

288 289 290
	/*** Find the closest encloser - cycle: name starting at sname,
	 * proceeding while longer than zname, shortening by one label on step.
	 * We need a pair where a name doesn't exist *and* its parent does. */
291 292 293 294
		/* LATER(optim.): perhaps iterate in the other order - that
		 * should help significantly against deep queries where we have
		 * a shallow proof in the cache.  We can also optimize by using
		 * only exact search unless we had a match in the previous iteration. */
Vladimír Čunát's avatar
Vladimír Čunát committed
295
	const int zname_labels = knot_dname_labels(k->zname, NULL);
296
	int last_nxproven_labels = -1;
Vladimír Čunát's avatar
Vladimír Čunát committed
297
	const knot_dname_t *name = qry->sname;
298
	for (int name_labels = sname_labels; name_labels >= zname_labels;
Vladimír Čunát's avatar
Vladimír Čunát committed
299
					--name_labels, name += 1 + name[0]) {
300 301
		/* Find a previous-or-equal NSEC3 in cache covering the name,
		 * checking TTL etc. */
Vladimír Čunát's avatar
.  
Vladimír Čunát committed
302
		const knot_db_val_t key = key_NSEC3_name(k, name, false, &ans->nsec_p);
Vladimír Čunát's avatar
Vladimír Čunát committed
303
		if (!key.data) continue;
304 305
		WITH_VERBOSE(qry) {
			char hash_txt[NSEC3_HASH_TXT_LEN + 1];
306
			key_NSEC3_hash2text(key, hash_txt);
307
			VERBOSE_MSG(qry, "=> NSEC3 depth %d: hash %s\n",
308 309
					name_labels - zname_labels, hash_txt);
		}
Vladimír Čunát's avatar
Vladimír Čunát committed
310 311 312 313
		knot_db_val_t val = { NULL, 0 };
		bool exact_match;
		uint32_t new_ttl;
		const uint8_t *hash_low;
314
		const char *err = find_leq_NSEC3(cache, qry, key, k, &ans->nsec_p, &val,
Vladimír Čunát's avatar
Vladimír Čunát committed
315 316 317 318 319 320 321 322 323
						 &exact_match, &hash_low, &new_ttl);
		if (err) {
			WITH_VERBOSE(qry) {
				auto_free char *name_str = kr_dname_text(name);
				VERBOSE_MSG(qry, "=> NSEC3 encloser error for %s: %s\n",
						name_str, err);
			}
			continue;
		}
324 325 326 327 328
		if (exact_match && name_labels != sname_labels
				&& name_labels + 1 != last_nxproven_labels) {
			/* This name exists (checked rank and TTL), and it's
			 * neither of the two interesting cases, so we do not
			 * keep searching for non-existence above this name. */
Vladimír Čunát's avatar
Vladimír Čunát committed
329 330 331 332
			VERBOSE_MSG(qry,
				"=> NSEC3 encloser: only found existence of an ancestor\n");
			return ESKIP;
		}
333 334 335 336 337
		/* Optimization: avoid the rest of the last iteration if pointless. */
		if (!exact_match && name_labels == zname_labels
		    && last_nxproven_labels != name_labels + 1) {
			break;
		}
Vladimír Čunát's avatar
Vladimír Čunát committed
338

339 340
		/* Basic checks OK -> materialize data, cleaning any previous
		 * records on that answer index (unsuccessful attempts). */
341
		knot_dname_t owner[KNOT_DNAME_MAXLEN];
342 343 344 345
		{
			int ret = dname_wire_reconstruct(owner, k->zname, hash_low);
			if (unlikely(ret)) continue;
		}
346
		const int ans_id = (exact_match && name_labels + 1 == last_nxproven_labels)
347
				 ? AR_CPE : AR_NSEC;
Vladimír Čunát's avatar
Vladimír Čunát committed
348 349
		{
			const struct entry_h *nsec_eh = val.data;
350
			memset(&ans->rrsets[ans_id], 0, sizeof(ans->rrsets[ans_id]));
351
			int ret = entry2answer(ans, ans_id, nsec_eh, knot_db_val_bound(val),
Vladimír Čunát's avatar
Vladimír Čunát committed
352 353 354
						owner, KNOT_RRTYPE_NSEC3, new_ttl);
			if (ret) return kr_error(ret);
		}
355

356 357 358 359 360 361 362 363 364 365 366
		if (!exact_match) {
			/* Non-existence proven, but we don't know if `name`
			 * is the next closer name.
			 * Note: we don't need to check for the sname being
			 * delegated away by this record, as with NSEC3 only
			 * *exact* match on an ancestor could do that. */
			last_nxproven_labels = name_labels;
			WITH_VERBOSE(qry) {
				char hash_low_txt[NSEC3_HASH_TXT_LEN + 1];
				nsec3_hash2text(owner, hash_low_txt);
				VERBOSE_MSG(qry,
367
					"=> NSEC3 depth %d: covered by %s -> TODO, new TTL %d\n",
368 369 370 371 372 373 374
					name_labels - zname_labels, hash_low_txt, new_ttl);
			}
			continue;
		}

		/* Exactly matched NSEC3: two cases, one after another. */
		const knot_rrset_t *nsec_rr = ans->rrsets[ans_id].set.rr;
375 376
		const uint8_t *bm = knot_nsec3_bitmap(nsec_rr->rrs.rdata);
		uint16_t bm_size = knot_nsec3_bitmap_len(nsec_rr->rrs.rdata);
377 378
		assert(bm);
		if (name_labels == sname_labels) {
Vladimír Čunát's avatar
Vladimír Čunát committed
379 380 381 382 383 384 385 386 387 388 389 390
			if (kr_nsec_bitmap_nodata_check(bm, bm_size, qry->stype,
							nsec_rr->owner) != 0) {
				VERBOSE_MSG(qry,
					"=> NSEC3 sname: match but failed type check\n");
				return ESKIP;
			}
			/* NODATA proven; just need to add SOA+RRSIG later */
			VERBOSE_MSG(qry,
				"=> NSEC3 sname: match proved NODATA, new TTL %d\n",
				new_ttl);
			ans->rcode = PKT_NODATA;
			return kr_ok();
391

Vladimír Čunát's avatar
Vladimír Čunát committed
392 393
		} /* else */

394 395
		assert(name_labels + 1 == last_nxproven_labels);
		if (kr_nsec_children_in_zone_check(bm, bm_size) != 0) {
Vladimír Čunát's avatar
Vladimír Čunát committed
396
			VERBOSE_MSG(qry,
397
				"=> NSEC3 encloser: found but delegated (or error)\n");
Vladimír Čunát's avatar
Vladimír Čunát committed
398 399
			return ESKIP;
		}
400
		/* NXDOMAIN proven *except* for wildcards. */
Vladimír Čunát's avatar
Vladimír Čunát committed
401
		WITH_VERBOSE(qry) {
402
			auto_free char *name_str = kr_dname_text(name);
Vladimír Čunát's avatar
Vladimír Čunát committed
403
			VERBOSE_MSG(qry,
404
				"=> NSEC3 encloser: confirmed as %s, new TTL %d\n",
405
				name_str, new_ttl);
406 407 408 409
		}
		*clencl_labels = name_labels;
		ans->rcode = PKT_NXDOMAIN;
		/* Avoid repeated NSEC3 - remove either if the hashes match.
Vladimír Čunát's avatar
.  
Vladimír Čunát committed
410 411 412
		 * This is very unlikely in larger zones: 1/size (per attempt).
		 * Well, deduplication would happen anyway when the answer
		 * from cache is read by kresd (internally). */
413
		if (unlikely(0 == memcmp(ans->rrsets[AR_NSEC].set.rr->owner + 1,
414
					 ans->rrsets[AR_CPE ].set.rr->owner + 1,
415
					 NSEC3_HASH_LEN))) {
416 417
			memset(&ans->rrsets[AR_CPE], 0, sizeof(ans->rrsets[AR_CPE]));
			/* LATER(optim.): perhaps check this earlier and avoid some work? */
Vladimír Čunát's avatar
Vladimír Čunát committed
418
		}
419
		return kr_ok();
Vladimír Čunát's avatar
Vladimír Čunát committed
420 421
	}

422 423 424 425 426 427 428 429
	/* We've ran out of options. */
	if (last_nxproven_labels > 0) {
		/* We didn't manage to prove existence of the closest encloser,
		 * meaning the only chance left is a *positive* wildcard record. */
		*clencl_labels = last_nxproven_labels - 1;
		ans->rcode = PKT_NXDOMAIN;
		/* FIXME: review */
	}
430
	return ESKIP;
431 432 433 434 435
}

int nsec3_src_synth(struct key *k, struct answer *ans, const knot_dname_t *clencl_name,
		    const struct kr_query *qry, struct kr_cache *cache)
{
436 437
	/* Find a previous-or-equal NSEC3 in cache covering or matching
	 * the source of synthesis, checking TTL etc. */
Vladimír Čunát's avatar
.  
Vladimír Čunát committed
438
	const knot_db_val_t key = key_NSEC3_name(k, clencl_name, true, &ans->nsec_p);
439 440 441 442 443 444 445 446 447 448
	if (!key.data) return kr_error(1);
	WITH_VERBOSE(qry) {
		char hash_txt[NSEC3_HASH_TXT_LEN + 1];
		key_NSEC3_hash2text(key, hash_txt);
		VERBOSE_MSG(qry, "=> NSEC3 wildcard: hash %s\n", hash_txt);
	}
	knot_db_val_t val = { NULL, 0 };
	bool exact_match;
	uint32_t new_ttl;
	const uint8_t *hash_low;
449
	const char *err = find_leq_NSEC3(cache, qry, key, k, &ans->nsec_p, &val,
450 451 452 453 454 455
					 &exact_match, &hash_low, &new_ttl);
	if (err) {
		VERBOSE_MSG(qry, "=> NSEC3 wildcard: %s\n", err);
		return kr_ok();
	}

Vladimír Čunát's avatar
.  
Vladimír Čunát committed
456
	/* LATER(optim.): avoid duplicities in answer. */
457 458 459 460 461

	/* Basic checks OK -> materialize the data (speculatively). */
	knot_dname_t owner[KNOT_DNAME_MAXLEN];
	{
		int ret = dname_wire_reconstruct(owner, k->zname, hash_low);
462
		if (unlikely(ret)) return kr_ok();
463
		const struct entry_h *nsec_eh = val.data;
464
		ret = entry2answer(ans, AR_WILD, nsec_eh, knot_db_val_bound(val),
465 466 467
				   owner, KNOT_RRTYPE_NSEC3, new_ttl);
		if (ret) return kr_error(ret);
	}
Vladimír Čunát's avatar
.  
Vladimír Čunát committed
468
	const knot_rrset_t *nsec_rr = ans->rrsets[AR_WILD].set.rr;
469 470 471 472 473 474 475

	if (!exact_match) {
		/* The record proves wildcard non-existence. */
		WITH_VERBOSE(qry) {
			char hash_low_txt[NSEC3_HASH_TXT_LEN + 1];
			nsec3_hash2text(owner, hash_low_txt);
			VERBOSE_MSG(qry,
476
				"=> NSEC3 wildcard: covered by %s -> TODO, new TTL %d\n",
477 478 479 480
				hash_low_txt, new_ttl);
		}
		return AR_SOA;
	}
481

Vladimír Čunát's avatar
.  
Vladimír Čunát committed
482
	/* The wildcard exists.  Find if it's NODATA - check type bitmap. */
483 484
	const uint8_t *bm = knot_nsec3_bitmap(nsec_rr->rrs.rdata);
	uint16_t bm_size = knot_nsec3_bitmap_len(nsec_rr->rrs.rdata);
Vladimír Čunát's avatar
.  
Vladimír Čunát committed
485 486 487 488 489 490 491
	assert(bm);
	if (kr_nsec_bitmap_nodata_check(bm, bm_size, qry->stype, nsec_rr->owner) == 0) {
		/* NODATA proven; just need to add SOA+RRSIG later */
		VERBOSE_MSG(qry, "=> NSEC3 wildcard: match proved NODATA, new TTL %d\n",
				 new_ttl);
		ans->rcode = PKT_NODATA;
		return AR_SOA;
492

Vladimír Čunát's avatar
.  
Vladimír Čunát committed
493 494 495 496 497 498
	} /* else */
	/* The data probably exists -> don't add this NSEC3
	 * and (later) try to find the real wildcard data */
	VERBOSE_MSG(qry, "=> NSEC3 wildcard: should exist (or error)\n");
	ans->rcode = PKT_NOERROR;
	memset(&ans->rrsets[AR_WILD], 0, sizeof(ans->rrsets[AR_WILD]));
499
	return kr_ok();
500 501
}