]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
add option to reorder cached RRs in answers
authorVladimír Čunát <vladimir.cunat@nic.cz>
Wed, 14 Sep 2016 10:53:01 +0000 (12:53 +0200)
committerVladimír Čunát <vladimir.cunat@nic.cz>
Mon, 24 Oct 2016 12:02:44 +0000 (14:02 +0200)
Fixes https://gitlab.labs.nic.cz/knot/resolver/issues/93.
API of a KR_EXPORT function is changed, so ABIVER is bumped.

13 files changed:
NEWS
config.mk
daemon/README.rst
daemon/engine.c
daemon/lua/sandbox.lua
lib/cache.c
lib/cache.h
lib/layer/rrcache.c
lib/rplan.c
lib/rplan.h
lib/utils.h
lib/zonecut.c
tests/test_cache.c

diff --git a/NEWS b/NEWS
index 319d662fd1227506f15a2a241cfb4790cfc1e1fd..4bd90776870b1bc3066f958105a4d1e68ea86223 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,8 @@
+Knot Resolver 1.x.y (2016-xx-yy)
+================================
+
+- It now requires knot >= 2.3.1 to link successfully.
+
 Knot Resolver 1.1.1 (2016-08-24)
 ================================
 
index fbed46eaa1ec456d94fa408a9a5772da52cb35d5..f83ee8ef31e38b6e04708eb65131035fb82c0cd2 100644 (file)
--- a/config.mk
+++ b/config.mk
@@ -2,7 +2,7 @@
 MAJOR := 1
 MINOR := 1
 PATCH := 1
-ABIVER := 1
+ABIVER := 2
 BUILDMODE := dynamic
 HARDENING := yes
 
index 758f1affa514ce6d884b658a4d559fc9a73a3d80..12619e685b88464a8127d243392dad90d18798b1 100644 (file)
@@ -394,6 +394,14 @@ Environment
     "Use in-bailiwick glue", "normal, permissive"
     "Use any glue records", "permissive"
 
+.. function:: reorder_RR([true | false])
+
+   :param boolean value: New value for the option *(optional)*
+   :return: The (new) value of the option
+
+   If set, resolver will vary the order of resource records within RR-sets
+   every time when answered from cache.  It is disabled by default.
+
 .. function:: user(name, [group])
 
    :param string name: user name
index 163a17ce9e41b48ccbc44428e98ff464fd11c3a2..15757d604439799466d2e32384a4a7d9949678ed 100644 (file)
@@ -66,6 +66,7 @@ static int l_help(lua_State *L)
                "verbose(true|false)\n    toggle verbose mode\n"
                "option(opt[, new_val])\n    get/set server option\n"
                "mode(strict|normal|permissive)\n    set resolver strictness level\n"
+               "reorder_RR([true|false])\n    set/get reordering of RRs within RRsets\n"
                "resolve(name, type[, class, flags, callback])\n    resolve query, callback when it's finished\n"
                "todname(name)\n    convert name to wire format\n"
                "tojson(val)\n    convert value to JSON\n"
index 41a9130ce9af189e628dcb543b619787313e0cca..4a5b3cb6d8a6b06143e28655d1d2b7b1003c4b8d 100644 (file)
@@ -49,6 +49,11 @@ function mode(m)
        return true
 end
 
+-- Trivial option alias
+function reorder_RR(val)
+       return option('REORDER_RR', val)
+end
+
 -- Function aliases
 -- `env.VAR returns os.getenv(VAR)`
 env = {}
index f642aeb8aaf11e05877b9cffb5cc7004055d915f..e3f356e20072ec4598d3aa4999a73d388331a888 100644 (file)
@@ -326,7 +326,8 @@ int kr_cache_peek_rank(struct kr_cache *cache, uint8_t tag, const knot_dname_t *
        return found->rank;
 }
 
-int kr_cache_materialize(knot_rrset_t *dst, const knot_rrset_t *src, uint32_t drift, knot_mm_t *mm)
+int kr_cache_materialize(knot_rrset_t *dst, const knot_rrset_t *src, uint32_t drift,
+               uint reorder, knot_mm_t *mm)
 {
        if (!dst || !src || dst == src) {
                return kr_error(EINVAL);
@@ -339,17 +340,36 @@ int kr_cache_materialize(knot_rrset_t *dst, const knot_rrset_t *src, uint32_t dr
                return kr_error(ENOMEM);
        }
 
-       /* Copy valid records */
+       /* Find valid records */
+       knot_rdata_t **valid = malloc(sizeof(knot_rdata_t *) * src->rrs.rr_count);
+       uint16_t valid_count = 0;
        knot_rdata_t *rd = src->rrs.data;
        for (uint16_t i = 0; i < src->rrs.rr_count; ++i) {
                if (knot_rdata_ttl(rd) >= drift) {
-                       if (knot_rdataset_add(&dst->rrs, rd, mm) != 0) {
-                               knot_rrset_clear(dst, mm);
-                               return kr_error(ENOMEM);
-                       }
+                       valid[valid_count++] = rd;
                }
                rd = kr_rdataset_next(rd);
        }
+
+       if (reorder && valid_count > 1) {
+               /* Reorder the valid part; it's a reversed rotation,
+                * done by two array reversals. */
+               uint16_t shift = reorder % valid_count;
+               for (uint16_t i = 0; i < shift / 2; ++i) {
+                       SWAP(valid[i], valid[shift - 1 - i]);
+               }
+               for (uint16_t i = 0; i < (valid_count - shift) / 2; ++i) {
+                       SWAP(valid[shift + i], valid[valid_count - 1 - i]);
+               }
+       }
+
+       int err = knot_rdataset_gather(&dst->rrs, valid, valid_count, mm);
+       free(valid);
+       if (err) {
+               knot_rrset_clear(dst, mm);
+               return kr_error(err);
+       }
+
        /* Fixup TTL by time passed */
        rd = dst->rrs.data;
        for (uint16_t i = 0; i < dst->rrs.rr_count; ++i) {
index ddf36272721008e9deebf105565065e9921eea76..71e47bea3cedb0c678abef68ed7c89b33b730fe2 100644 (file)
@@ -207,11 +207,13 @@ int kr_cache_peek_rr(struct kr_cache *cache, knot_rrset_t *rr, uint8_t *rank, ui
  * @param dst destination for materialized RRSet
  * @param src read-only RRSet (its rdataset may be changed depending on the result)
  * @param drift time passed between cache time and now
+ * @param reorder (pseudo)-random seed to reorder the data or zero
  * @param mm memory context
  * @return 0 or an errcode
  */
 KR_EXPORT
-int kr_cache_materialize(knot_rrset_t *dst, const knot_rrset_t *src, uint32_t drift, knot_mm_t *mm);
+int kr_cache_materialize(knot_rrset_t *dst, const knot_rrset_t *src, uint32_t drift,
+               uint reorder, knot_mm_t *mm);
 
 /**
  * Insert RRSet into cache, replacing any existing data.
index a0ca028f0f3666e9b01b7d12a44d6c19c49c719b..1cfcf6536dd5e624613969b79935b22df42945eb 100644 (file)
@@ -78,7 +78,7 @@ static int loot_rr(struct kr_cache *cache, knot_pkt_t *pkt, const knot_dname_t *
 
        /* Update packet answer */
        knot_rrset_t rr_copy;
-       ret = kr_cache_materialize(&rr_copy, &cache_rr, drift, &pkt->mm);
+       ret = kr_cache_materialize(&rr_copy, &cache_rr, drift, qry->reorder, &pkt->mm);
        if (ret == 0) {
                ret = knot_pkt_put(pkt, KNOT_COMPR_HINT_QNAME, &rr_copy, KNOT_PF_FREE);
                if (ret != 0) {
index e38a4d8c578b8f9d20ab8a5fd34e08e99b0640ca..b5915f964eb40cd249f417982fbf4d670af95fac 100644 (file)
@@ -132,6 +132,9 @@ static struct kr_query *kr_rplan_push_query(struct kr_rplan *rplan,
        qry->ns.addr[0].ip.sa_family = AF_UNSPEC;
        gettimeofday(&qry->timestamp, NULL);
        kr_zonecut_init(&qry->zone_cut, (const uint8_t *)"", rplan->pool);
+       qry->reorder = qry->flags & QUERY_REORDER_RR
+               ? knot_wire_get_id(rplan->request->answer->wire)
+               : 0;
        array_push(rplan->pending, qry);
 
        return qry;
index d06cb92a020f2901bbb813748fc54fe494f6336f..66bdef0995a1741aace3502913f302be04568bb3 100644 (file)
@@ -48,7 +48,8 @@
        X(PERMISSIVE,      1 << 20) /**< Permissive resolver mode. */ \
        X(STRICT,          1 << 21) /**< Strict resolver mode. */ \
        X(BADCOOKIE_AGAIN, 1 << 22) /**< Query again because bad cookie returned. */ \
-       X(CNAME,           1 << 23) /**< Query response contains CNAME in answer section. */
+       X(CNAME,           1 << 23) /**< Query response contains CNAME in answer section. */ \
+       X(REORDER_RR,      1 << 24) /**< Reorder cached RRs. */
 
 /** Query flags */
 enum kr_query_flag {
@@ -73,6 +74,7 @@ struct kr_query {
        uint32_t flags;
        uint32_t secret;
        uint16_t fails;
+       uint16_t reorder; /**< Seed to reorder (cached) RRs in answer or zero. */
        struct timeval timestamp;
        struct kr_zonecut zone_cut;
        struct kr_nsrep ns;
index b7ef1f2514c44eaf519a19af6de4fad50022e4b3..0eab195cfcaa348124e14c4019eee4a142c67fe0 100644 (file)
@@ -166,3 +166,12 @@ int kr_rrarray_add(rr_array_t *array, const knot_rrset_t *rr, knot_mm_t *pool);
  */
 KR_EXPORT
 char *kr_module_call(struct kr_context *ctx, const char *module, const char *prop, const char *input);
+
+/** Swap two places.  Note: the parameters need to be without side effects. */
+#define SWAP(x, y) do { /* http://stackoverflow.com/a/3982430/587396 */ \
+       unsigned char swap_temp[sizeof(x) == sizeof(y) ? (ssize_t)sizeof(x) : -1]; \
+       memcpy(swap_temp, &y, sizeof(x)); \
+       memcpy(&y,        &x, sizeof(x)); \
+       memcpy(&x, swap_temp, sizeof(x)); \
+       } while(0)
+
index 8b586c8332c9bd965f1d36f53062b1de4246afb8..610327dd804c7fcc716875af27d88437010e820a 100644 (file)
@@ -330,7 +330,7 @@ static int fetch_ns(struct kr_context *ctx, struct kr_zonecut *cut, const knot_d
 
        /* Materialize as we'll going to do more cache lookups. */
        knot_rrset_t rr_copy;
-       ret = kr_cache_materialize(&rr_copy, &cached_rr, drift, cut->pool);
+       ret = kr_cache_materialize(&rr_copy, &cached_rr, drift, 0, cut->pool);
        if (ret != 0) {
                return ret;
        }
@@ -380,7 +380,7 @@ static int fetch_rrset(knot_rrset_t **rr, struct kr_cache *cache,
                return kr_error(ENOMEM);
        }
 
-       ret = kr_cache_materialize(*rr, &cached_rr, drift, pool);
+       ret = kr_cache_materialize(*rr, &cached_rr, drift, 0, pool);
        if (ret != 0) {
                knot_rrset_free(rr, pool);
                return ret;
index f73a3b62ee95d65cdf5a30b1b7c39e388ece2541..4ba4d5322fe5a6926a63f0143c86880d31de45a6 100644 (file)
@@ -39,17 +39,19 @@ knot_db_val_t global_namedb_data = {namedb_data, NAMEDB_DATA_SIZE};
 #define CACHE_TTL 10
 #define CACHE_TIME 0
 
-int (*original_knot_rdataset_add)(knot_rdataset_t *rrs, const knot_rdata_t *rr, knot_mm_t *mm) = NULL;
+int (*original_knot_rdataset_gather)(knot_rdataset_t *dst, knot_rdata_t **src,
+               uint16_t count, knot_mm_t *mm) = NULL;
 
-int knot_rdataset_add(knot_rdataset_t *rrs, const knot_rdata_t *rr, knot_mm_t *mm)
+int knot_rdataset_gather(knot_rdataset_t *dst, knot_rdata_t **src, uint16_t count,
+               knot_mm_t *mm)
 {
        int err, err_mock;
        err_mock = (int)mock();
-       if (original_knot_rdataset_add == NULL) {
-               original_knot_rdataset_add = dlsym(RTLD_NEXT,"knot_rdataset_add");
-               assert_non_null (original_knot_rdataset_add);
+       if (original_knot_rdataset_gather == NULL) {
+               original_knot_rdataset_gather = dlsym(RTLD_NEXT,"knot_rdataset_gather");
+               assert_non_null (original_knot_rdataset_gather);
        }       
-       err = original_knot_rdataset_add(rrs, rr, mm);
+       err = original_knot_rdataset_gather(dst, src, count, mm);
        if (err_mock != 0)
            err = err_mock;
        return err;
@@ -235,7 +237,7 @@ static void test_materialize(void **state)
 
        global_rr.owner = NULL;
        knot_rrset_init(&output_rr, NULL, 0, 0);
-       kr_cache_materialize(&output_rr, &global_rr, 0, &global_mm);
+       kr_cache_materialize(&output_rr, &global_rr, 0, 0, &global_mm);
        res_cmp_ok_empty = knot_rrset_equal(&global_rr, &output_rr, KNOT_RRSET_COMPARE_HEADER);
        res_cmp_fail_empty = knot_rrset_equal(&global_rr, &output_rr, KNOT_RRSET_COMPARE_WHOLE);
        knot_rrset_clear(&output_rr, &global_mm);
@@ -244,15 +246,15 @@ static void test_materialize(void **state)
        assert_false(res_cmp_fail_empty);
 
        knot_rrset_init(&output_rr, NULL, 0, 0);
-       will_return (knot_rdataset_add, 0);
-       kr_cache_materialize(&output_rr, &global_rr, 0, &global_mm);
+       will_return (knot_rdataset_gather, 0);
+       kr_cache_materialize(&output_rr, &global_rr, 0, 0, &global_mm);
        res_cmp_ok = knot_rrset_equal(&global_rr, &output_rr, KNOT_RRSET_COMPARE_WHOLE);
        knot_rrset_clear(&output_rr, &global_mm);
        assert_true(res_cmp_ok);
 
        knot_rrset_init(&output_rr, NULL, 0, 0);
-       will_return (knot_rdataset_add, KNOT_EINVAL);
-       kr_cache_materialize(&output_rr, &global_rr, 0, &global_mm);
+       will_return (knot_rdataset_gather, KNOT_ENOMEM);
+       kr_cache_materialize(&output_rr, &global_rr, 0, 0, &global_mm);
        res_cmp_fail = knot_rrset_equal(&global_rr, &output_rr, KNOT_RRSET_COMPARE_WHOLE);
        knot_rrset_clear(&output_rr, &global_mm);
        assert_false(res_cmp_fail);