From 0d587deaf3bf20bfe0892944494b8edd5bb1b543 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Vladim=C3=ADr=20=C4=8Cun=C3=A1t?= Date: Thu, 26 Oct 2017 17:32:28 +0200 Subject: [PATCH] fix CNAMEs and some details --- lib/cache.c | 255 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 162 insertions(+), 93 deletions(-) diff --git a/lib/cache.c b/lib/cache.c index a79b894ae..d1fa02105 100644 --- a/lib/cache.c +++ b/lib/cache.c @@ -261,8 +261,8 @@ static bool is_expiring(uint32_t orig_ttl, uint32_t new_ttl) static uint8_t get_lowest_rank(const struct kr_request *req, const struct kr_query *qry); static int found_exact_hit(kr_layer_t *ctx, knot_pkt_t *pkt, knot_db_val_t val, - uint8_t lowest_rank, uint16_t ktype); -static const struct entry_h *closest_NS(kr_layer_t *ctx, struct key *k); + uint8_t lowest_rank); +static knot_db_val_t closest_NS(kr_layer_t *ctx, struct key *k); @@ -458,6 +458,7 @@ static int entry_h_len(const knot_db_val_t val) /** There may be multiple entries within, so rewind `val` to the one we want. * * ATM there are multiple types only for the NS ktype. + * \return error code */ static int entry_h_seek(knot_db_val_t *val, uint16_t type) { @@ -623,17 +624,19 @@ static bool check_dname_for_lf(const knot_dname_t *n) /** TODO */ -static knot_db_val_t key_exact_type(struct key *k, uint16_t ktype) +static knot_db_val_t key_exact_type(struct key *k, uint16_t type) { + switch (type) { /* Sanity check: forbidden types represented in other way(s). */ - switch (ktype) { case KNOT_RRTYPE_RRSIG: - case KNOT_RRTYPE_CNAME: - case KNOT_RRTYPE_DNAME: case KNOT_RRTYPE_NSEC: case KNOT_RRTYPE_NSEC3: assert(false); return (knot_db_val_t){}; + /* xNAME lumped into NS. */ + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_DNAME: + type = KNOT_RRTYPE_NS; default: break; } @@ -641,8 +644,8 @@ static knot_db_val_t key_exact_type(struct key *k, uint16_t ktype) int name_len = k->buf[0]; k->buf[name_len + 1] = 0; /* make sure different names can never match */ k->buf[name_len + 2] = 'E'; /* tag for exact name+type matches */ - memcpy(k->buf + name_len + 3, &ktype, 2); - k->type = ktype; + memcpy(k->buf + name_len + 3, &type, 2); + k->type = type; /* CACHE_KEY_DEF: key == dname_lf + '\0' + 'E' + RRTYPE */ return (knot_db_val_t){ k->buf + 1, name_len + 4 }; } @@ -859,6 +862,10 @@ int dname_wire_reconstruct(knot_dname_t *buf, const struct key *k, return kr_ok(); } + +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); + /** function for .produce phase */ int cache_lmdb_peek(kr_layer_t *ctx, knot_pkt_t *pkt) { @@ -892,16 +899,15 @@ int cache_lmdb_peek(kr_layer_t *ctx, knot_pkt_t *pkt) /** 1. find the name or the closest (available) zone, not considering wildcards * 1a. exact name+type match (can be negative answer in insecure zones) */ - uint16_t ktype = qry->stype; - if (ktype == KNOT_RRTYPE_CNAME || ktype == KNOT_RRTYPE_DNAME) { - ktype = KNOT_RRTYPE_NS; + if (qry->stype == KNOT_RRTYPE_RRSIG) { + return ctx->state; /* LATER: some other behavior for this STYPE? */ } - knot_db_val_t key = key_exact_type(k, ktype); + knot_db_val_t key = key_exact_type(k, qry->stype); knot_db_val_t val = { }; ret = cache_op(cache, read, &key, &val, 1); switch (ret) { case 0: /* found an entry: test conditions, materialize into pkt, etc. */ - ret = found_exact_hit(ctx, pkt, val, lowest_rank, ktype); + ret = found_exact_hit(ctx, pkt, val, lowest_rank); if (ret == -abs(ENOENT)) { break; } else if (ret) { @@ -921,14 +927,42 @@ int cache_lmdb_peek(kr_layer_t *ctx, knot_pkt_t *pkt) /** 1b. otherwise, find the longest prefix NS/xNAME (with OK time+rank). [...] */ k->zname = qry->sname; - kr_dname_lf(k->buf, qry->sname, NULL); /* LATER(optim.): probably remove */ - const struct entry_h *eh = closest_NS(ctx, k); - if (!eh) { + if (qry->stype == KNOT_RRTYPE_DS) { /* DS is parent-side. */ + k->zname = knot_wire_next_label(k->zname, NULL); + if (!k->zname) { + return ctx->state; /* can't go above root */ + } + } + kr_dname_lf(k->buf, k->zname, NULL); /* LATER(optim.): probably remove */ + const knot_db_val_t val_cut = closest_NS(ctx, k); + if (!val_cut.data) { VERBOSE_MSG(qry, "=> not even root NS in cache\n"); return ctx->state; /* nothing to do without any NS at all */ } - VERBOSE_MSG(qry, "=> trying zone: "); - kr_dname_print(k->zname, "", "\n"); + switch (k->type) { + case KNOT_RRTYPE_NS: + VERBOSE_MSG(qry, "=> trying zone: "); + kr_dname_print(k->zname, "", "\n"); + break; + case KNOT_RRTYPE_CNAME: + ret = answer_simple_hit(ctx, pkt, KNOT_RRTYPE_CNAME, val_cut.data, + val_cut.data + val_cut.len, + get_new_ttl(val_cut.data, qry->creation_time.tv_sec)); + /* TODO: ^^ cumbersome code */ + if (ret == kr_ok()) { + VERBOSE_MSG(qry, "=> satisfied by CNAME\n"); + return KR_STATE_DONE; + } else { + return ctx->state; + } + + case KNOT_RRTYPE_DNAME: + VERBOSE_MSG(qry, "=> DNAME not supported yet\n"); // LATER + return ctx->state; + default: + assert(false); + } + #if 0 if (!eh) { /* fall back to root hints? */ ret = kr_zonecut_set_sbelt(req->ctx, &qry->zone_cut); @@ -939,35 +973,23 @@ int cache_lmdb_peek(kr_layer_t *ctx, knot_pkt_t *pkt) //qry->flags.AWAIT_CUT = false; return ctx->state; } -#endif - switch (k->type) { - // FIXME xNAME: return/generate whatever is required - case KNOT_RRTYPE_NS: - break; - default: - assert(false); - return ctx->state; - } -#if 0 /* Now `eh` points to the closest NS record that we've found, * and that's the only place to start - we may either find * a negative proof or we may query upstream from that point. */ kr_zonecut_set(&qry->zone_cut, k->zname); ret = kr_make_query(qry, pkt); // FIXME: probably not yet - qname minimization if (ret) return KR_STATE_FAIL; -#endif - /* Note: up to here we can run on any cache backend, * without touching the code. */ - if (!eh->nsec1_pos) { /* No NSEC1 RRs for this zone in cache. */ /* TODO: NSEC3 */ //VERBOSE_MSG(qry, " no NSEC1\n"); //return ctx->state; } +#endif /* collecting multiple NSEC* + RRSIG records, in preparation for the answer * + track the progress @@ -991,7 +1013,7 @@ int cache_lmdb_peek(kr_layer_t *ctx, knot_pkt_t *pkt) //while (true) { //for (int i_nsecp = 0; i // TODO(NSEC3): better signalling when to "continue;" and when to "break;" // incl. clearing partial answers in `ans` - assert(eh->nsec1_pos <= 1); + //assert(eh->nsec1_pos <= 1); int nsec = 1; switch (nsec) { case 1: { @@ -1175,7 +1197,7 @@ do_soa: knot_db_val_t val = { }; ret = cache_op(cache, read, &key, &val, 1); const struct entry_h *eh; - if (ret || !(eh = entry_h_consistent(val, ktype))) { + if (ret || !(eh = entry_h_consistent(val, KNOT_RRTYPE_SOA))) { return ctx->state; } void *eh_data_bound = val.data + val.len; @@ -1342,10 +1364,9 @@ static int stash_rrset(const ranked_rr_array_t *arr, int arr_i, uint32_t 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) { + switch (rr->type) { case KNOT_RRTYPE_NSEC: if (!kr_rank_test(entry->rank, KR_RANK_SECURE)) { /* Skip any NSECs that aren't validated. */ @@ -1358,22 +1379,19 @@ static int stash_rrset(const ranked_rr_array_t *arr, int arr_i, uint32_t min_ttl k->zlf_len = knot_dname_size(knot_rrsig_signer_name(&rr_sigs->rrs, 0)) - 1; key = key_NSEC1(k, rr->owner, false); break; - case KNOT_RRTYPE_CNAME: - case KNOT_RRTYPE_DNAME: - ktype = KNOT_RRTYPE_NS; // fallthrough default: ret = kr_dname_lf(k->buf, rr->owner, NULL); if (ret) { VERBOSE_MSG(qry, " 3\n"); return KR_STATE_FAIL; } - key = key_exact_type(k, ktype); + key = key_exact_type(k, rr->type); } /* Find the whole entry-set and the particular entry within. */ knot_db_val_t val_orig_all = { }, val_orig_entry = { }; const struct entry_h *eh_orig = NULL; - if (!kr_rank_test(entry->rank, KR_RANK_SECURE) || ktype == KNOT_RRTYPE_NS) { + if (!kr_rank_test(entry->rank, KR_RANK_SECURE) || k->type == KNOT_RRTYPE_NS) { ret = cache_op(cache, read, &key, &val_orig_all, 1); if (ret) val_orig_all = (knot_db_val_t){ }; val_orig_entry = val_orig_all; @@ -1465,7 +1483,7 @@ static int stash_rrset(const ranked_rr_array_t *arr, int arr_i, uint32_t min_ttl val_orig_entry.data + val_orig_entry.len, len_after); } /* The multi-entry type needs adjusting the flags. */ - if (ktype == KNOT_RRTYPE_NS) { + if (k->type == KNOT_RRTYPE_NS) { eh = val.data; if (val_orig_all.len) { const struct entry_h *eh0 = val_orig_all.data; @@ -1492,20 +1510,49 @@ static int stash_rrset(const ranked_rr_array_t *arr, int arr_i, uint32_t min_ttl 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); + int sigs = rr_sigs ? rr_sigs->rrs.rr_count : 0; + kr_log_verbose("(%d B total, incl. %d RRSIGs)\n", (int)storage_size, sigs); } return kr_ok(); } -/** 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, - uint8_t lowest_rank, uint16_t ktype) -{ #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; + + /* All OK, so start constructing the (pseudo-)packet. */ + int ret = pkt_renew(pkt, qry->sname, qry->stype); + CHECK_RET(ret); + + /* Materialize the sets for the answer in (pseudo-)packet. */ + struct answer ans = {}; + ret = entry2answer(&ans, AR_ANSWER, eh, eh_bound, + qry->sname, type, new_ttl); + CHECK_RET(ret); + /* Put links to the materialized data into the pkt. */ + ret = pkt_alloc_space(pkt, 1 + (ans.rrsets[AR_ANSWER].sig_rds.rr_count > 0)); + CHECK_RET(ret); + ret = pkt_append(pkt, &ans.rrsets[AR_ANSWER], eh->rank); + CHECK_RET(ret); + /* Finishing touches. */ + qry->flags.EXPIRING = is_expiring(eh->ttl, new_ttl); + qry->flags.CACHED = true; + qry->flags.NO_MINIMIZE = true; + return kr_ok(); +} + + +/** 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, + uint8_t lowest_rank) +{ struct kr_request *req = ctx->req; struct kr_query *qry = req->current_query; @@ -1532,28 +1579,9 @@ static int found_exact_hit(kr_layer_t *ctx, knot_pkt_t *pkt, knot_db_val_t val, //FIXME assert(false); } - - /* All OK, so start constructing the (pseudo-)packet. */ - ret = pkt_renew(pkt, qry->sname, qry->stype); - CHECK_RET(ret); - - /* Materialize the sets for the answer in (pseudo-)packet. */ - struct answer ans = {}; - ret = entry2answer(&ans, AR_ANSWER, eh, eh_bound, - qry->sname, qry->stype, new_ttl); - CHECK_RET(ret); - /* Put links to the materialized data into the pkt. */ - ret = pkt_alloc_space(pkt, 1 + (ans.rrsets[AR_ANSWER].sig_rds.rr_count > 0)); - CHECK_RET(ret); - ret = pkt_append(pkt, &ans.rrsets[AR_ANSWER], eh->rank); - CHECK_RET(ret); - /* Finishing touches. */ - qry->flags.EXPIRING = is_expiring(eh->ttl, new_ttl); - qry->flags.CACHED = true; - qry->flags.NO_MINIMIZE = true; - return kr_ok(); -#undef CHECK_RET + return answer_simple_hit(ctx, pkt, qry->stype, eh, eh_bound, new_ttl); } +#undef CHECK_RET int kr_cache_peek_exact(struct kr_cache *cache, const knot_dname_t *name, uint16_t type, struct kr_cache_p *peek) @@ -1572,20 +1600,16 @@ int kr_cache_peek_exact(struct kr_cache *cache, const knot_dname_t *name, uint16 return KR_STATE_FAIL; } - uint16_t ktype = type; - if (ktype == KNOT_RRTYPE_CNAME || ktype == KNOT_RRTYPE_DNAME) { - assert(false); // FIXME NOTIMPL ATM - ktype = KNOT_RRTYPE_NS; - } - knot_db_val_t key = key_exact_type(k, ktype); + knot_db_val_t key = key_exact_type(k, type); knot_db_val_t val = { }; ret = cache_op(cache, read, &key, &val, 1); + if (!ret) ret = entry_h_seek(&val, type); if (ret) { - kr_log_verbose("miss\n"); + kr_log_verbose("miss (ret: %d)\n", ret); return ret; } - const struct entry_h *eh = entry_h_consistent(val, ktype); - if (!eh || (type == KNOT_RRTYPE_NS && !eh->has_ns)) { + const struct entry_h *eh = entry_h_consistent(val, type); + if (!eh) { kr_log_verbose("miss\n"); return kr_error(ENOENT); } @@ -1605,18 +1629,22 @@ int kr_cache_peek_exact(struct kr_cache *cache, const knot_dname_t *name, uint16 * CNAME is only considered for equal name, of course. * We also store NSEC* parameters at NS type; probably the latest two will be kept. * Found type is returned via k->type. + * + * \param exact_match Whether exact match is considered special. */ -static const struct entry_h *closest_NS(kr_layer_t *ctx, struct key *k) +static knot_db_val_t closest_NS(kr_layer_t *ctx, struct key *k) { + static const knot_db_val_t NOTHING = {}; struct kr_request *req = ctx->req; struct kr_query *qry = req->current_query; struct kr_cache *cache = &req->ctx->cache; int zlf_len = k->buf[0]; - // FIXME: DS is parent-side record - bool exact_match = true; + // FIXME: review xNAME + DS, ranks, etc. + uint8_t rank_min = KR_RANK_INSECURE | KR_RANK_AUTH; // LATER(optim): if stype is NS, we check the same value again + bool exact_match = true; do { k->buf[0] = zlf_len; knot_db_val_t key = key_exact_type(k, KNOT_RRTYPE_NS); @@ -1624,30 +1652,71 @@ static const struct entry_h *closest_NS(kr_layer_t *ctx, struct key *k) int ret = cache_op(cache, read, &key, &val, 1); switch (ret) { case 0: { - const struct entry_h *eh = entry_h_consistent(val, KNOT_RRTYPE_NS); + /* Check consistency, find any type. + * "break;" goes to shortening by another label */ + const struct entry_h *eh = entry_h_consistent(val, KNOT_RRTYPE_NS), + *eh_orig = eh; + const knot_db_val_t val_orig = val; assert(eh); if (!eh) break; // do something about EILSEQ? - int32_t new_ttl = get_new_ttl(eh, qry->creation_time.tv_sec); - if (new_ttl < 0) break; - // FIXME: xNAME - //uint16_t ktype = exact_match ? KNOT_RRTYPE_CNAME : KNOT_RRTYPE_DNAME; - if (!eh->has_ns || eh->is_negative) { - break; + /* More types are possible; try in order. */ + uint16_t type = 0; + while (type != KNOT_RRTYPE_DNAME) { + /* Determine the next type to try. */ + switch (type) { + case 0: + type = KNOT_RRTYPE_NS; + if (!eh_orig->has_ns) continue; + break; + case KNOT_RRTYPE_NS: + type = KNOT_RRTYPE_CNAME; + /* CNAME is interesting only if we + * directly hit the name that was asked */ + if (!exact_match || !eh_orig->has_cname) + continue; + break; + case KNOT_RRTYPE_CNAME: + type = KNOT_RRTYPE_DNAME; + /* DNAME is interesting only if we did NOT + * directly hit the name that was asked */ + if (exact_match || !eh_orig->has_dname) + continue; + break; + default: + assert(false); + return NOTHING; + } + /* Find the entry for the type, check positivity, TTL + * For non-fatal failures just "continue;" to try the next type. */ + val = val_orig; + ret = entry_h_seek(&val, type); + if (ret || !(eh = entry_h_consistent(val, KNOT_RRTYPE_CNAME))) { + assert(false); + break; + } + if (eh->is_negative) continue; + int32_t new_ttl = get_new_ttl(eh, qry->creation_time.tv_sec); + if (new_ttl < 0) continue; + if (type != KNOT_RRTYPE_NS && eh->rank < rank_min) { + continue; + /* For NS any kr_rank is accepted, + * as insecure or even nonauth is OK */ + } + /* We found our match. */ + k->type = type; + k->zlf_len = zlf_len; + return val; } - /* any kr_rank is accepted, as insecure or even nonauth is OK */ - k->type = KNOT_RRTYPE_NS; - k->zlf_len = zlf_len; - return eh; } case (-abs(ENOENT)): break; default: assert(false); - return NULL; // TODO: do something with kr_error(ret)? + return NOTHING; // TODO: do something with kr_error(ret)? } WITH_VERBOSE { - VERBOSE_MSG(qry, "NS "); + VERBOSE_MSG(qry, "NS/xNAME "); kr_dname_print(k->zname, "", " NOT found, "); kr_log_verbose("name length in LF: %d\n", zlf_len); } @@ -1655,7 +1724,7 @@ static const struct entry_h *closest_NS(kr_layer_t *ctx, struct key *k) /* remove one more label */ exact_match = false; if (k->zname[0] == 0) { /* missing root NS in cache */ - return NULL; + return NOTHING; } zlf_len -= (k->zname[0] + 1); k->zname += (k->zname[0] + 1); -- 2.47.2