]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
fix DNAME support
authorVladimír Čunát <vladimir.cunat@nic.cz>
Mon, 30 Mar 2020 15:28:26 +0000 (17:28 +0200)
committerPetr Špaček <petr.spacek@nic.cz>
Mon, 27 Apr 2020 07:24:31 +0000 (09:24 +0200)
NEWS
daemon/lua/kres-gen.lua
doc/upgrading.rst
lib/cache/api.c
lib/cache/peek.c
lib/dnssec.h
lib/layer/iterate.c
lib/layer/validate.c
lib/utils.c
lib/utils.h

diff --git a/NEWS b/NEWS
index 6584df92aa4bf92c172c801df4bed099056a9c3b..bc43716e097ee36eca83dc7bb694fc0fdb641e79 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -14,6 +14,7 @@ Bugfixes
 - cache: missing filesystem support for pre-allocation is no longer fatal (#549)
 - lua: policy.rpz() no longer watches the file when watch is set to false (!954)
 - fix a strict aliasing problem that might've lead to "miscompilation" (!962)
+- fix handling of DNAMEs, especially signed ones (#234, !965)
 - lua resolve(): correctly include EDNS0 in the virtual packet (!963)
   Custom modules might have been confused by that.
 - do not leak bogus data into SERVFAIL answers (#396)
index 5b9b69275f56da1caa1680e6e4d4987b8bba812e..853d23d7a3a4874eb56972b63fb84a4b5c5a7a0a 100644 (file)
@@ -129,6 +129,7 @@ struct ranked_rr_array_entry {
        _Bool to_wire : 1;
        _Bool expiring : 1;
        _Bool in_progress : 1;
+       _Bool dont_cache : 1;
        knot_rrset_t *rr;
 };
 typedef struct ranked_rr_array_entry ranked_rr_array_entry_t;
index e35a264ff91c530f016c53f85dfef7ed868fc59d..b1b3a004e467ca6d5909eb4bb72b3649703186ca 100644 (file)
@@ -18,6 +18,7 @@ Module changes
 
 * Modules which use :c:type:`kr_request.trace_log` handler need update to modified handler API. Example migration is `modules/watchdog/watchdog.lua <https://gitlab.labs.nic.cz/knot/knot-resolver/-/merge_requests/957/diffs#6831501329bbf9e494048fe269c6b02944fc227c>`_.
 * Modules which were using logger :c:func:`kr_log_qverbose_impl` need migration to new logger :c:func:`kr_log_q`. Example migration is `modules/rebinding/rebinding.lua <https://gitlab.labs.nic.cz/knot/knot-resolver/-/merge_requests/957/diffs#6c74dcae147221ca64286a3ed028057adb6813b9>`_.
+* Modules which were using :c:func:`kr_ranked_rrarray_add` should note that on success it no longer returns exclusively zero but index into the array (non-negative).  Error states are unchanged (negative).
 
 
 4.x to 5.x
index fcf81374323146fe55e43712a418c5c8f2d557b7..17da2c10618690f8742f492a2f7ed5103c0a84fc 100644 (file)
@@ -378,9 +378,9 @@ int cache_stash(kr_layer_t *ctx, knot_pkt_t *pkt)
                /* uncached entries are located at the end */
                for (ssize_t i = arr->len - 1; i >= 0; --i) {
                        ranked_rr_array_entry_t *entry = arr->at[i];
-                       if (entry->qry_uid != qry->uid) {
+                       if (entry->qry_uid != qry->uid || entry->dont_cache) {
                                continue;
-                               /* TODO: probably safe to break but maybe not worth it */
+                               /* TODO: probably safe to break on uid mismatch but maybe not worth it */
                        }
                        int ret = stash_rrarray_entry(
                                        arr, i, qry, cache, &unauth_cnt, nsec_pmap,
index 16bfb9ff787e05ca235246b96c97cd50dcafffb1..0d66bb6303253cc7efa25e135014a489f84809bf 100644 (file)
@@ -16,6 +16,8 @@ static int closest_NS(struct kr_cache *cache, struct key *k, entry_list_t el,
                        struct kr_query *qry, bool only_NS, bool is_DS);
 static int answer_simple_hit(kr_layer_t *ctx, knot_pkt_t *pkt, uint16_t type,
                const struct entry_h *eh, const void *eh_bound, uint32_t new_ttl);
+static int answer_dname_hit(kr_layer_t *ctx, knot_pkt_t *pkt, const knot_dname_t *dname_owner,
+               const struct entry_h *eh, const void *eh_bound, uint32_t new_ttl);
 static int try_wild(struct key *k, struct answer *ans, const knot_dname_t *clencl_name,
                    uint16_t type, uint8_t lowest_rank,
                    const struct kr_query *qry, struct kr_cache *cache);
@@ -161,12 +163,18 @@ int peek_nosync(kr_layer_t *ctx, knot_pkt_t *pkt)
                                                KNOT_RRTYPE_CNAME, qry->timestamp.tv_sec);
                ret = answer_simple_hit(ctx, pkt, KNOT_RRTYPE_CNAME, v.data,
                                        knot_db_val_bound(v), new_ttl);
-               /* TODO: ^^ cumbersome code; we also recompute the TTL */
                return ret == kr_ok() ? KR_STATE_DONE : ctx->state;
                }
-       case KNOT_RRTYPE_DNAME:
-               VERBOSE_MSG(qry, "=> DNAME not supported yet\n"); // LATER
-               return ctx->state;
+       case KNOT_RRTYPE_DNAME: {
+               const knot_db_val_t v = el[EL_DNAME];
+               assert(v.data && v.len);
+               /* TTL: for simplicity, we just ask for TTL of the generated CNAME. */
+               const int32_t new_ttl = get_new_ttl(v.data, qry, qry->sname,
+                                               KNOT_RRTYPE_CNAME, qry->timestamp.tv_sec);
+               ret = answer_dname_hit(ctx, pkt, k->zname, v.data,
+                                       knot_db_val_bound(v), new_ttl);
+               return ret == kr_ok() ? KR_STATE_DONE : ctx->state;
+               }
        }
 
        /* We have to try proving from NSEC*. */
@@ -405,12 +413,25 @@ static int peek_encloser(
        return -ABS(ENOENT);
 }
 
+static void answer_simple_qflags(struct kr_qflags *qf, const struct entry_h *eh,
+                                uint32_t new_ttl)
+{
+       /* Finishing touches. */
+       qf->EXPIRING = is_expiring(eh->ttl, new_ttl);
+       qf->CACHED = true;
+       qf->NO_MINIMIZE = true;
+       qf->DNSSEC_INSECURE = kr_rank_test(eh->rank, KR_RANK_INSECURE);
+       if (qf->DNSSEC_INSECURE) {
+               qf->DNSSEC_WANT = false;
+       }
+}
 
-static int answer_simple_hit(kr_layer_t *ctx, knot_pkt_t *pkt, uint16_t type,
-               const struct entry_h *eh, const void *eh_bound, uint32_t new_ttl)
 #define CHECK_RET(ret) do { \
        if ((ret) < 0) { assert(false); return kr_error((ret)); } \
 } while (false)
+
+static int answer_simple_hit(kr_layer_t *ctx, knot_pkt_t *pkt, uint16_t type,
+               const struct entry_h *eh, const void *eh_bound, uint32_t new_ttl)
 {
        struct kr_request *req = ctx->req;
        struct kr_query *qry = req->current_query;
@@ -430,22 +451,71 @@ static int answer_simple_hit(kr_layer_t *ctx, knot_pkt_t *pkt, uint16_t type,
        ret = pkt_append(pkt, &ans.rrsets[AR_ANSWER], eh->rank);
        CHECK_RET(ret);
 
-       /* Finishing touches. */
-       struct kr_qflags * const qf = &qry->flags;
-       qf->EXPIRING = is_expiring(eh->ttl, new_ttl);
-       qf->CACHED = true;
-       qf->NO_MINIMIZE = true;
-       qf->DNSSEC_INSECURE = kr_rank_test(eh->rank, KR_RANK_INSECURE);
-       if (qf->DNSSEC_INSECURE) {
-               qf->DNSSEC_WANT = false;
-       }
+       answer_simple_qflags(&qry->flags, eh, new_ttl);
+
        VERBOSE_MSG(qry, "=> satisfied by exact %s: rank 0%.2o, new TTL %d\n",
                        (type == KNOT_RRTYPE_CNAME ? "CNAME" : "RRset"),
                        eh->rank, new_ttl);
        return kr_ok();
 }
-#undef CHECK_RET
 
+static int answer_dname_hit(kr_layer_t *ctx, knot_pkt_t *pkt, const knot_dname_t *dname_owner,
+               const struct entry_h *eh, const void *eh_bound, uint32_t new_ttl)
+{
+       struct kr_request *req = ctx->req;
+       struct kr_query *qry = req->current_query;
+
+       /* All OK, so start constructing the (pseudo-)packet. */
+       int ret = pkt_renew(pkt, qry->sname, qry->stype);
+       CHECK_RET(ret);
+
+       /* Materialize the DNAME for the answer in (pseudo-)packet. */
+       struct answer ans;
+       memset(&ans, 0, sizeof(ans));
+       ans.mm = &pkt->mm;
+       ret = entry2answer(&ans, AR_ANSWER, eh, eh_bound,
+                          dname_owner, KNOT_RRTYPE_DNAME, new_ttl);
+       CHECK_RET(ret);
+       /* Put link to the RRset into the pkt. */
+       ret = pkt_append(pkt, &ans.rrsets[AR_ANSWER], eh->rank);
+       CHECK_RET(ret);
+       const knot_dname_t *dname_target =
+               knot_dname_target(ans.rrsets[AR_ANSWER].set.rr->rrs.rdata);
+
+       /* Generate CNAME RRset for the answer in (pseudo-)packet. */
+       const int AR_CNAME = AR_SOA;
+       knot_rrset_t *rr = ans.rrsets[AR_CNAME].set.rr
+               = knot_rrset_new(qry->sname, KNOT_RRTYPE_CNAME, KNOT_CLASS_IN,
+                                new_ttl, ans.mm);
+       CHECK_RET(rr ? kr_ok() : -ENOMEM);
+       const knot_dname_t *cname_target = knot_dname_replace_suffix(qry->sname,
+                       knot_dname_labels(dname_owner, NULL), dname_target, ans.mm);
+       CHECK_RET(cname_target ? kr_ok() : -ENOMEM);
+       const int rdata_len = knot_dname_size(cname_target);
+
+       if (rdata_len <= KNOT_DNAME_MAXLEN
+           && knot_dname_labels(cname_target, NULL) <= KNOT_DNAME_MAXLABELS) {
+               /* Normal case: the target name fits. */
+               rr->rrs.count = 1;
+               rr->rrs.size = knot_rdata_size(rdata_len);
+               rr->rrs.rdata = mm_alloc(ans.mm, rr->rrs.size);
+               CHECK_RET(rr->rrs.rdata ? kr_ok() : -ENOMEM);
+               knot_rdata_init(rr->rrs.rdata, rdata_len, cname_target);
+               /* Put link to the RRset into the pkt. */
+               ret = pkt_append(pkt, &ans.rrsets[AR_CNAME], eh->rank);
+               CHECK_RET(ret);
+       } else {
+               /* Note that it's basically a successful answer; name just doesn't fit. */
+               knot_wire_set_rcode(pkt->wire, KNOT_RCODE_YXDOMAIN);
+       }
+
+       answer_simple_qflags(&qry->flags, eh, new_ttl);
+       VERBOSE_MSG(qry, "=> satisfied by DNAME+CNAME: rank 0%.2o, new TTL %d\n",
+                       eh->rank, new_ttl);
+       return kr_ok();
+}
+
+#undef CHECK_RET
 
 /** TODO: description; see the single call site for now. */
 static int found_exact_hit(kr_layer_t *ctx, knot_pkt_t *pkt, knot_db_val_t val,
@@ -619,8 +689,22 @@ static int closest_NS(struct kr_cache *cache, struct key *k, entry_list_t el,
                need_zero = false;
                /* More types are possible; try in order.
                 * For non-fatal failures just "continue;" to try the next type. */
+               /* Now a complication - we need to try EL_DNAME before NSEC*
+                * (Unfortunately that's not easy to write very nicely.) */
+               if (!only_NS) {
+                       const int i = EL_DNAME;
+                       ret = check_NS_entry(k, el[i], i, exact_match, is_DS,
+                                               qry, timestamp);
+                       if (ret < 0) goto next_label; else
+                       if (!ret) {
+                               /* We found our match. */
+                               k->zlf_len = zlf_len;
+                               return kr_ok();
+                       }
+               }
                const int el_count = only_NS ? EL_NS + 1 : EL_LENGTH;
                for (int i = 0; i < el_count; ++i) {
+                       if (i == EL_DNAME) continue;
                        ret = check_NS_entry(k, el[i], i, exact_match, is_DS,
                                                qry, timestamp);
                        if (ret < 0) goto next_label; else
index 170b3a87f20b31d1c4077eee2838c3c7e0014993..d9601eaa998ebb9b0fbafcf1a9030ddfc21941c9 100644 (file)
@@ -45,6 +45,7 @@ struct kr_rrset_validation_ctx {
        uint32_t qry_uid;               /*!< Current query uid. */
        uint32_t flags;                 /*!< Output - Flags. */
        uint32_t err_cnt;               /*!< Output - Number of validation failures. */
+       uint32_t cname_norrsig_cnt;     /*!< Output - Number of CNAMEs missing RRSIGs. */
        int result;                     /*!< Output - 0 or error code. */
        struct {
                unsigned int matching_name_type;        /*!< Name + type matches */
index 6558c50b7de02431ad0e6a7825ba35f153942500..3d2a93cb20583248b90663da3f984cd5287b7060 100644 (file)
@@ -374,7 +374,7 @@ static int pick_authority(knot_pkt_t *pkt, struct kr_request *req, bool to_wire)
                                                qry->flags.FORWARD || referral);
                int ret = kr_ranked_rrarray_add(&req->auth_selected, rr,
                                                rank, to_wire, qry->uid, &req->pool);
-               if (ret != kr_ok()) {
+               if (ret < 0) {
                        return ret;
                }
        }
@@ -498,6 +498,8 @@ static int unroll_cname(knot_pkt_t *pkt, struct kr_request *req, bool referral,
                /* CNAME was found at previous iteration, but records may not follow the correct order.
                 * Try to find records for pending_cname owner from section start. */
                cname = pending_cname;
+               size_t cname_answ_selected_i = -1;
+               bool cname_is_occluded = false; /* whether `cname` is in a DNAME's bailiwick */
                pending_cname = NULL;
                const int cname_labels = knot_dname_labels(cname, NULL);
                for (unsigned i = 0; i < an->count; ++i) {
@@ -506,13 +508,33 @@ static int unroll_cname(knot_pkt_t *pkt, struct kr_request *req, bool referral,
                        /* Skip the RR if its owner+type doesn't interest us. */
                        const uint16_t type = kr_rrset_type_maysig(rr);
                        const bool type_OK = rr->type == query->stype || type == query->stype
-                               || type == KNOT_RRTYPE_CNAME || type == KNOT_RRTYPE_DNAME;
-                               /* TODO: actually handle DNAMEs */
-                       if (rr->rclass != KNOT_CLASS_IN || !type_OK
-                           || !knot_dname_is_equal(rr->owner, cname)
+                                               || type == KNOT_RRTYPE_CNAME;
+                       if (rr->rclass != KNOT_CLASS_IN
                            || knot_dname_in_bailiwick(rr->owner, query->zone_cut.name) < 0) {
                                continue;
                        }
+                       const bool all_OK = type_OK && knot_dname_is_equal(rr->owner, cname);
+
+                       const bool to_wire = is_final && !referral;
+
+                       if (!all_OK && type == KNOT_RRTYPE_DNAME
+                                       && knot_dname_in_bailiwick(cname, rr->owner) >= 1) {
+                               /* This DNAME (or RRSIGs) cover the current target (`cname`),
+                                * so it is interesting and will occlude its CNAME.
+                                * We rely on CNAME being sent along with DNAME
+                                * (mandatory unless YXDOMAIN). */
+                               cname_is_occluded = true;
+                               uint8_t rank = get_initial_rank(rr, query, true,
+                                               query->flags.FORWARD || referral);
+                               int ret = kr_ranked_rrarray_add(&req->answ_selected, rr,
+                                               rank, to_wire, query->uid, &req->pool);
+                               if (ret < 0) {
+                                       return KR_STATE_FAIL;
+                               }
+                       }
+                       if (!all_OK) {
+                               continue;
+                       }
 
                        if (rr->type == KNOT_RRTYPE_RRSIG) {
                                int rrsig_labels = knot_rrsig_labels(rr->rrs.rdata);
@@ -528,39 +550,38 @@ static int unroll_cname(knot_pkt_t *pkt, struct kr_request *req, bool referral,
                        }
 
                        /* Process records matching current SNAME */
-                       int state = KR_STATE_FAIL;
-                       bool to_wire = false;
-                       if (is_final) {
-                               /* if not referral, mark record to be written to final answer */
-                               to_wire = !referral;
-                       } else {
+                       if (!is_final) {
                                int cnt_ = 0;
-                               state = update_nsaddr(rr, query->parent, &cnt_);
+                               int state = update_nsaddr(rr, query->parent, &cnt_);
                                if (state & KR_STATE_FAIL) {
                                        return state;
                                }
                        }
                        uint8_t rank = get_initial_rank(rr, query, true,
                                        query->flags.FORWARD || referral);
-                       state = kr_ranked_rrarray_add(&req->answ_selected, rr,
-                                                     rank, to_wire, query->uid, &req->pool);
-                       if (state != kr_ok()) {
+                       int ret = kr_ranked_rrarray_add(&req->answ_selected, rr,
+                                               rank, to_wire, query->uid, &req->pool);
+                       if (ret < 0) {
                                return KR_STATE_FAIL;
                        }
-                       /* Jump to next CNAME target */
-                       if ((query->stype == KNOT_RRTYPE_CNAME) || (rr->type != KNOT_RRTYPE_CNAME)) {
-                               continue;
-                       }
-                       pending_cname = knot_cname_name(rr->rrs.rdata);
-                       if (!pending_cname) {
-                               break;
+                       cname_answ_selected_i = ret;
+
+                       /* Select the next CNAME target, but don't jump immediately.
+                        * There can be records for "old" cname (RRSIGs are interesting);
+                        * more importantly there might be a DNAME for `cname_is_occluded`. */
+                       if (query->stype != KNOT_RRTYPE_CNAME && rr->type == KNOT_RRTYPE_CNAME) {
+                               pending_cname = knot_cname_name(rr->rrs.rdata);
+                               if (!pending_cname) {
+                                       break;
+                               }
                        }
-                       /* Don't use pending_cname immediately.
-                        * There are can be records for "old" cname. */
                }
                if (!pending_cname) {
                        break;
                }
+               if (cname_is_occluded) {
+                       req->answ_selected.at[cname_answ_selected_i]->dont_cache = true;
+               }
                if (++(query->cname_depth) > KR_CNAME_CHAIN_LIMIT) {
                        VERBOSE_MSG("<= error: CNAME chain exceeded max length %d\n",
                                        /* people count objects from 0, no CNAME = 0 */
@@ -828,7 +849,7 @@ static int process_stub(knot_pkt_t *pkt, struct kr_request *req)
                /* KR_RANK_AUTH: we don't have the records directly from
                 * an authoritative source, but we do trust the server and it's
                 * supposed to only send us authoritative records. */
-               if (err != kr_ok()) {
+               if (err < 0) {
                        return KR_STATE_FAIL;
                }
        }
@@ -1068,6 +1089,9 @@ static int resolve(kr_layer_t *ctx, knot_pkt_t *pkt)
        case KNOT_RCODE_NOERROR:
        case KNOT_RCODE_NXDOMAIN:
                break; /* OK */
+       case KNOT_RCODE_YXDOMAIN: /* Basically a successful answer; name just doesn't fit. */
+               knot_wire_set_rcode(req->answer->wire, KNOT_RCODE_YXDOMAIN);
+               break;
        case KNOT_RCODE_REFUSED:
        case KNOT_RCODE_SERVFAIL:
                if (query->flags.STUB) {
index c60858b430566c7be008d9bc67be3d64b9977d23..2fa89d23df7e096a62b6769ad2cf79e303a2eeed 100644 (file)
@@ -82,6 +82,32 @@ static void log_bogus_rrsig(kr_rrset_validation_ctx_t *vctx, const struct kr_que
        }
 }
 
+/** Check that given CNAME could be generated by given DNAME (no DNSSEC validation). */
+static bool cname_matches_dname(const knot_rrset_t *rr_cn, const knot_rrset_t *rr_dn)
+{
+       assert(rr_cn->type == KNOT_RRTYPE_CNAME && rr_dn->type == KNOT_RRTYPE_DNAME);
+       /* When DNAME substitution happens, let's consider the "prefix"
+        * that is carried over and the "suffix" that is replaced.
+        * (Here we consider the label order used in wire and presentation.) */
+       const int prefix_labels = knot_dname_in_bailiwick(rr_cn->owner, rr_dn->owner);
+       if (prefix_labels < 1)
+               return false;
+       const knot_dname_t *cn_target = knot_cname_name(rr_cn->rrs.rdata);
+       const knot_dname_t *dn_target = knot_dname_target(rr_dn->rrs.rdata);
+               /* ^ We silently use the first RR in each RRset.  Could be e.g. logged. */
+       /* Check that the suffixes are correct - and even prefix label counts. */
+       if (knot_dname_in_bailiwick(cn_target, dn_target) != prefix_labels)
+               return false;
+       /* Check that prefixes match.  Find end of the first one and compare. */
+       const knot_dname_t *cn_se = rr_cn->owner;
+       for (int i = 0; i < prefix_labels; ++i)
+               cn_se += 1 + *cn_se;
+       return strncmp((const char *)rr_cn->owner, (const char *)cn_target,
+                       cn_se - rr_cn->owner) == 0;
+               /* ^ We use the fact that dnames are always zero-terminated
+                * to avoid any possible over-read in cn_target. */
+}
+
 static int validate_section(kr_rrset_validation_ctx_t *vctx, const struct kr_query *qry,
                            knot_mm_t *pool)
 {
@@ -94,7 +120,6 @@ static int validate_section(kr_rrset_validation_ctx_t *vctx, const struct kr_que
         */
        vctx->zone_name = vctx->keys ? vctx->keys->owner : NULL;
 
-       int validation_result = 0;
        for (ssize_t i = 0; i < vctx->rrs->len; ++i) {
                ranked_rr_array_entry_t *entry = vctx->rrs->at[i];
                knot_rrset_t * const rr = entry->rr;
@@ -121,7 +146,26 @@ static int validate_section(kr_rrset_validation_ctx_t *vctx, const struct kr_que
                }
 
                uint8_t rank_orig = entry->rank;
-               validation_result = kr_rrset_validate(vctx, rr);
+               int validation_result = kr_rrset_validate(vctx, rr);
+
+               /* Handle the case of CNAMEs synthesized from DNAMEs (they don't have RRSIGs). */
+               if (rr->type == KNOT_RRTYPE_CNAME && validation_result == kr_error(ENOENT)) {
+                       for (ssize_t j = 0; j < vctx->rrs->len; ++j) {
+                               ranked_rr_array_entry_t *e_dname = vctx->rrs->at[j];
+                               if ((e_dname->rr->type == KNOT_RRTYPE_DNAME)
+                                   /* If the order is wrong, we will need two passes. */
+                                   && kr_rank_test(e_dname->rank, KR_RANK_SECURE)
+                                   && cname_matches_dname(rr, e_dname->rr)) {
+                                       /* Now we believe the CNAME is OK. */
+                                       validation_result = kr_ok();
+                                       break;
+                               }
+                       }
+                       if (validation_result != kr_ok()) {
+                               vctx->cname_norrsig_cnt += 1;
+                       }
+               }
+
                if (validation_result == kr_ok()) {
                        kr_rank_set(&entry->rank, KR_RANK_SECURE);
 
@@ -172,10 +216,16 @@ static int validate_records(struct kr_request *req, knot_pkt_t *answer, knot_mm_
                .has_nsec3      = has_nsec3,
                .flags          = 0,
                .err_cnt        = 0,
+               .cname_norrsig_cnt = 0,
                .result         = 0
        };
 
        int ret = validate_section(&vctx, qry, pool);
+       if (vctx.err_cnt && vctx.err_cnt == vctx.cname_norrsig_cnt) {
+               VERBOSE_MSG(qry, ">< all validation errors are missing RRSIGs on CNAMES, trying again in hope for DNAMEs\n");
+               vctx.err_cnt = vctx.cname_norrsig_cnt = vctx.result = 0;
+               ret = validate_section(&vctx, qry, pool);
+       }
        req->answ_validated = (vctx.err_cnt == 0);
        if (ret != kr_ok()) {
                return ret;
index 2755d65f66b1c7cd808407f612769212989605c0..2be3ca60bc6fb9b233286a66f01eb602854c132b 100644 (file)
@@ -755,7 +755,7 @@ int kr_ranked_rrarray_add(ranked_rr_array_t *array, const knot_rrset_t *rr,
                                abort();
                        }
                }
-               return kr_ok();
+               return i;
        }
 
        /* No stashed rrset found, add */
@@ -768,6 +768,7 @@ int kr_ranked_rrarray_add(ranked_rr_array_t *array, const knot_rrset_t *rr,
        if (!entry) {
                return kr_error(ENOMEM);
        }
+       memset(entry, 0, sizeof(*entry)); /* default all to zeros */
 
        knot_rrset_t *rr_new = knot_rrset_new(rr->owner, rr->type, rr->rclass, rr->ttl, pool);
        if (!rr_new) {
@@ -780,9 +781,6 @@ int kr_ranked_rrarray_add(ranked_rr_array_t *array, const knot_rrset_t *rr,
        entry->qry_uid = qry_uid;
        entry->rr = rr_new;
        entry->rank = rank;
-       entry->revalidation_cnt = 0;
-       entry->cached = false;
-       entry->yielded = false;
        entry->to_wire = to_wire;
        entry->in_progress = true;
        if (array_push(*array, entry) < 0) {
@@ -792,7 +790,9 @@ int kr_ranked_rrarray_add(ranked_rr_array_t *array, const knot_rrset_t *rr,
                return kr_error(ENOMEM);
        }
 
-       return to_wire_ensure_unique(array, array->len - 1);
+       ret = to_wire_ensure_unique(array, array->len - 1);
+       if (ret < 0) return ret;
+       return array->len - 1;
 }
 
 /** Comparator for qsort() on an array of knot_data_t pointers. */
index 886ef871102320579b9f55747dcaae2e7289d9d1..55e280fdf233c741afd3e5cfedea2d4ff07c1522 100644 (file)
@@ -204,6 +204,7 @@ struct ranked_rr_array_entry {
        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 */
        bool in_progress : 1; /**< build of RRset in progress, i.e. different format of RR data */
+       bool dont_cache : 1; /**< avoid caching; useful e.g. for generated data */
        knot_rrset_t *rr;
 };
 typedef struct ranked_rr_array_entry ranked_rr_array_entry_t;
@@ -420,6 +421,8 @@ int kr_rrkey(char *key, uint16_t class, const knot_dname_t *owner,
  *
  * To convert to standard RRs inside, you need to call _finalize() afterwards,
  * and the memory of rr->rrs.rdata has to remain until then.
+ *
+ * \return array index (>= 0) or error code (< 0)
  */
 KR_EXPORT
 int kr_ranked_rrarray_add(ranked_rr_array_t *array, const knot_rrset_t *rr,