return ret;
}
-
-
-struct nsec_p {
- struct {
- uint8_t salt_len;
- uint8_t alg;
- uint16_t iters;
- } s;
- uint8_t *salt;
-};
-
/* When going stricter, BEWARE of breaking entry_h_consistent_NSEC() */
struct entry_h * entry_h_consistent(knot_db_val_t data, uint16_t type)
{
}
int32_t res = entry->ttl - diff;
if (res < 0 && owner && qry && qry->stale_cb) {
- /* Stale-serving decision. FIXME: modularize or make configurable, etc. */
+ /* Stale-serving decision, delegated to a callback. */
int res_stale = qry->stale_cb(res, owner, type, qry);
if (res_stale >= 0)
return res_stale;
return ret;
}
+static int nsec_p_init(struct nsec_p *nsec_p, const uint8_t *nsec_p_raw)
+{
+ nsec_p->raw = nsec_p_raw;
+ if (!nsec_p_raw) return kr_ok();
+ nsec_p->hash = nsec_p_mkHash(nsec_p->raw);
+ /* Convert NSEC3 params to another format. */
+ const dnssec_binary_t rdata = {
+ .size = nsec_p_rdlen(nsec_p->raw),
+ .data = (uint8_t *)/*const-cast*/nsec_p->raw,
+ };
+ int ret = dnssec_nsec3_params_from_rdata(&nsec_p->libknot, &rdata);
+ return ret == DNSSEC_EOK ? kr_ok() : kr_error(ret);
+}
+
+static void nsec_p_cleanup(struct nsec_p *nsec_p)
+{
+ dnssec_binary_free(&nsec_p->libknot.salt);
+ /* We don't really need to clear it, but it's not large. (`salt` zeroed above) */
+ memset(nsec_p, 0, sizeof(*nsec_p));
+}
+
/**
* \note we don't transition to KR_STATE_FAIL even in case of "unexpected errors".
*/
memcpy(&stamp, el[i].data, sizeof(stamp));
const int32_t remains = stamp - qry->timestamp.tv_sec; /* using SOA serial arith. */
if (remains < 0) goto cont;
- ans.nsec_p = el[i].len > sizeof(stamp) ?
- el[i].data + sizeof(stamp) : NULL;
+ {
+ const uint8_t *nsec_p_raw = el[i].len > sizeof(stamp)
+ ? el[i].data + sizeof(stamp) : NULL;
+ nsec_p_init(&ans.nsec_p, nsec_p_raw);
+ }
/**** 2. and 3. inside */
ret = peek_encloser(k, &ans, sname_labels,
lowest_rank, qry, cache);
+ nsec_p_cleanup(&ans.nsec_p);
if (!ret) break;
if (ret < 0) return ctx->state;
cont:
/**** 2. Find a closest (provable) encloser (of sname). */
int clencl_labels = -1;
bool clencl_is_tentative = false;
- if (!ans->nsec_p) { /* NSEC */
+ if (!ans->nsec_p.raw) { /* NSEC */
int ret = nsec1_encloser(k, ans, sname_labels, &clencl_labels,
&cover_low_kwz, &cover_hi_kwz, qry, cache);
if (ret) return ret;
} else {
int ret = nsec3_encloser(k, ans, sname_labels, &clencl_labels,
- &cover_low_kwz, &cover_hi_kwz, qry, cache);
+ qry, cache);
clencl_is_tentative = ret == ABS(ENOENT) && clencl_labels >= 0;
/* ^^ Last chance: *positive* wildcard record under this clencl. */
if (ret && !clencl_is_tentative) return ret;
/**** 3. source of synthesis checks, in case the next closer name was covered.
**** 3a. We want to query for NSEC* of source of synthesis (SS) or its
* predecessor, providing us with a proof of its existence or non-existence. */
- if (ncloser_covered && !ans->nsec_p) {
+ if (ncloser_covered && !ans->nsec_p.raw) {
int ret = nsec1_src_synth(k, ans, clencl_name,
cover_low_kwz, cover_hi_kwz, qry, cache);
if (ret == AR_SOA) return 0;
assert(ret <= 0);
if (ret) return ret;
- } else if (ncloser_covered && ans->nsec_p && !clencl_is_tentative) {
- int ret = nsec3_src_synth(k, ans, clencl_name,
- cover_low_kwz, cover_hi_kwz, qry, cache);
+ } else if (ncloser_covered && ans->nsec_p.raw && !clencl_is_tentative) {
+ int ret = nsec3_src_synth(k, ans, clencl_name, qry, cache);
if (ret == AR_SOA) return 0;
assert(ret <= 0);
if (ret) return ret;
}
-/** Try to satisfy via wildcard. See the single call site. */
+/** Try to satisfy via wildcard (positively). See the single call site. */
static int try_wild(struct key *k, struct answer *ans, const knot_dname_t *clencl_name,
const uint16_t type, const uint8_t lowest_rank,
const struct kr_query *qry, struct kr_cache *cache)
#include <stdbool.h>
#include <stdint.h>
+#include <dnssec/error.h>
+#include <dnssec/nsec.h>
#include <libknot/consts.h>
#include <libknot/db/db.h>
#include <libknot/dname.h>
/** Dematerialize a rdataset. */
int rdataset_dematerialize(const knot_rdataset_t *rds, void * restrict data);
+/** NSEC* parameters; almost nothing is meaningful for NSEC. */
+struct nsec_p {
+ const uint8_t *raw; /**< Pointer to raw NSEC3 parameters; NULL for NSEC. */
+ nsec_p_hash_t hash; /**< Hash of `raw`, used for cache keys. */
+ dnssec_nsec3_params_t libknot; /**< Format for libknot; owns malloced memory! */
+};
+
/** Partially constructed answer when gathering RRsets from cache. */
struct answer {
- int rcode; /**< PKT_NODATA, etc. */
- const uint8_t *nsec_p; /**< Let's avoid mixing different NSEC* parameters in one answer. */
- knot_mm_t *mm; /**< Allocator for rrsets */
+ int rcode; /**< PKT_NODATA, etc. */
+ struct nsec_p nsec_p; /**< Don't mix different NSEC* parameters in one answer. */
+ knot_mm_t *mm; /**< Allocator for rrsets */
struct answer_rrset {
ranked_rr_array_entry_t set; /**< set+rank for the main data */
knot_rdataset_t sig_rds; /**< RRSIG data, if any */
/** TODO. See nsec1_encloser(...) */
int nsec3_encloser(struct key *k, struct answer *ans,
const int sname_labels, int *clencl_labels,
- knot_db_val_t *cover_low_kwz, knot_db_val_t *cover_hi_kwz,
const struct kr_query *qry, struct kr_cache *cache);
/** TODO. See nsec1_src_synth(...) */
int nsec3_src_synth(struct key *k, struct answer *ans, const knot_dname_t *clencl_name,
- knot_db_val_t cover_low_kwz, knot_db_val_t cover_hi_kwz,
const struct kr_query *qry, struct kr_cache *cache);
#include "lib/dnssec/nsec.h"
#include "lib/layer/iterate.h"
-#include <dnssec/error.h>
-#include <dnssec/nsec.h>
#include <libknot/rrtype/nsec3.h>
static const knot_db_val_t VAL_EMPTY = { NULL, 0 };
/** 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,
- const bool add_wildcard,
- const nsec_p_hash_t nsec_p_hash, const dnssec_nsec3_params_t *nsec3_p)
+ const bool add_wildcard, const struct nsec_p *nsec_p)
{
- knot_db_val_t val = key_NSEC3_common(k, k->zname, nsec_p_hash);
- const bool ok = val.data && nsec3_p;
+ bool ok = k && name && nsec_p && nsec_p->raw;
if (!ok) return VAL_EMPTY;
+ knot_db_val_t val = key_NSEC3_common(k, k->zname, nsec_p->hash);
+ if (!val.data) return val;
/* Make `name` point to correctly wildcarded owner name. */
uint8_t buf[KNOT_DNAME_MAXLEN];
.data = val.data + val.len,
};
/* FIXME: vv this requires a patched libdnssec - tries to realloc() */
- int ret = dnssec_nsec3_hash(&dname, nsec3_p, &hash);
+ int ret = dnssec_nsec3_hash(&dname, &nsec_p->libknot, &hash);
if (ret != DNSSEC_EOK) return VAL_EMPTY;
assert(hash.size == NSEC3_HASH_LEN);
val.len += hash.size;
int nsec3_encloser(struct key *k, struct answer *ans,
const int sname_labels, int *clencl_labels,
- knot_db_val_t *cover_low_kwz, knot_db_val_t *cover_hi_kwz,
const struct kr_query *qry, struct kr_cache *cache)
/* TODO: cleanup params */
{
static const int ESKIP = ABS(ENOENT);
/* Basic sanity check. */
const bool ok = k && k->zname && ans && clencl_labels
- && cover_low_kwz && cover_hi_kwz
&& qry && cache;
if (!ok) {
assert(!EINVAL);
return kr_error(EINVAL);
}
- const nsec_p_hash_t nsec_p_hash = nsec_p_mkHash(ans->nsec_p);
- /* Convert NSEC3 params to another format. */
- dnssec_nsec3_params_t nsec3_p;
- {
- const dnssec_binary_t rdata = {
- .size = nsec_p_rdlen(ans->nsec_p),
- .data = (uint8_t *)/*const-cast*/ans->nsec_p,
- };
- int ret = dnssec_nsec3_params_from_rdata(&nsec3_p, &rdata);
- if (ret != DNSSEC_EOK) return kr_error(ret);
- }
-
/*** 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. */
--name_labels, name += 1 + name[0]) {
/* Find a previous-or-equal NSEC3 in cache covering the name,
* checking TTL etc. */
- const knot_db_val_t key =
- key_NSEC3_name(k, name, false, nsec_p_hash, &nsec3_p);
+ const knot_db_val_t key = key_NSEC3_name(k, name, false, &ans->nsec_p);
if (!key.data) continue;
WITH_VERBOSE(qry) {
char hash_txt[NSEC3_HASH_TXT_LEN + 1];
*clencl_labels = name_labels;
ans->rcode = PKT_NXDOMAIN;
/* Avoid repeated NSEC3 - remove either if the hashes match.
- * This is very unlikely in larger zones: 1/size (per attempt). */
+ * 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). */
if (unlikely(0 == memcmp(ans->rrsets[AR_NSEC].set.rr->owner + 1,
ans->rrsets[AR_CPE ].set.rr->owner + 1,
NSEC3_HASH_LEN))) {
}
int nsec3_src_synth(struct key *k, struct answer *ans, const knot_dname_t *clencl_name,
- knot_db_val_t cover_low_kwz, knot_db_val_t cover_hi_kwz,
const struct kr_query *qry, struct kr_cache *cache)
/* TODO: cleanup params */
{
- /* TODO: deduplicate this code block, *including* the recomputation. */
- const nsec_p_hash_t nsec_p_hash = nsec_p_mkHash(ans->nsec_p);
- /* Convert NSEC3 params to another format. */
- dnssec_nsec3_params_t nsec3_p;
- {
- const dnssec_binary_t rdata = {
- .size = nsec_p_rdlen(ans->nsec_p),
- .data = (uint8_t *)/*const-cast*/ans->nsec_p,
- };
- int ret = dnssec_nsec3_params_from_rdata(&nsec3_p, &rdata);
- if (ret != DNSSEC_EOK) return kr_error(ret);
- }
-
/* Find a previous-or-equal NSEC3 in cache covering or matching
* the source of synthesis, checking TTL etc. */
- const knot_db_val_t key =
- key_NSEC3_name(k, clencl_name, true, nsec_p_hash, &nsec3_p);
+ const knot_db_val_t key = key_NSEC3_name(k, clencl_name, true, &ans->nsec_p);
if (!key.data) return kr_error(1);
WITH_VERBOSE(qry) {
char hash_txt[NSEC3_HASH_TXT_LEN + 1];
return kr_ok();
}
- /* FIXME: avoid duplicities in answer. */
+ /* LATER(optim.): avoid duplicities in answer. */
/* Basic checks OK -> materialize the data (speculatively). */
knot_dname_t owner[KNOT_DNAME_MAXLEN];
owner, KNOT_RRTYPE_NSEC3, new_ttl);
if (ret) return kr_error(ret);
}
-
- //const knot_rrset_t *nsec_rr = ans->rrsets[AR_WILD].set.rr;
+ const knot_rrset_t *nsec_rr = ans->rrsets[AR_WILD].set.rr;
if (!exact_match) {
/* The record proves wildcard non-existence. */
return AR_SOA;
}
+ /* The wildcard exists. Find if it's NODATA - check type bitmap. */
+ uint8_t *bm = NULL;
+ uint16_t bm_size;
+ knot_nsec3_bitmap(&nsec_rr->rrs, 0, &bm, &bm_size);
+ 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;
- /* FIXME XXX */
- VERBOSE_MSG(qry, "=> NSEC3 wildcard: failed!\n");
+ } /* 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]));
return kr_ok();
}