]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
.
authorVladimír Čunát <vladimir.cunat@nic.cz>
Wed, 18 Oct 2017 16:10:27 +0000 (18:10 +0200)
committerVladimír Čunát <vladimir.cunat@nic.cz>
Wed, 18 Oct 2017 16:10:27 +0000 (18:10 +0200)
lib/cache.c
lib/utils.c
lib/utils.h

index 5c7657185a0e17f6e1d74e8e2d340b184c01899f..49f97049ba6daa7c3c32040f613db4091fcfcb76 100644 (file)
@@ -59,7 +59,7 @@ static inline int cache_clear(struct kr_cache *cache)
 static int assert_right_version(struct kr_cache *cache)
 {
        /* Check cache ABI version */
-       uint8_t key_str[] = "\x00\x00V"; /* CACHE_KEY; zero-term. but we don't care */
+       uint8_t key_str[] = "\x00\x00V"; /* CACHE_KEY_DEF; zero-term. but we don't care */
        knot_db_val_t key = { .data = key_str, .len = sizeof(key_str) };
        knot_db_val_t val = { };
        int ret = cache_op(cache, read, &key, &val, 1);
@@ -495,6 +495,7 @@ int kr_cache_insert_rrsig(struct kr_cache *cache, const knot_rrset_t *rr, uint8_
        return kr_cache_insert(cache, KR_CACHE_SIG, rr->owner, covered, &header, data);
 }
 
+#include "lib/dnssec/nsec.h"
 #include "lib/dnssec/ta.h"
 #include "lib/layer/iterate.h"
 #include "lib/resolve.h"
@@ -533,15 +534,23 @@ struct nsec_p {
 };
 
 /** Check basic consistency of entry_h, not looking into ->data.
- * \note only exact hits are really considered ATM. */
+ * \note only exact hits and NSEC1 are really considered ATM. */
 static struct entry_h * entry_h_consistent(knot_db_val_t data, uint16_t ktype)
 {
        if (data.len < offsetof(struct entry_h, data))
                return NULL;
        const struct entry_h *eh = data.data;
        bool ok = true;
-       if (eh->is_negative)
-               ok = ok && !kr_rank_test(eh->rank, KR_RANK_SECURE);
+
+       switch (ktype) {
+       case KNOT_RRTYPE_NSEC:
+               ok = ok && !(eh->is_negative || eh->has_ns || eh->has_cname
+                               || eh->has_dname);
+               break;
+       default:
+               if (eh->is_negative)
+                       ok = ok && !kr_rank_test(eh->rank, KR_RANK_SECURE);
+       }
 
        //LATER: rank sanity
        return ok ? /*const-cast*/(struct entry_h *)eh : NULL;
@@ -556,6 +565,34 @@ struct key {
 };
 
 
+
+static int32_t get_new_ttl(const struct entry_h *entry, uint32_t current_time)
+{
+       int32_t diff = current_time - entry->time;
+       if (diff < 0) {
+               /* We may have obtained the record *after* the request started. */
+               diff = 0;
+       }
+       int32_t res = entry->ttl - diff;
+       VERBOSE_MSG(NULL, "TTL remains: %d\n", (int)res);
+       return res;
+}
+int32_t kr_cache_ttl(const struct kr_cache_p *peek, uint32_t current_time)
+{
+       const struct entry_h *eh = peek->raw_data;
+       return get_new_ttl(eh, current_time);
+}
+
+/** Record is expiring if it has less than 1% TTL (or less than 5s) */
+static bool is_expiring(uint32_t orig_ttl, uint32_t new_ttl)
+{
+       int64_t nttl = new_ttl; /* avoid potential over/under-flow */
+       return 100 * (nttl - 5) < orig_ttl;
+}
+
+
+
+
 /* forwards for larger chunks of code */
 
 static uint8_t get_lowest_rank(const struct kr_request *req, const struct kr_query *qry);
@@ -566,6 +603,21 @@ static const struct entry_h *closest_NS(kr_layer_t *ctx, struct key *k);
 
 
 
+struct answer {
+       int rcode;      /**< PKT_NODATA, etc. ?? */
+       uint8_t nsec_v; /**< 1 or 3 */
+       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 */
+       } rrsets[1+1+3]; /**< answer, SOA, 3*NSECx (at most, all optional) */
+};
+enum {
+       AR_ANSWER = 0,
+       AR_SOA,
+       AR_NSEC,
+};
+
 
 
 /* TODO: move rdataset_* and pkt_* functions into a separate c-file. */
@@ -622,6 +674,58 @@ int kr_cache_materialize(knot_rdataset_t *dst, const struct kr_cache_p *ref,
        return rdataset_materialize(dst, eh->data, ref->raw_bound, new_ttl, pool);
 }
 
+
+/** Materialize RRset + RRSIGs into ans->rrsets[id].
+ * LATER(optim.): it's slightly wasteful that we allocate knot_rrset_t for the packet
+ */
+static int entry2answer(struct answer *ans, int id,
+               const struct entry_h *eh, const void *eh_bound,
+               const knot_dname_t *owner, uint16_t type, uint32_t new_ttl)
+{
+       /* We assume it's zeroed.  Do basic sanity check. */
+       if (ans->rrsets[id].set.rr || ans->rrsets[id].sig_rds.data
+           || (type == KNOT_RRTYPE_NSEC && ans->nsec_v != 1)
+           || (type == KNOT_RRTYPE_NSEC3 && ans->nsec_v != 3)) {
+               assert(false);
+               return kr_error(EINVAL);
+       }
+       /* Materialize the base RRset. */
+       knot_rrset_t *rr = ans->rrsets[id].set.rr
+               = knot_rrset_new(owner, type, KNOT_CLASS_IN, ans->mm);
+       if (!rr) return kr_error(ENOMEM);
+       int ret = rdataset_materialize(&rr->rrs, eh->data, eh_bound, new_ttl, ans->mm);
+       if (ret < 0) goto fail;
+       size_t data_off = ret;
+       ans->rrsets[id].set.expiring = is_expiring(eh->ttl, new_ttl);
+       /* Materialize the RRSIG RRset for the answer in (pseudo-)packet. */
+       bool want_rrsigs = kr_rank_test(eh->rank, KR_RANK_SECURE);
+                       //^^ TODO: vague; function parameter instead?
+       if (want_rrsigs) {
+               ret = rdataset_materialize(&ans->rrsets[id].sig_rds, eh->data + data_off,
+                                          eh_bound, new_ttl, ans->mm);
+               if (ret < 0) goto fail;
+
+               // TODO
+               #if 0
+               /* sanity check: we consumed exactly all data */
+               int unused_bytes = eh_bound - (void *)eh->data - data_off - ret;
+               if (ktype != KNOT_RRTYPE_NS && unused_bytes) {
+                       /* ^^ it doesn't have to hold in multi-RRset entries; LATER: more checks? */
+                       VERBOSE_MSG(qry, "BAD?  Unused bytes: %d\n", unused_bytes);
+               }
+               #endif
+       }
+       return kr_ok();
+fail:
+       /* Cleanup the item that we might've (partially) written to. */
+       knot_rrset_free(&ans->rrsets[id].set.rr, ans->mm);
+       knot_rdataset_clear(&ans->rrsets[id].sig_rds, ans->mm);
+       memset(&ans->rrsets[id], 0, sizeof(ans->rrsets[id]));
+       return kr_error(ret);
+}
+
+
+
 /** Compute size of dematerialized rdataset.  NULL is accepted as empty set. */
 static int rdataset_dematerialize_size(const knot_rdataset_t *rds)
 {
@@ -730,25 +834,37 @@ int pkt_alloc_space(knot_pkt_t *pkt, int count)
        return kr_ok();
 }
 
-/** Append an RRset into the current section (*shallow* copy), with given rank.
- * \note it works with empty set as well (skipped). */
-int pkt_append(knot_pkt_t *pkt, const knot_rrset_t *rrset, uint8_t rank)
+/** Append RRset + its RRSIGs into the current section (*shallow* copy), with given rank.
+ * \note it works with empty set as well (skipped).
+ * \note KNOT_CLASS_IN is assumed
+ */
+int pkt_append(knot_pkt_t *pkt, const struct answer_rrset *rrset, uint8_t rank)
 {
-       if (!rrset->rrs.rr_count) {
-               return kr_ok();
-       }
        /* allocate space, to be sure */
-       int ret = pkt_alloc_space(pkt, 1);
+       int rrset_cnt = (rrset->set.rr->rrs.rr_count > 0) + (rrset->sig_rds.rr_count > 0);
+       int ret = pkt_alloc_space(pkt, rrset_cnt);
        if (ret) return kr_error(ret);
-       /* allocate rank */
-       uint8_t *rr_rank = mm_alloc(&pkt->mm, sizeof(*rr_rank));
-       if (!rr_rank) return kr_error(ENOMEM);
-       *rr_rank = rank;
-       /* append the RR array */
-       pkt->rr[pkt->rrset_count] = *rrset;
-       pkt->rr[pkt->rrset_count].additional = rr_rank;
-       ++pkt->rrset_count;
-       ++(pkt->sections[pkt->current].count);
+       /* write both sets */
+       const knot_rdataset_t *rdss[2] = { &rrset->set.rr->rrs, &rrset->sig_rds };
+       for (int i = 0; i < 2; ++i) {
+               /* allocate rank */
+               uint8_t *rr_rank = mm_alloc(&pkt->mm, sizeof(*rr_rank));
+               if (!rr_rank) return kr_error(ENOMEM);
+               *rr_rank = (i == 0) ? rank : (KR_RANK_INITIAL | KR_RANK_AUTH);
+                       /* rank for RRSIGs isn't really useful: ^^ */
+               /* append the RR array */
+               pkt->rr[pkt->rrset_count] = (i == 0) ? *rrset->set.rr
+                       : (knot_rrset_t){
+                               .owner = knot_dname_copy(rrset->set.rr->owner, &pkt->mm),
+                                       /* ^^ well, another copy isn't really needed */
+                               .type = KNOT_RRTYPE_RRSIG,
+                               .rclass = KNOT_CLASS_IN,
+                               .rrs = *rdss[i],
+                               .additional = rr_rank,
+                       };
+               ++pkt->rrset_count;
+               ++(pkt->sections[pkt->current].count);
+       }
        return kr_ok();
 }
 
@@ -760,40 +876,35 @@ int pkt_append(knot_pkt_t *pkt, const knot_rrset_t *rrset, uint8_t rank)
 
 
 
-/** TODO
- * CACHE_KEY */
+/** TODO */
 static knot_db_val_t key_exact_type(struct key *k, uint16_t ktype)
 {
        k->buf[k->name_len + 1] = 0; /* make sure different names can never match */
        k->buf[k->name_len + 2] = 'E'; /* tag for exact name+type matches */
        memcpy(k->buf + k->name_len + 3, &ktype, 2);
        k->type = ktype;
-       /* key == dname_lf + '\0' + 'E' + RRTYPE */
+       /* CACHE_KEY_DEF: key == dname_lf + '\0' + 'E' + RRTYPE */
        return (knot_db_val_t){ k->buf + 1, k->name_len + 4 };
 }
 
-static int32_t get_new_ttl(const struct entry_h *entry, uint32_t current_time)
-{
-       int32_t diff = current_time - entry->time;
-       if (diff < 0) {
-               /* We may have obtained the record *after* the request started. */
-               diff = 0;
-       }
-       int32_t res = entry->ttl - diff;
-       VERBOSE_MSG(NULL, "TTL remains: %d\n", (int)res);
-       return res;
-}
-int32_t kr_cache_ttl(const struct kr_cache_p *peek, uint32_t current_time)
-{
-       const struct entry_h *eh = peek->raw_data;
-       return get_new_ttl(eh, current_time);
-}
-
-/** Record is expiring if it has less than 1% TTL (or less than 5s) */
-static bool is_expiring(uint32_t orig_ttl, uint32_t new_ttl)
+/** TODO */
+static knot_db_val_t key_NSEC1(struct key *k, const knot_dname_t *name, int zonename_len)
 {
-       int64_t nttl = new_ttl; /* avoid potential over/under-flow */
-       return 100 * (nttl - 5) < orig_ttl;
+       /* we basically need dname_lf with two bytes added
+        * on a correct place within the name (the cut) */
+       int ret = knot_dname_lf(k->buf, name, NULL);
+       if (ret) {
+               assert(false);
+               return (knot_db_val_t){};
+       }
+       uint8_t *begin = k->buf + zonename_len + 1; /* one byte after zone's zero */
+       uint8_t *end = k->buf + k->buf[0] - 1; /* we don't need final zero */
+       memmove(begin + 2, begin, end - begin);
+       begin[0] = 0;
+       begin[1] = '1'; /* tag for NSEC1 */
+       /* CACHE_KEY_DEF: key == zone's dname_lf + 0 + '1' + dname_lf
+        * of the name within the zone without the final 0 */
+       return (knot_db_val_t){ k->buf + 1, k->buf[0] + 1 };
 }
 
 
@@ -853,7 +964,6 @@ int cache_lmdb_peek(kr_layer_t *ctx, knot_pkt_t *pkt)
                return ctx->state;
        }
 
-       bool expiring = false;
 
        /** 1b. otherwise, find the longest prefix NS/xNAME (with OK time+rank). [...] */
        k->dname = qry->sname;
@@ -898,10 +1008,16 @@ int cache_lmdb_peek(kr_layer_t *ctx, knot_pkt_t *pkt)
 
        return ctx->state;
 
+       /* collecting multiple NSEC* + RRSIG records, in preparation for the answer
+        *  + track the progress
+        */
+       struct answer ans = {};
+       ans.mm = &pkt->mm;
+
        /** 2. closest (provable) encloser.
         * iterate over all NSEC* chain parameters
         */
-       while (true) { //for (int i_nsecp = 0; i
+       //while (true) { //for (int i_nsecp = 0; i
                int nsec = 1;
                switch (nsec) {
                case 1: {
@@ -909,29 +1025,67 @@ int cache_lmdb_peek(kr_layer_t *ctx, knot_pkt_t *pkt)
                         * the QNAME, checking TTL etc. */
                        
                        //nsec_leq()
-                       /* we basically need dname_lf with two bytes added
-                        * on a correct place within the name (the cut) */
-                       int ret = knot_dname_lf(k->buf, qry->sname, NULL);
-                       if (ret) return KR_STATE_FAIL;
-
-                       uint8_t *begin = k->buf + k->name_len + 1; /* one byte after zone's zero */
-                       uint8_t *end = k->buf + k->buf[0] - 1; /* we don't need final zero */
-                       memmove(begin + 2, begin, end - begin);
-                       begin[0] = 0;
-                       begin[1] = '1'; /* tag for NSEC1 */
-                       knot_db_val_t key = { k->buf + 1, k->buf[0] + 1 };
+                       knot_db_val_t key = key_NSEC1(k, qry->sname, k->name_len);
+                       if (!key.data) break; /* FIXME: continue?
+                               similarly: other breaks here - also invalidate ans AR_NSEC */
                        knot_db_val_t val = { };
-                       /* key == zone's dname_lf + 0 + '1' + dname_lf of the name
-                        * within the zone without the final 0 */
                        ret = cache_op(cache, read_leq, &key, &val);
-                       const struct entry_h *eh = val.data; // TODO: entry_h_consistent for NSEC*?
-                       // FIXME: check that it covers the name
-
+                       if (ret < 0) break;
+                       bool exact_match = (ret == 0);
+                       const struct entry_h *eh = entry_h_consistent(val, KNOT_RRTYPE_NSEC);
+                       void *eh_data_bound = val.data + val.len;
+                       if (!eh) break;
                        int32_t new_ttl = get_new_ttl(eh, qry->creation_time.tv_sec);
-                       if (new_ttl < 0 || eh->rank < lowest_rank) {
-                               break; // continue?
+                       if (new_ttl < 0 || !kr_rank_test(eh->rank, KR_RANK_SECURE)) {
+                               break;
+                       }
+
+                       size_t nwz_off = k->name_len + 2;
+                               /* CACHE_KEY_DEF: zone name lf + 0 '1' + name within zone */
+                       if (!exact_match) {
+                               /* FIXME: check that it's still in the zone and covers the name */
+                               bool in_zone = key.len >= nwz_off
+                                       /* CACHE_KEY_DEF */
+                                       && !memcmp(k->buf + 1, key.data, k->name_len + 2);
+                               if (!in_zone) break;
+                       }
+
+                       /* Get owner name of the record. */
+                       const knot_dname_t *owner;
+                       knot_dname_t owner_buf[KNOT_DNAME_MAXLEN];
+                       if (exact_match) {
+                               owner = qry->sname;
+                       } else {
+                               /* Reconstruct from key: first the ending, then zone name. */
+                               ret = knot_dname_lf2wire(owner_buf, key.len - nwz_off,
+                                                        key.data + nwz_off);
+                                       /* CACHE_KEY_DEF */
+                               if (ret) break;
+                               ret = knot_dname_lf2wire(owner_buf + (key.len - nwz_off),
+                                                        key.len, key.data);
+                               if (ret) break;
+                       }
+
+                       /* Basic checks OK -> materialize data. */
+                       ret = entry2answer(&ans, AR_NSEC, eh, eh_data_bound,
+                                          owner, KNOT_RRTYPE_NSEC, new_ttl);
+                       if (ret) break;
+
+                       if (exact_match) {
+                               uint8_t *bm = NULL;
+                               uint16_t bm_size;
+                               knot_nsec_bitmap(&ans.rrsets[AR_NSEC].set.rr->rrs,
+                                                &bm, &bm_size);
+                               if (!bm) break;
+                               if (kr_nsec_bitmap_contains_type(bm, bm_size, qry->stype)) {
+                                       break; /* exact positive answer should exist! */
+                               }
+                               /* NODATA proven; just add SOA+RRSIG */
+                               //FIXME
+
+                       } else { /* inexact match */
+                               //FIXME
                        }
-                       expiring = expiring || is_expiring(eh->ttl, new_ttl);
 
                        break;
                        }
@@ -941,17 +1095,22 @@ int cache_lmdb_peek(kr_layer_t *ctx, knot_pkt_t *pkt)
                default:
                        assert(false);
                }
-       }
+       //}
 
        /** 3. wildcard checks.  FIXME
         */
 
 
+       bool expiring = false; // TODO
        qry->flags.EXPIRING = expiring;
        
        return ctx->state;
 }
 
+
+static int stash_rrset(const ranked_rr_array_t *arr, int arr_i, uint32_t min_ttl,
+                       const struct kr_query *qry, struct kr_cache *cache);
+
 int cache_lmdb_stash(kr_layer_t *ctx, knot_pkt_t *pkt)
 {
        struct kr_request *req = ctx->req;
@@ -968,6 +1127,7 @@ int cache_lmdb_stash(kr_layer_t *ctx, knot_pkt_t *pkt)
 
        const uint32_t min_ttl = MAX(DEFAULT_MINTTL, req->ctx->cache.ttl_min);
        ranked_rr_array_t *selected[] = kr_request_selected(req);
+       int ret = 0;
        for (int psec = KNOT_ANSWER; psec <= KNOT_ADDITIONAL; ++psec) {
                const ranked_rr_array_t *arr = selected[psec];
                /* uncached entries are located at the end */
@@ -977,127 +1137,146 @@ int cache_lmdb_stash(kr_layer_t *ctx, knot_pkt_t *pkt)
                                continue;
                                /* TODO: probably safe to break but maybe not worth it */
                        }
-                       if (entry->cached) {
-                               continue;
-                       }
-                       knot_rrset_t *rr = entry->rr;
-
-                       // for now; TODO
-                       switch (rr->type) {
-                       case KNOT_RRTYPE_RRSIG:
-                       case KNOT_RRTYPE_NSEC:
-                       case KNOT_RRTYPE_NSEC3:
-                               continue;
-                       default:
-                               break;
-                       }
+                       int ret = stash_rrset(arr, i, min_ttl, qry, cache);
+                       if (ret) goto error;
+               }
+       }
+error:
+       kr_cache_sync(cache);
+       return ret ? ret : ctx->state;
+}
 
-                       /* Find corresponding signatures.  LATER(optim.): speed. */
-                       const knot_rrset_t *rr_sigs = NULL;
-                       if (kr_rank_test(entry->rank, KR_RANK_SECURE)) {
-                               for (ssize_t j = arr->len - 1; j >= 0; --j) {
-                                       /* TODO: ATM we assume that some properties are the same
-                                        * for all RRSIGs in the set (esp. label count). */
-                                       ranked_rr_array_entry_t *e = arr->at[j];
-                                       if (e->qry_uid != qry->uid || e->cached
-                                           || e->rr->type != KNOT_RRTYPE_RRSIG
-                                           || knot_rrsig_type_covered(&e->rr->rrs, 0) != rr->type
-                                           || !knot_dname_is_equal(rr->owner, e->rr->owner))
-                                       {
-                                               continue;
-                                       }
-                                       bool is_wild = knot_rrsig_labels(&e->rr->rrs, 0)
-                                               != knot_dname_labels(e->rr->owner, NULL);
-                                       if (is_wild) {
-                                               continue; // FIXME
-                                       }
-                                       rr_sigs = e->rr;
-                                       break;
-                               }
-                       }
+/** It's simply inside of cycle taken out to decrease indentation.
+ * \return kr_ok() or KR_STATE_FAIL */
+static int stash_rrset(const ranked_rr_array_t *arr, int arr_i, uint32_t min_ttl,
+                       const struct kr_query *qry, struct kr_cache *cache)
+{
+       const ranked_rr_array_entry_t *entry = arr->at[arr_i];
+       if (entry->cached) {
+               return kr_ok();
+       }
+       knot_rrset_t *rr = entry->rr;
 
-                       struct key k_storage, *k = &k_storage;
-                       int ret = knot_dname_lf(k->buf, rr->owner, NULL);
-                       if (ret) {
-                               kr_cache_sync(cache);
-                               return KR_STATE_FAIL;
-                       }
-                       k->name_len = k->buf[0];
+       switch (rr->type) {
+       case KNOT_RRTYPE_RRSIG:
+       // for now; FIXME
+       case KNOT_RRTYPE_NSEC3:
+               return kr_ok();
+       default:
+               break;
+       }
 
-                       uint16_t ktype = rr->type;
-                       if (ktype == KNOT_RRTYPE_CNAME || ktype == KNOT_RRTYPE_DNAME) {
-                               assert(false); // FIXME NOTIMPL ATM
-                               ktype = KNOT_RRTYPE_NS;
+       /* Find corresponding signatures.  LATER(optim.): speed. */
+       const knot_rrset_t *rr_sigs = NULL;
+       if (kr_rank_test(entry->rank, KR_RANK_SECURE)) {
+               for (ssize_t j = arr->len - 1; j >= 0; --j) {
+                       /* TODO: ATM we assume that some properties are the same
+                        * for all RRSIGs in the set (esp. label count). */
+                       ranked_rr_array_entry_t *e = arr->at[j];
+                       if (e->qry_uid != qry->uid || e->cached
+                           || e->rr->type != KNOT_RRTYPE_RRSIG
+                           || knot_rrsig_type_covered(&e->rr->rrs, 0) != rr->type
+                           || !knot_dname_is_equal(rr->owner, e->rr->owner))
+                       {
+                               return kr_ok();
                        }
-                       knot_db_val_t key = key_exact_type(k, ktype);
-
-                       if (!kr_rank_test(entry->rank, KR_RANK_SECURE)) {
-                               /* If equal rank was accepted, spoofing a single answer would be enough
-                                * to e.g. override NS record in AUTHORITY section.
-                                * This way they would have to hit the first answer
-                                * (whenever TTL nears expiration). */
-                               knot_db_val_t val = { };
-                               ret = cache_op(cache, read, &key, &val, 1);
-                               struct entry_h *eh;
-                               if (ret == 0 && (eh = entry_h_consistent(val, ktype))) {
-                                       int32_t old_ttl = get_new_ttl(eh, qry->creation_time.tv_sec);
-                                       if (old_ttl > 0 && !is_expiring(old_ttl, eh->ttl)
-                                           && entry->rank <= eh->rank) {
-                                               WITH_VERBOSE {
-                                                       VERBOSE_MSG(qry, "=> not overwriting ");
-                                                       kr_rrtype_print(rr->type, "", " ");
-                                                       kr_dname_print(rr->owner, "", "\n");
-                                               }
-                                               continue;
-                                       }
-                               }
+                       bool is_wild = knot_rrsig_labels(&e->rr->rrs, 0)
+                               != knot_dname_labels(e->rr->owner, NULL);
+                       if (is_wild) {
+                               return kr_ok(); // FIXME, especially for NSEC1!
                        }
+                       rr_sigs = e->rr;
+                       break;
+               }
+       }
 
-                       const knot_rdataset_t *rds_sigs = rr_sigs ? &rr_sigs->rrs : NULL;
-                       /* Compute TTL, just in case they weren't equal. */
-                       uint32_t ttl = -1;
-                       const knot_rdataset_t *rdatasets[] = { &rr->rrs, rds_sigs, NULL };
-                       for (int j = 0; rdatasets[j]; ++j) {
-                               knot_rdata_t *rd = rdatasets[j]->data;
-                               for (uint16_t l = 0; l < rdatasets[j]->rr_count; ++l) {
-                                       ttl = MIN(ttl, knot_rdata_ttl(rd));
-                                       rd = kr_rdataset_next(rd);
-                               }
-                       } /* TODO: consider expirations of RRSIGs as well, just in case. */
-                       ttl = MAX(ttl, min_ttl);
+       int ret = 0;
+       /* Construct the key under which RRs will be stored. */
+       uint16_t ktype = rr->type;
+       struct key k_storage, *k = &k_storage;
+       knot_db_val_t key;
+       switch (ktype) {
+       case KNOT_RRTYPE_NSEC:
+               assert(rr_sigs->rrs.rr_count);
+               int zone_len = knot_dname_size(
+                       knot_rrsig_signer_name(&rr_sigs->rrs, 0));
+               key = key_NSEC1(k, rr->owner, zone_len);
+               break;
+       case KNOT_RRTYPE_CNAME:
+       case KNOT_RRTYPE_DNAME:
+               assert(false); // FIXME NOTIMPL ATM
+               ktype = KNOT_RRTYPE_NS; // fallthrough
+       default:
+               ret = knot_dname_lf(k->buf, rr->owner, NULL);
+               if (ret) return KR_STATE_FAIL;
+               k->name_len = k->buf[0];
 
-                       int rr_ssize = rdataset_dematerialize_size(&rr->rrs);
-                       int storage_size = offsetof(struct entry_h, data) + rr_ssize
-                               + rdataset_dematerialize_size(rds_sigs);
+               key = key_exact_type(k, ktype);
+       }
 
-                       knot_db_val_t val = { .len = storage_size, .data = NULL };
-                       ret = cache_op(cache, write, &key, &val, 1);
-                       if (ret) continue;
-                       struct entry_h *eh = val.data;
-                       *eh = (struct entry_h){
-                               .time = qry->timestamp.tv_sec,
-                               .ttl  = ttl,
-                               .rank = entry->rank,
-                               .has_ns = rr->type == KNOT_RRTYPE_NS,
-                       };
-                       if (rdataset_dematerialize(&rr->rrs, eh->data)
-                           || rdataset_dematerialize(rds_sigs, eh->data + rr_ssize)) {
-                               /* minimize the damage from incomplete write; TODO: better */
-                               eh->ttl = 0;
-                               eh->rank = 0;
-                               assert(false);
-                       }
-                       WITH_VERBOSE {
-                               VERBOSE_MSG(qry, "=> stashed rank: 0%0.2o, ", entry->rank);
-                               kr_rrtype_print(rr->type, "", " ");
-                               kr_dname_print(rr->owner, "", " ");
-                               kr_log_verbose("(%d B)\n", (int)storage_size);
+       if (!kr_rank_test(entry->rank, KR_RANK_SECURE)) {
+               /* If equal rank was accepted, spoofing a single answer would be enough
+                * to e.g. override NS record in AUTHORITY section.
+                * This way they would have to hit the first answer
+                * (whenever TTL nears expiration). */
+               knot_db_val_t val = { };
+               ret = cache_op(cache, read, &key, &val, 1);
+               struct entry_h *eh;
+               if (ret == 0 && (eh = entry_h_consistent(val, ktype))) {
+                       int32_t old_ttl = get_new_ttl(eh, qry->creation_time.tv_sec);
+                       if (old_ttl > 0 && !is_expiring(old_ttl, eh->ttl)
+                           && entry->rank <= eh->rank) {
+                               WITH_VERBOSE {
+                                       VERBOSE_MSG(qry, "=> not overwriting ");
+                                       kr_rrtype_print(rr->type, "", " ");
+                                       kr_dname_print(rr->owner, "", "\n");
+                               }
+                               return kr_ok();
                        }
                }
        }
 
-       kr_cache_sync(cache);
-       return ctx->state;
+       const knot_rdataset_t *rds_sigs = rr_sigs ? &rr_sigs->rrs : NULL;
+       /* Compute TTL, just in case they weren't equal. */
+       uint32_t ttl = -1;
+       const knot_rdataset_t *rdatasets[] = { &rr->rrs, rds_sigs, NULL };
+       for (int j = 0; rdatasets[j]; ++j) {
+               knot_rdata_t *rd = rdatasets[j]->data;
+               for (uint16_t l = 0; l < rdatasets[j]->rr_count; ++l) {
+                       ttl = MIN(ttl, knot_rdata_ttl(rd));
+                       rd = kr_rdataset_next(rd);
+               }
+       } /* TODO: consider expirations of RRSIGs as well, just in case. */
+       ttl = MAX(ttl, min_ttl);
+
+       int rr_ssize = rdataset_dematerialize_size(&rr->rrs);
+       int storage_size = offsetof(struct entry_h, data) + rr_ssize
+               + rdataset_dematerialize_size(rds_sigs);
+
+       knot_db_val_t val = { .len = storage_size, .data = NULL };
+       ret = cache_op(cache, write, &key, &val, 1);
+       if (ret) return kr_ok();
+       struct entry_h *eh = val.data;
+       *eh = (struct entry_h){
+               .time = qry->timestamp.tv_sec,
+               .ttl  = ttl,
+               .rank = entry->rank,
+               .has_ns = rr->type == KNOT_RRTYPE_NS,
+       };
+       if (rdataset_dematerialize(&rr->rrs, eh->data)
+           || rdataset_dematerialize(rds_sigs, eh->data + rr_ssize)) {
+               /* minimize the damage from incomplete write; TODO: better */
+               eh->ttl = 0;
+               eh->rank = 0;
+               assert(false);
+       }
+       WITH_VERBOSE {
+               VERBOSE_MSG(qry, "=> stashed rank: 0%0.2o, ", entry->rank);
+               kr_rrtype_print(rr->type, "", " ");
+               kr_dname_print(rr->owner, "", " ");
+               kr_log_verbose("(%d B)\n", (int)storage_size);
+       }
+       return kr_ok();
 }
 
 
@@ -1117,15 +1296,6 @@ static int found_exact_hit(kr_layer_t *ctx, knot_pkt_t *pkt, knot_db_val_t val,
                CHECK_RET(-EILSEQ);
                // LATER: recovery, perhaps via removing the entry?
        }
-
-       int32_t new_ttl = get_new_ttl(eh, qry->creation_time.tv_sec);
-       if (new_ttl < 0 || eh->rank < lowest_rank) {
-               /* Positive record with stale TTL or bad rank.
-                * It's unlikely that we find a negative one,
-                * so we might theoretically skip all the cache code. */
-               return kr_error(ENOENT);
-       }
-
        void *eh_data_bound = val.data + val.len;
 
        /* In case of NS ktype, there may be multiple types within.
@@ -1166,6 +1336,14 @@ static int found_exact_hit(kr_layer_t *ctx, knot_pkt_t *pkt, knot_db_val_t val,
                }
        }
 
+       int32_t new_ttl = get_new_ttl(eh, qry->creation_time.tv_sec);
+       if (new_ttl < 0 || eh->rank < lowest_rank) {
+               /* Positive record with stale TTL or bad rank.
+                * It's unlikely that we find a negative one,
+                * so we might theoretically skip all the cache code. */
+               return kr_error(ENOENT);
+       }
+
        if (eh->is_negative) {
                // insecure zones might have a negative-answer packet here
                //FIXME
@@ -1175,41 +1353,16 @@ static int found_exact_hit(kr_layer_t *ctx, knot_pkt_t *pkt, knot_db_val_t val,
        /* All OK, so start constructing the (pseudo-)packet. */
        int ret = pkt_renew(pkt, qry->sname, qry->stype);
        CHECK_RET(ret);
-       /* Materialize the base RRset for the answer in (pseudo-)packet. */
-       knot_rrset_t rrset = {};
-       rrset.owner = knot_dname_copy(qry->sname, &pkt->mm); /* well, not needed, really */
-       if (!rrset.owner) CHECK_RET(-EILSEQ); /* there could be various reasons for error */
-       rrset.type = qry->stype;
-       rrset.rclass = KNOT_CLASS_IN;
-       ret = rdataset_materialize(&rrset.rrs, eh->data,
-                                  eh_data_bound, new_ttl, &pkt->mm);
+
+       /* Materialize the sets for the answer in (pseudo-)packet. */
+       struct answer ans = {};
+       ret = entry2answer(&ans, AR_ANSWER, eh, eh_data_bound,
+                          qry->sname, qry->stype, new_ttl);
        CHECK_RET(ret);
-       size_t data_off = ret;
-       /* Materialize the RRSIG RRset for the answer in (pseudo-)packet. */
-       bool want_rrsigs = kr_rank_test(eh->rank, KR_RANK_SECURE);
-                       //^^ TODO: vague
-       knot_rrset_t rrsigs = {};
-       if (want_rrsigs) {
-               rrsigs.owner = knot_dname_copy(qry->sname, &pkt->mm); /* well, not needed, really */
-               if (!rrsigs.owner) CHECK_RET(-EILSEQ);
-               rrsigs.type = KNOT_RRTYPE_RRSIG;
-               rrsigs.rclass = KNOT_CLASS_IN;
-               ret = rdataset_materialize(&rrsigs.rrs, eh->data + data_off,
-                                          eh_data_bound, new_ttl, &pkt->mm);
-               /* sanity check: we consumed exactly all data */
-               CHECK_RET(ret);
-               int unused_bytes = eh_data_bound - (void *)eh->data - data_off - ret;
-               if (ktype != KNOT_RRTYPE_NS && unused_bytes) {
-                       /* ^^ it doesn't have to hold in multi-RRset entries; LATER: more checks? */
-                       VERBOSE_MSG(qry, "BAD?  Unused bytes: %d\n", unused_bytes);
-               }
-       }
        /* Put links to the materialized data into the pkt. */
-       ret = pkt_alloc_space(pkt, rrset.rrs.rr_count + rrsigs.rrs.rr_count);
-       CHECK_RET(ret);
-       ret = pkt_append(pkt, &rrset, eh->rank);
+       ret = pkt_alloc_space(pkt, 1 + (ans.rrsets[AR_ANSWER].sig_rds.rr_count > 0));
        CHECK_RET(ret);
-       ret = pkt_append(pkt, &rrsigs, KR_RANK_INITIAL);
+       ret = pkt_append(pkt, &ans.rrsets[AR_ANSWER], eh->rank);
        CHECK_RET(ret);
        /* Finishing touches. */
        qry->flags.EXPIRING = is_expiring(eh->ttl, new_ttl);
index 8a039d5b111b5dd99ec0dd47ce9e0c6aece1c462..1d943f6aac289bab5b99fe2ddca507485890ff76 100644 (file)
@@ -797,3 +797,8 @@ void kr_qry_print(const struct kr_query *qry, const char *prefix, const char *po
        kr_rrtype_print(qry->stype, " ", postfix);
 }
 
+int knot_dname_lf2wire(knot_dname_t *dst, uint8_t len, const uint8_t *lf)
+{
+       //FIXME
+       assert(false);
+}
index 23c895c206bf37a9653ed66f233ff63ae396b594..4a779558c8051e6ec222384c2196369d1dacaf88 100644 (file)
@@ -120,9 +120,10 @@ struct ranked_rr_array_entry {
        uint32_t qry_uid;
        uint8_t rank; /**< enum kr_rank */
        uint8_t revalidation_cnt;
-       bool cached;  /**< whether it has been stashed to cache already */
-       bool yielded;
-       bool to_wire; /**< whether to be put into the answer */
+       bool cached : 1;  /**< whether it has been stashed to cache already */
+       bool yielded : 1;
+       bool to_wire : 1; /**< whether to be put into the answer */
+       bool expiring : 1; /**< low remaining TTL; see is_expiring; only used in cache ATM */
        knot_rrset_t *rr;
 };
 typedef struct ranked_rr_array_entry ranked_rr_array_entry_t;
@@ -288,3 +289,10 @@ static inline uint16_t kr_rrset_type_maysig(const knot_rrset_t *rr)
                type = knot_rrsig_type_covered(&rr->rrs, 0);
        return type;
 }
+
+/** Convert name from lookup format to wire.  See knot_dname_lf
+ *
+ * \note len bytes are read and len+1 are written.
+ */
+int knot_dname_lf2wire(knot_dname_t *dst, uint8_t len, const uint8_t *lf);
+