]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
lib/cache/api: generalize ECS code to support longest prefix match
authorMarek Vavruša <mvavrusa@cloudflare.com>
Mon, 11 Jun 2018 00:12:00 +0000 (17:12 -0700)
committerMarek Vavruša <mvavrusa@cloudflare.com>
Fri, 7 Sep 2018 17:45:21 +0000 (10:45 -0700)
The current semantics is to try to look up an exact match, or
look into global scope. This commit changes that to longest prefix
match, so that wider cache scopes can be used to match against
multiple prefixes. In order to do this, the key scope format needs to
be changed from:

```
u8[] address
u8   scope_len
```

To:

```
u8[4 or 16] address
u8          scope_len
```

The fixed address length is necessary to be able to use lexicographic
lesser-or-equal scan supported by the database. For example, if the
search key is 192.168.0/24 (`\192\168\0\0\24'), any wider prefix
must be lexicographically smaller. The `\192\168\16` wouldn't be,
but the `\192\168\0\0\16` is, hence the key format change.

The fixed key size is also necessary to separate IPv4 scopes from
IPv6 scopes and vice versa. This is checked by comparing the
length of the found lesser-or-equal key - if the length is the same,
and key (without the scope part) is equal, the scope must be the same address
family.

daemon/lua/kres-gen.lua
daemon/lua/kres-gen.sh
daemon/lua/kres.lua
lib/cache/api.c
lib/cache/api.h
lib/cache/entry_pkt.c
lib/cache/impl.h
lib/cache/peek.c
lib/resolve.h

index 3a3ad30373e0d08b4d18143f83ca1cdb3e30941f..6698839b64f1673e1cfa7c7596d2fcbea0a3d6ff 100644 (file)
@@ -90,6 +90,12 @@ typedef struct {
        void *root;
        struct knot_mm *pool;
 } map_t;
+struct kr_cache_scope {
+       uint8_t family;
+       uint8_t scope_len;
+       uint8_t address[16];
+};
+typedef struct kr_cache_scope kr_cache_scope_t;
 struct kr_qflags {
        _Bool NO_MINIMIZE : 1;
        _Bool NO_THROTTLE : 1;
@@ -201,8 +207,7 @@ struct kr_request {
        trace_log_f trace_log;
        trace_callback_f trace_finish;
        int vars_ref;
-       int cache_scope_len_bits;
-       const uint8_t *cache_scope;
+       kr_cache_scope_t cache_scope;
        knot_mm_t pool;
 };
 enum kr_rank {KR_RANK_INITIAL, KR_RANK_OMIT, KR_RANK_TRY, KR_RANK_INDET = 4, KR_RANK_BOGUS, KR_RANK_MISMATCH, KR_RANK_MISSING, KR_RANK_INSECURE, KR_RANK_AUTH = 16, KR_RANK_SECURE = 32};
@@ -343,10 +348,9 @@ _Bool kr_dnssec_key_revoked(const uint8_t *);
 int kr_dnssec_key_tag(uint16_t, const uint8_t *, size_t);
 int kr_dnssec_key_match(const uint8_t *, size_t, const uint8_t *, size_t);
 int kr_cache_closest_apex(struct kr_cache *, const knot_dname_t *, _Bool, knot_dname_t **);
-int kr_cache_insert_rr(struct kr_cache *, const knot_rrset_t *, const knot_rrset_t *, uint8_t, uint32_t);
 int kr_cache_remove(struct kr_cache *, const knot_dname_t *, uint16_t);
 int kr_cache_remove_subtree(struct kr_cache *, const knot_dname_t *, _Bool, int);
-int kr_cache_insert_rr(struct kr_cache *, const knot_rrset_t *, const knot_rrset_t *, uint8_t, uint32_t, const uint8_t *, int);
+int kr_cache_insert_rr(struct kr_cache *, const knot_rrset_t *, const knot_rrset_t *, uint8_t, uint32_t, const kr_cache_scope_t *);
 int kr_cache_sync(struct kr_cache *);
 typedef struct {
        uint8_t bitmap[32];
index 108d65a3e9f95051614adc6f5c44f0393f986e4e..3398622b786cc59dde80ea345ccf07014bcf853f 100755 (executable)
@@ -66,6 +66,8 @@ genResType "knot_rrset_t" | sed 's/\<owner\>/_owner/; s/\<ttl\>/_ttl/'
        # generics
        map_t
        # libkres
+       struct kr_cache_scope
+       kr_cache_scope_t
        struct kr_qflags
        rr_array_t
        struct ranked_rr_array_entry
index 7f3feeef649ff6046ea093b48e447289b87489c7..7c5c9873fc13e22d52c04b26469d2e1106379705 100644 (file)
@@ -820,7 +820,7 @@ ffi.metatype(ranked_rr_array_t, {
 local kr_cache_t = ffi.typeof('struct kr_cache')
 ffi.metatype( kr_cache_t, {
        __index = {
-               insert = function (self, rr, rrsig, rank, timestamp, scope, scope_bits)
+               insert = function (self, rr, rrsig, rank, timestamp, scope)
                        assert(ffi.istype(kr_cache_t, self))
                        assert(ffi.istype(knot_rrset_t, rr), 'RR must be a rrset type')
                        assert(not rrsig or ffi.istype(knot_rrset_t, rrsig), 'RRSIG must be nil or of the rrset type')
@@ -831,7 +831,7 @@ ffi.metatype( kr_cache_t, {
                                timestamp = tonumber(now.tv_sec)
                        end
                        -- Insert record into cache
-                       local ret = C.kr_cache_insert_rr(self, rr, rrsig, tonumber(rank or 0), timestamp, scope, scope_bits or 0)
+                       local ret = C.kr_cache_insert_rr(self, rr, rrsig, tonumber(rank or 0), timestamp, scope)
                        if ret ~= 0 then return nil, knot_error_t(ret) end
                        return true
                end,
index b1732fb19e36d5264e29865668c9e1efce212faf..a7d796a8d2c87988629bfb51f8b13b8ec63b1642 100644 (file)
@@ -59,7 +59,7 @@ static const uint16_t CACHE_VERSION = 5;
 static ssize_t stash_rrset(struct kr_cache *cache, const struct kr_query *qry,
                const knot_rrset_t *rr, const knot_rrset_t *rr_sigs, uint32_t timestamp,
                uint8_t rank, trie_t *nsec_pmap, bool *has_optout,
-               const uint8_t *scope, int scope_len_bits);
+               kr_cache_scope_t *scope);
 /** Preliminary checks before stash_rrset().  Don't call if returns <= 0. */
 static int stash_rrset_precond(const knot_rrset_t *rr, const struct kr_query *qry/*logs*/);
 
@@ -157,13 +157,13 @@ int kr_cache_sync(struct kr_cache *cache)
 }
 
 int kr_cache_insert_rr(struct kr_cache *cache, const knot_rrset_t *rr, const knot_rrset_t *rrsig,
-                       uint8_t rank, uint32_t timestamp, const uint8_t *scope, int scope_len_bits)
+                       uint8_t rank, uint32_t timestamp, kr_cache_scope_t *scope)
 {
        int err = stash_rrset_precond(rr, NULL);
        if (err <= 0) {
                return kr_ok();
        }
-       ssize_t written = stash_rrset(cache, NULL, rr, rrsig, timestamp, rank, NULL, NULL, scope, scope_len_bits);
+       ssize_t written = stash_rrset(cache, NULL, rr, rrsig, timestamp, rank, NULL, NULL, scope);
                /* Zone's NSEC* parames aren't updated, but that's probably OK
                 * for kr_cache_insert_rr() */
        if (written >= 0) {
@@ -269,29 +269,56 @@ static bool check_rrtype(uint16_t type, const struct kr_query *qry/*logging*/)
        return ret;
 }
 
-int cache_key_write_scope(struct key *k, size_t off, const uint8_t *scope, int scope_len_bits)
+/** Helper to convert IANA address family codes to address lengths. */
+static int iana_family_to_length(int iana_family)
 {
-       const int scope_len_bytes = (scope_len_bits + 7) / 8;
-       if (!k || !scope || off + scope_len_bytes + 1 > KR_CACHE_KEY_MAXLEN) {
-               return kr_error(EINVAL);
+       switch (iana_family) {
+       case KNOT_ADDR_FAMILY_IPV4:
+               return 4;
+       case KNOT_ADDR_FAMILY_IPV6:
+               return 16;
+       default:
+               return 0;
        }
+}
 
-       /* Write scope at current offset */
-       memmove(k->buf + off, scope, scope_len_bytes);
+int cache_key_write_scope(struct key *k, size_t off, const kr_cache_scope_t *scope)
+{
+       if (!scope || scope->family == AF_UNSPEC) {
+               return 0;
+       }
 
-       /* Write a terminal byte to distinguish 'no scope' from 'global scope' */
-       k->buf[off + scope_len_bytes] = '\0';
+       int address_length = iana_family_to_length(scope->family);
+       if (!k || address_length <= 0 || off + address_length + 1 > KR_CACHE_KEY_MAXLEN) {
+               return kr_error(EINVAL);
+       }
 
-       return scope_len_bytes + 1;
+       /* Write scope at current offset */
+       const int scope_len_bytes = (scope->scope_len + 7) / 8;
+       memmove(k->buf + off, scope->address, scope_len_bytes);
+       memset(k->buf + off + scope_len_bytes, 0, address_length - scope_len_bytes);
+       /* Mask bits in the last byte if the scope length isn't divisible by 8
+        * e.g. 11111100/5 -> 11111000, it will shift right and left with zero extension.
+        * This is necessary for correct LEQ search when the bitmask does not end on the whole byte boundary.
+        */
+       const size_t shift = (scope_len_bytes * 8) - scope->scope_len;
+       if (shift != 0) {
+               const size_t last_byte_off = off + scope_len_bytes - 1;
+               k->buf[last_byte_off] = (k->buf[last_byte_off] >> shift) << shift;
+       }
+
+       /* Always write bitlength byte to distinguish 'not scoped' from 'global scope' */
+       k->buf[off + address_length] = scope->scope_len;
+
+       return address_length + 1;
 }
 
 /** Like key_exact_type() but omits a couple checks not holding for pkt cache. */
-knot_db_val_t key_exact_type_maypkt(struct key *k, uint16_t type, const uint8_t *scope, int scope_len_bits)
+knot_db_val_t key_exact_type_maypkt(struct key *k, uint16_t type, const kr_cache_scope_t *scope)
 {
        assert(check_rrtype(type, NULL));
        if (!is_scopable_type(type)) {
                scope = NULL;
-               scope_len_bits = 0;
        }
 
        switch (type) {
@@ -315,7 +342,7 @@ knot_db_val_t key_exact_type_maypkt(struct key *k, uint16_t type, const uint8_t
        k->type = type;
        off += sizeof(type);
 
-       int ret = cache_key_write_scope(k, off, scope, scope_len_bits);
+       int ret = cache_key_write_scope(k, off, scope);
        if (ret > 0) {
                off += ret;
        }
@@ -455,7 +482,7 @@ static int stash_rrset_precond(const knot_rrset_t *rr, const struct kr_query *qr
 static ssize_t stash_rrset(struct kr_cache *cache, const struct kr_query *qry,
                const knot_rrset_t *rr, const knot_rrset_t *rr_sigs, uint32_t timestamp,
                uint8_t rank, trie_t *nsec_pmap, bool *has_optout,
-               const uint8_t *scope, int scope_len)
+               kr_cache_scope_t *scope)
 {
        assert(stash_rrset_precond(rr, qry) > 0);
        if (!cache) {
@@ -476,7 +503,6 @@ static ssize_t stash_rrset(struct kr_cache *cache, const struct kr_query *qry,
 
        /* Construct the key under which RRs will be stored,
         * and add corresponding nsec_pmap item (if necessary). */
-       int used_scope_len = -1;
        struct key k_storage, *k = &k_storage;
        knot_db_val_t key;
        switch (rr->type) {
@@ -530,19 +556,16 @@ static ssize_t stash_rrset(struct kr_cache *cache, const struct kr_query *qry,
                        assert(!ret);
                        return kr_error(ret);
                }
-               /* Scope the record if authoritative, and scopeable type */
-               if ((!qry || !qry->parent) && kr_rank_test(rank, KR_RANK_AUTH) && is_scopable_type(rr->type)) {
-                       used_scope_len = scope_len;
-               } else {
-                       /*
-                       * Exclude infrastructure service requests (e.g. A/AAAA for an NS set)
-                       * and exclude non-authoritative data (records from other sections)
-                       */
+               /*
+                * Scope the record if it's authoritative rank, and scopeable type.
+                * Exclude infrastructure service requests (e.g. A/AAAA for an NS set)
+                * and exclude non-authoritative data (records from other sections)
+                */
+               if ((qry && qry->parent) || !kr_rank_test(rank, KR_RANK_AUTH) || !is_scopable_type(rr->type)) {
                        scope = NULL;
-                       scope_len = 0;
                }
-               
-               key = key_exact_type(k, rr->type, scope, scope_len);
+
+               key = key_exact_type(k, rr->type, scope);
        }
 
        /* Compute materialized sizes of the new data. */
@@ -603,7 +626,8 @@ static ssize_t stash_rrset(struct kr_cache *cache, const struct kr_query *qry,
                        *encl_str = kr_dname_text(encloser);
                VERBOSE_MSG(qry, "=> stashed %s%s %s, rank 0%.2o, scoped: %d "
                        "%d B total, incl. %d RRSIGs\n",
-                       (wild_labels ? "*." : ""), encl_str, type_str, rank, used_scope_len,
+                       (wild_labels ? "*." : ""), encl_str, type_str, rank,
+                       scope ? (int)scope->scope_len : 0,
                        (int)val_new_entry.len, (rr_sigs ? rr_sigs->rrs.count : 0)
                        );
        } }
@@ -647,7 +671,7 @@ static int stash_rrarray_entry(ranked_rr_array_t *arr, int arr_i,
 
        struct kr_request *req = qry->request;
        ssize_t written = stash_rrset(cache, qry, rr, rr_sigs, qry->timestamp.tv_sec,
-                                       entry->rank, nsec_pmap, has_optout, req->cache_scope, req->cache_scope_len_bits);
+                                       entry->rank, nsec_pmap, has_optout, &req->cache_scope);
        if (written < 0) {
                kr_log_error("[%5hu][cach] stash failed, ret = %d\n", qry->id, ret);
                return (int) written;
@@ -688,13 +712,14 @@ static int stash_nsec_p(const knot_dname_t *dname, const char *nsec_p_v,
        struct key k_storage, *k = &k_storage;
        int ret = kr_dname_lf(k->buf, dname, false);
        if (ret) return kr_error(ret);
-       knot_db_val_t key = key_exact_type(k, KNOT_RRTYPE_NS, NULL, 0);
+       knot_db_val_t key = key_exact_type(k, KNOT_RRTYPE_NS, NULL);
        knot_db_val_t val_orig = { NULL, 0 };
        ret = cache_op(cache, read, &key, &val_orig, 1);
        if (ret && ret != -ABS(ENOENT)) {
                VERBOSE_MSG(qry, "=> EL read failed (ret: %d)\n", ret);
                return kr_ok();
        }
+
        /* Prepare new entry_list_t so we can just write at el[0]. */
        entry_list_t el;
        int log_refresh_by = 0;
@@ -775,7 +800,7 @@ static int peek_exact_real(struct kr_cache *cache, const knot_dname_t *name, uin
        int ret = kr_dname_lf(k->buf, name, false);
        if (ret) return kr_error(ret);
 
-       knot_db_val_t key = key_exact_type(k, type, NULL, 0);
+       knot_db_val_t key = key_exact_type(k, type, NULL);
        knot_db_val_t val = { NULL, 0 };
        ret = cache_op(cache, read, &key, &val, 1);
        if (!ret) ret = entry_h_seek(&val, type);
@@ -823,7 +848,7 @@ int kr_cache_remove(struct kr_cache *cache, const knot_dname_t *name, uint16_t t
        int ret = kr_dname_lf(k->buf, name, false);
        if (ret) return kr_error(ret);
 
-       knot_db_val_t key = key_exact_type(k, type, NULL, 0);
+       knot_db_val_t key = key_exact_type(k, type, NULL);
        return cache_op(cache, remove, &key, 1);
 }
 
@@ -843,7 +868,7 @@ int kr_cache_match(struct kr_cache *cache, const knot_dname_t *name,
        if (ret) return kr_error(ret);
 
        // use a mock type
-       knot_db_val_t key = key_exact_type(k, KNOT_RRTYPE_A, NULL, 0);
+       knot_db_val_t key = key_exact_type(k, KNOT_RRTYPE_A, NULL);
        /* CACHE_KEY_DEF */
        key.len -= sizeof(uint16_t); /* the type */
        if (!exact_name) {
index f830dafe363022223c93cba409cb3759e7391d7a..2905f1185a57204d453b3231074ccc96cfe0e2f1 100644 (file)
@@ -54,6 +54,21 @@ struct kr_cache
        uint64_t checkpoint_monotime; /**< Monotonic milliseconds on the last check-point. */
 };
 
+/*
+ * Reuse ECS semantics for cache scoping - the scope is defined as network prefix.
+ * \see draft-ietf-dnsop-edns-client-subnet
+ */
+struct kr_cache_scope {
+       /*! \brief FAMILY */
+       uint8_t family;
+       /*! \brief SCOPE PREFIX-LENGTH */
+       uint8_t scope_len;
+       /*! \brief ADDRESS */
+       uint8_t address[KNOT_EDNS_CLIENT_SUBNET_ADDRESS_MAXLEN];
+};
+
+typedef struct kr_cache_scope kr_cache_scope_t;
+
 /**
  * Open/create cache with provided storage options.
  * @param cache cache structure to be initialized
@@ -100,12 +115,11 @@ static inline void kr_cache_make_checkpoint(struct kr_cache *cache)
  * @param rank rank of the data
  * @param timestamp current time
  * @param scope scope of the record
- * @param scope_len_bits scope of the record in bits
  * @return 0 or an errcode
  */
 KR_EXPORT
 int kr_cache_insert_rr(struct kr_cache *cache, const knot_rrset_t *rr, const knot_rrset_t *rrsig,
-                       uint8_t rank, uint32_t timestamp, const uint8_t *scope, int scope_len_bits);
+                       uint8_t rank, uint32_t timestamp, kr_cache_scope_t *scope);
 
 /**
  * Clear all items from the cache.
index 6256211497da34f92e9fcd6c77e702e674854f5f..ee9ea2e3725be0ec54ba5e333d45b144f953d295 100644 (file)
@@ -114,7 +114,7 @@ void stash_pkt(const knot_pkt_t *pkt, const struct kr_query *qry,
                assert(owner == NULL);
                return;
        }
-       key = key_exact_type_maypkt(k, pkt_type, NULL, 0);
+       key = key_exact_type_maypkt(k, pkt_type, NULL);
 
        /* For now we stash the full packet byte-exactly as it came from upstream. */
        const uint16_t pkt_size = pkt->size;
index 206b8d91252ddc22d25b27032e91a845e01133b7..cdcaaf84472bbea50a29949be035ed3c1bba120e 100644 (file)
@@ -159,18 +159,18 @@ static inline bool is_scopable_type(uint16_t type)
  *       .. 1   terminator \x00
  *
  *  The E tag has additional information:
- *       .. t   type in text (e.g AAAA, t = 1 .. 9 (as of now))
+ *       .. 2   type in wireformat (e.g AAAA = [0 28])
  *       .. s   cache scope (e.g. [192 168 1], s = 0 .. 16)
  */
-int cache_key_write_scope(struct key *k, size_t off, const uint8_t *scope, int scope_len_bits);
+int cache_key_write_scope(struct key *k, size_t off, const kr_cache_scope_t *scope);
 
 /** Finish constructing string key for for exact search.
  * It's assumed that kr_dname_lf(k->buf, owner, *) had been ran.
  */
-knot_db_val_t key_exact_type_maypkt(struct key *k, uint16_t type, const uint8_t *scope, int scope_len_bits);
+knot_db_val_t key_exact_type_maypkt(struct key *k, uint16_t type, const kr_cache_scope_t *scope);
 
 /** Like key_exact_type_maypkt but with extra checks if used for RRs only. */
-static inline knot_db_val_t key_exact_type(struct key *k, uint16_t type, const uint8_t *scope, int scope_len_bits)
+static inline knot_db_val_t key_exact_type(struct key *k, uint16_t type, const kr_cache_scope_t *scope)
 {
        switch (type) {
        /* Sanity check: forbidden types represented in other way(s). */
@@ -179,7 +179,7 @@ static inline knot_db_val_t key_exact_type(struct key *k, uint16_t type, const u
                assert(false);
                return (knot_db_val_t){ NULL, 0 };
        }
-       return key_exact_type_maypkt(k, type, scope, scope_len_bits);
+       return key_exact_type_maypkt(k, type, scope);
 }
 
 
index 2e03db3486a34e9890fa782d413658a065e18ba2..c13d69dd90e44eca09447150e418330a160659d2 100644 (file)
@@ -115,16 +115,61 @@ static uint8_t get_lowest_rank(const struct kr_request *req, const struct kr_que
 /**
  * Return cache scope as a hexstring.
  */
-static char *cache_scope_hex(const uint8_t *scope, int scope_len_bits)
+static char *cache_scope_hex(kr_cache_scope_t *scope)
 {
-       const int len = (scope_len_bits + 7) / 8;
+       if (!scope || scope->family == AF_UNSPEC) {
+               return NULL;
+       }
+
+       const int len = (scope->scope_len + 7) / 8;
        char *hex_str = calloc(1, len * 2 + 1);
        for (int i = 0; i < len; ++i) {
-               snprintf(hex_str + (i * 2), 3, "%02x", scope[i]);
+               snprintf(hex_str + (i * 2), 3, "%02x", scope->address[i]);
        }
+
        return hex_str;
 }
 
+static inline size_t cache_key_scope_off(struct key *k)
+{
+       /* Seek past the name [terminator, tag] + u16 type */
+       return (k->buf[0] + (2 * sizeof(uint8_t)) + sizeof(uint16_t));
+
+}
+
+/**
+ * Seek scope from the cache key.
+ * Note: see cache_key_write_scope documentation for key format reference.
+ */
+static int cache_key_read_scope(knot_db_val_t key, size_t off, const uint8_t **scope, uint8_t *scope_len)
+{
+       /* Check if there's at least bitlength byte */
+       if (key.len == 0 || off >= key.len) {
+               return kr_error(ENOENT);
+       }
+       /* Set pointer and retrieve bitlength */
+       const uint8_t *base = (const uint8_t *)key.data;
+       scope[0] = base + off;
+       scope_len[0] = base[key.len - 1];
+       return kr_ok();
+}
+
+/* Check that one scoped key covers another one (they're not necessarily equal) */
+static int cache_key_match_scope(knot_db_val_t wanted_key, knot_db_val_t found_key, size_t key_length, const kr_cache_scope_t *scope)
+{
+       /* Check that the key part (without the scope) matches to make sure the keys differ only in scope. */
+       if (found_key.len == wanted_key.len && memcmp(found_key.data, wanted_key.data, key_length) == 0) {
+               /* Parse the scope from cached key and check that it covers the requested scope */
+               uint8_t found_scope_len = 0;
+               const uint8_t *found_scope = NULL;
+               if (cache_key_read_scope(found_key, key_length, &found_scope, &found_scope_len) == 0 &&
+                       kr_bitcmp((const char *)found_scope, (const char *)scope->address, found_scope_len) == 0) {
+                               return kr_ok();
+               }
+       }
+       return kr_error(ENOENT);
+}
+
 /** Almost whole .produce phase for the cache module.
  * \note we don't transition to KR_STATE_FAIL even in case of "unexpected errors".
  */
@@ -145,16 +190,19 @@ int peek_nosync(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) */
-       knot_db_val_t key = key_exact_type_maypkt(k, qry->stype, req->cache_scope, req->cache_scope_len_bits);
+       kr_cache_scope_t *scope = &req->cache_scope;
+       knot_db_val_t key = key_exact_type_maypkt(k, qry->stype, scope);
        knot_db_val_t val = { NULL, 0 };
        ret = cache_op(cache, read, &key, &val, 1);
        /*  If the name is expected to be scope, but there's no scoped result in cache,
-        *  check global scope, as the name may not be scoped by server. */
-       if (req->cache_scope != NULL && ret && ret == -abs(ENOENT)) {
-               /* Retry using global scope */
-               VERBOSE_MSG(qry, "=> searching global scope /0\n");
-               key = key_exact_type_maypkt(k, qry->stype, req->cache_scope, 0);
-               ret = cache_op(cache, read, &key, &val, 1);
+        *  check closest scope, as the name may not be scoped by server. */
+       if (ret && ret == -abs(ENOENT) && scope->family != AF_UNSPEC && is_scopable_type(qry->stype)) {
+               knot_db_val_t wanted_key = key;
+               VERBOSE_MSG(qry, "=> searching closest scope for /%d\n", scope->scope_len);
+               int err = cache_op(cache, read_leq, &key, &val);
+               if (err >= 0) {
+                       ret = cache_key_match_scope(wanted_key, key, cache_key_scope_off(k), scope);
+               }
        }
        if (!ret) {
                /* found an entry: test conditions, materialize into pkt, etc. */
@@ -165,12 +213,6 @@ int peek_nosync(kr_layer_t *ctx, knot_pkt_t *pkt)
                assert(false);
                return ctx->state;
        } else if (!ret) {
-               WITH_VERBOSE(qry) {
-                       if (req->cache_scope && is_scopable_type(qry->stype)) {
-                               auto_free char *hex_str = cache_scope_hex(req->cache_scope, req->cache_scope_len_bits);
-                               VERBOSE_MSG(qry, "=> found exact match in scope %s/%d\n", hex_str, req->cache_scope_len_bits);
-                       }
-               }
                cache->stats.hit += 1;
                return KR_STATE_DONE;
        }
@@ -280,7 +322,7 @@ int peek_nosync(kr_layer_t *ctx, knot_pkt_t *pkt)
                /* Assuming k->buf still starts with zone's prefix,
                 * look up the SOA in cache. */
                k->buf[0] = k->zlf_len;
-               key = key_exact_type(k, KNOT_RRTYPE_SOA, NULL, 0);
+               key = key_exact_type(k, KNOT_RRTYPE_SOA, NULL);
                knot_db_val_t val = { NULL, 0 };
                ret = cache_op(cache, read, &key, &val, 1);
                const struct entry_h *eh;
@@ -483,11 +525,11 @@ static int answer_simple_hit(kr_layer_t *ctx, knot_pkt_t *pkt, uint16_t type,
        }
        WITH_VERBOSE(qry) {
                auto_free char *scope_hex = NULL;
-               if (req->cache_scope && is_scopable_type(type)) {
-                       scope_hex = cache_scope_hex(req->cache_scope, req->cache_scope_len_bits);
+               if (req->cache_scope.family != AF_UNSPEC && is_scopable_type(type)) {
+                       scope_hex = cache_scope_hex(&req->cache_scope);
                }
                VERBOSE_MSG(qry, "=> satisfied by exact RR or CNAME: rank 0%.2o, new TTL %d, scope %s/%d\n",
-                               eh->rank, new_ttl, scope_hex ? scope_hex : "", scope_hex ? req->cache_scope_len_bits : 0);
+                               eh->rank, new_ttl, scope_hex ? scope_hex : "", req->cache_scope.scope_len);
        }
        return kr_ok();
 }
@@ -540,7 +582,7 @@ static int try_wild(struct key *k, struct answer *ans, const knot_dname_t *clenc
                    const uint16_t type, const uint8_t lowest_rank,
                    const struct kr_query *qry, struct kr_cache *cache)
 {
-       knot_db_val_t key = key_exact_type(k, type, NULL, 0);
+       knot_db_val_t key = key_exact_type(k, type, NULL);
        /* Find the record. */
        knot_db_val_t val = { NULL, 0 };
        int ret = cache_op(cache, read, &key, &val, 1);
@@ -628,13 +670,11 @@ 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, uint8_t rank_min)
 {
        /* get the current timestamp */
-       const uint8_t *cache_scope = NULL;
-       int cache_scope_len_bits = 0;
+       kr_cache_scope_t *cache_scope = NULL;
        uint32_t timestamp;
        if (qry) {
                timestamp = qry->timestamp.tv_sec;
-               cache_scope = qry->request->cache_scope;
-               cache_scope_len_bits = qry->request->cache_scope_len_bits;
+               cache_scope = &qry->request->cache_scope;
        } else {
                struct timeval tv;
                if (gettimeofday(&tv, NULL)) return kr_error(errno);
@@ -653,13 +693,16 @@ static int closest_NS(struct kr_cache *cache, struct key *k, entry_list_t el,
                 * The CNAME is going to get rewritten to NS key, but it will be scoped if possible.
                 */
                const uint16_t find_type = exact_match ? KNOT_RRTYPE_CNAME : KNOT_RRTYPE_NS;
-               knot_db_val_t key = key_exact_type(k, find_type, cache_scope, cache_scope_len_bits);
+               knot_db_val_t key = key_exact_type(k, find_type, cache_scope);
                knot_db_val_t val;
                int ret = cache_op(cache, read, &key, &val, 1);
                /* Try in global scope if scoped, but no immediate match found */
-               if (exact_match && cache_scope != NULL && ret == -abs(ENOENT)) {
-                       key = key_exact_type_maypkt(k, KNOT_RRTYPE_NS, cache_scope, 0);
-                       ret = cache_op(cache, read, &key, &val, 1);
+               if (exact_match && ret == -abs(ENOENT) && cache_scope && cache_scope->family != AF_UNSPEC) {
+                       knot_db_val_t wanted_key = key;
+                       int err = cache_op(cache, read_leq, &key, &val);
+                       if (err >= 0) {
+                               ret = cache_key_match_scope(wanted_key, key, cache_key_scope_off(k), cache_scope);
+                       }
                }
                if (ret == -abs(ENOENT)) goto next_label;
                if (ret) {
index 95ee203a074e96920253fb814b75f6d105a9609b..f5db47c5347341076579868c395f800e0f50863a 100644 (file)
@@ -226,8 +226,7 @@ struct kr_request {
        trace_log_f trace_log; /**< Logging tracepoint */
        trace_callback_f trace_finish; /**< Request finish tracepoint */
        int vars_ref; /**< Reference to per-request variable table. LUA_NOREF if not set. */
-       int cache_scope_len_bits; /**< Cache scope length (bits) */
-       const uint8_t *cache_scope; /**< Cache scope for the request */
+       kr_cache_scope_t cache_scope; /**< Request cache scope */
        knot_mm_t pool;
 };