From: Vladimír Čunát Date: Wed, 14 Sep 2016 10:53:01 +0000 (+0200) Subject: add option to reorder cached RRs in answers X-Git-Tag: v1.2.0-rc1~89^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=aa880bca7e1d9c9ffe938ff11a56d52c8a5ef077;p=thirdparty%2Fknot-resolver.git add option to reorder cached RRs in answers Fixes https://gitlab.labs.nic.cz/knot/resolver/issues/93. API of a KR_EXPORT function is changed, so ABIVER is bumped. --- diff --git a/NEWS b/NEWS index 319d662fd..4bd907768 100644 --- 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) ================================ diff --git a/config.mk b/config.mk index fbed46eaa..f83ee8ef3 100644 --- a/config.mk +++ b/config.mk @@ -2,7 +2,7 @@ MAJOR := 1 MINOR := 1 PATCH := 1 -ABIVER := 1 +ABIVER := 2 BUILDMODE := dynamic HARDENING := yes diff --git a/daemon/README.rst b/daemon/README.rst index 758f1affa..12619e685 100644 --- a/daemon/README.rst +++ b/daemon/README.rst @@ -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 diff --git a/daemon/engine.c b/daemon/engine.c index 163a17ce9..15757d604 100644 --- a/daemon/engine.c +++ b/daemon/engine.c @@ -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" diff --git a/daemon/lua/sandbox.lua b/daemon/lua/sandbox.lua index 41a9130ce..4a5b3cb6d 100644 --- a/daemon/lua/sandbox.lua +++ b/daemon/lua/sandbox.lua @@ -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 = {} diff --git a/lib/cache.c b/lib/cache.c index f642aeb8a..e3f356e20 100644 --- a/lib/cache.c +++ b/lib/cache.c @@ -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) { diff --git a/lib/cache.h b/lib/cache.h index ddf362727..71e47bea3 100644 --- a/lib/cache.h +++ b/lib/cache.h @@ -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. diff --git a/lib/layer/rrcache.c b/lib/layer/rrcache.c index a0ca028f0..1cfcf6536 100644 --- a/lib/layer/rrcache.c +++ b/lib/layer/rrcache.c @@ -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) { diff --git a/lib/rplan.c b/lib/rplan.c index e38a4d8c5..b5915f964 100644 --- a/lib/rplan.c +++ b/lib/rplan.c @@ -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; diff --git a/lib/rplan.h b/lib/rplan.h index d06cb92a0..66bdef099 100644 --- a/lib/rplan.h +++ b/lib/rplan.h @@ -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; diff --git a/lib/utils.h b/lib/utils.h index b7ef1f251..0eab195cf 100644 --- a/lib/utils.h +++ b/lib/utils.h @@ -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) + diff --git a/lib/zonecut.c b/lib/zonecut.c index 8b586c833..610327dd8 100644 --- a/lib/zonecut.c +++ b/lib/zonecut.c @@ -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; diff --git a/tests/test_cache.c b/tests/test_cache.c index f73a3b62e..4ba4d5322 100644 --- a/tests/test_cache.c +++ b/tests/test_cache.c @@ -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);