]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
new policy engine - prototype
authorVladimír Čunát <vladimir.cunat@nic.cz>
Wed, 22 Jul 2020 16:43:04 +0000 (18:43 +0200)
committerVladimír Čunát <vladimir.cunat@nic.cz>
Mon, 12 Jun 2023 08:31:43 +0000 (10:31 +0200)
Picked up old work, rebase-squashed after many months;
then fixed up a little as needed in this newer version.
(and later many minor fixes got squashed in)

24 files changed:
daemon/lua/kres-gen-30.lua
daemon/lua/kres-gen-31.lua
daemon/lua/kres-gen-32.lua
daemon/lua/kres-gen.sh
daemon/main.c
lib/cache/api.c
lib/cache/api.h
lib/cache/cdb_api.h
lib/cache/cdb_lmdb.c
lib/cache/cdb_lmdb.h
lib/cache/entry_rr.c
lib/cache/impl.h
lib/log.c
lib/log.h
lib/meson.build
lib/resolve.h
lib/rules/api.c [new file with mode: 0644]
lib/rules/api.h [new file with mode: 0644]
lib/rules/defaults.c [new file with mode: 0644]
lib/rules/impl.h [new file with mode: 0644]
modules/hints/hints.c
modules/policy/policy.lua
modules/stats/test.integr/kresd_config.j2
utils/cache_gc/db.c

index 4353c5ce00ad7b620a1128982194c296ac1caf1b..9ad421a1f908738fcb6f35d12d557358807b584d 100644 (file)
@@ -193,6 +193,7 @@ struct kr_request_qsource_flags {
        _Bool http : 1;
        _Bool xdp : 1;
 };
+typedef unsigned long kr_rule_tags_t;
 struct kr_extended_error {
        int32_t info_code;
        const char *extra_text;
@@ -239,6 +240,7 @@ struct kr_request {
        unsigned int count_no_nsaddr;
        unsigned int count_fail_row;
        alloc_wire_f alloc_wire_cb;
+       kr_rule_tags_t rule_tags;
        struct kr_extended_error extended_error;
 };
 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};
@@ -314,7 +316,7 @@ struct kr_server_selection {
        struct local_state *local_state;
 };
 typedef int kr_log_level_t;
-enum kr_log_group {LOG_GRP_UNKNOWN = -1, LOG_GRP_SYSTEM = 1, LOG_GRP_CACHE, LOG_GRP_IO, LOG_GRP_NETWORK, LOG_GRP_TA, LOG_GRP_TLS, LOG_GRP_GNUTLS, LOG_GRP_TLSCLIENT, LOG_GRP_XDP, LOG_GRP_DOH, LOG_GRP_DNSSEC, LOG_GRP_HINT, LOG_GRP_PLAN, LOG_GRP_ITERATOR, LOG_GRP_VALIDATOR, LOG_GRP_RESOLVER, LOG_GRP_SELECTION, LOG_GRP_ZCUT, LOG_GRP_COOKIES, LOG_GRP_STATISTICS, LOG_GRP_REBIND, LOG_GRP_WORKER, LOG_GRP_POLICY, LOG_GRP_TASENTINEL, LOG_GRP_TASIGNALING, LOG_GRP_TAUPDATE, LOG_GRP_DAF, LOG_GRP_DETECTTIMEJUMP, LOG_GRP_DETECTTIMESKEW, LOG_GRP_GRAPHITE, LOG_GRP_PREFILL, LOG_GRP_PRIMING, LOG_GRP_SRVSTALE, LOG_GRP_WATCHDOG, LOG_GRP_NSID, LOG_GRP_DNSTAP, LOG_GRP_TESTS, LOG_GRP_DOTAUTH, LOG_GRP_HTTP, LOG_GRP_CONTROL, LOG_GRP_MODULE, LOG_GRP_DEVEL, LOG_GRP_RENUMBER, LOG_GRP_EDE, LOG_GRP_REQDBG};
+enum kr_log_group {LOG_GRP_UNKNOWN = -1, LOG_GRP_SYSTEM = 1, LOG_GRP_CACHE, LOG_GRP_IO, LOG_GRP_NETWORK, LOG_GRP_TA, LOG_GRP_TLS, LOG_GRP_GNUTLS, LOG_GRP_TLSCLIENT, LOG_GRP_XDP, LOG_GRP_DOH, LOG_GRP_DNSSEC, LOG_GRP_HINT, LOG_GRP_PLAN, LOG_GRP_ITERATOR, LOG_GRP_VALIDATOR, LOG_GRP_RESOLVER, LOG_GRP_SELECTION, LOG_GRP_ZCUT, LOG_GRP_COOKIES, LOG_GRP_STATISTICS, LOG_GRP_REBIND, LOG_GRP_WORKER, LOG_GRP_POLICY, LOG_GRP_TASENTINEL, LOG_GRP_TASIGNALING, LOG_GRP_TAUPDATE, LOG_GRP_DAF, LOG_GRP_DETECTTIMEJUMP, LOG_GRP_DETECTTIMESKEW, LOG_GRP_GRAPHITE, LOG_GRP_PREFILL, LOG_GRP_PRIMING, LOG_GRP_SRVSTALE, LOG_GRP_WATCHDOG, LOG_GRP_NSID, LOG_GRP_DNSTAP, LOG_GRP_TESTS, LOG_GRP_DOTAUTH, LOG_GRP_HTTP, LOG_GRP_CONTROL, LOG_GRP_MODULE, LOG_GRP_DEVEL, LOG_GRP_RENUMBER, LOG_GRP_EDE, LOG_GRP_RULES, LOG_GRP_REQDBG};
 
 kr_layer_t kr_layer_t_static;
 _Bool kr_dbg_assertion_abort;
index a68dd653f3198b973c7df9ff1d908db29ac3bdd7..434adcfdf22503c42a91275fe7acd8625d24e0a2 100644 (file)
@@ -193,6 +193,7 @@ struct kr_request_qsource_flags {
        _Bool http : 1;
        _Bool xdp : 1;
 };
+typedef unsigned long kr_rule_tags_t;
 struct kr_extended_error {
        int32_t info_code;
        const char *extra_text;
@@ -239,6 +240,7 @@ struct kr_request {
        unsigned int count_no_nsaddr;
        unsigned int count_fail_row;
        alloc_wire_f alloc_wire_cb;
+       kr_rule_tags_t rule_tags;
        struct kr_extended_error extended_error;
 };
 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};
@@ -314,7 +316,7 @@ struct kr_server_selection {
        struct local_state *local_state;
 };
 typedef int kr_log_level_t;
-enum kr_log_group {LOG_GRP_UNKNOWN = -1, LOG_GRP_SYSTEM = 1, LOG_GRP_CACHE, LOG_GRP_IO, LOG_GRP_NETWORK, LOG_GRP_TA, LOG_GRP_TLS, LOG_GRP_GNUTLS, LOG_GRP_TLSCLIENT, LOG_GRP_XDP, LOG_GRP_DOH, LOG_GRP_DNSSEC, LOG_GRP_HINT, LOG_GRP_PLAN, LOG_GRP_ITERATOR, LOG_GRP_VALIDATOR, LOG_GRP_RESOLVER, LOG_GRP_SELECTION, LOG_GRP_ZCUT, LOG_GRP_COOKIES, LOG_GRP_STATISTICS, LOG_GRP_REBIND, LOG_GRP_WORKER, LOG_GRP_POLICY, LOG_GRP_TASENTINEL, LOG_GRP_TASIGNALING, LOG_GRP_TAUPDATE, LOG_GRP_DAF, LOG_GRP_DETECTTIMEJUMP, LOG_GRP_DETECTTIMESKEW, LOG_GRP_GRAPHITE, LOG_GRP_PREFILL, LOG_GRP_PRIMING, LOG_GRP_SRVSTALE, LOG_GRP_WATCHDOG, LOG_GRP_NSID, LOG_GRP_DNSTAP, LOG_GRP_TESTS, LOG_GRP_DOTAUTH, LOG_GRP_HTTP, LOG_GRP_CONTROL, LOG_GRP_MODULE, LOG_GRP_DEVEL, LOG_GRP_RENUMBER, LOG_GRP_EDE, LOG_GRP_REQDBG};
+enum kr_log_group {LOG_GRP_UNKNOWN = -1, LOG_GRP_SYSTEM = 1, LOG_GRP_CACHE, LOG_GRP_IO, LOG_GRP_NETWORK, LOG_GRP_TA, LOG_GRP_TLS, LOG_GRP_GNUTLS, LOG_GRP_TLSCLIENT, LOG_GRP_XDP, LOG_GRP_DOH, LOG_GRP_DNSSEC, LOG_GRP_HINT, LOG_GRP_PLAN, LOG_GRP_ITERATOR, LOG_GRP_VALIDATOR, LOG_GRP_RESOLVER, LOG_GRP_SELECTION, LOG_GRP_ZCUT, LOG_GRP_COOKIES, LOG_GRP_STATISTICS, LOG_GRP_REBIND, LOG_GRP_WORKER, LOG_GRP_POLICY, LOG_GRP_TASENTINEL, LOG_GRP_TASIGNALING, LOG_GRP_TAUPDATE, LOG_GRP_DAF, LOG_GRP_DETECTTIMEJUMP, LOG_GRP_DETECTTIMESKEW, LOG_GRP_GRAPHITE, LOG_GRP_PREFILL, LOG_GRP_PRIMING, LOG_GRP_SRVSTALE, LOG_GRP_WATCHDOG, LOG_GRP_NSID, LOG_GRP_DNSTAP, LOG_GRP_TESTS, LOG_GRP_DOTAUTH, LOG_GRP_HTTP, LOG_GRP_CONTROL, LOG_GRP_MODULE, LOG_GRP_DEVEL, LOG_GRP_RENUMBER, LOG_GRP_EDE, LOG_GRP_RULES, LOG_GRP_REQDBG};
 
 kr_layer_t kr_layer_t_static;
 _Bool kr_dbg_assertion_abort;
index 222891e3fb6b7e67ab2cea01f4140f4a11e936d6..812397b2ff5a88e2174e22a5a36311db1e87cc46 100644 (file)
@@ -194,6 +194,17 @@ struct kr_request_qsource_flags {
        _Bool http : 1;
        _Bool xdp : 1;
 };
+typedef unsigned long kr_rule_tags_t;
+struct kr_rule_zonefile_config {
+       const char *filename;
+       const char *input_str;
+       size_t input_len;
+       _Bool is_rpz;
+       _Bool nodata;
+       kr_rule_tags_t tags;
+       const char *origin;
+       uint32_t ttl;
+};
 struct kr_extended_error {
        int32_t info_code;
        const char *extra_text;
@@ -240,6 +251,7 @@ struct kr_request {
        unsigned int count_no_nsaddr;
        unsigned int count_fail_row;
        alloc_wire_f alloc_wire_cb;
+       kr_rule_tags_t rule_tags;
        struct kr_extended_error extended_error;
 };
 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};
@@ -315,7 +327,7 @@ struct kr_server_selection {
        struct local_state *local_state;
 };
 typedef int kr_log_level_t;
-enum kr_log_group {LOG_GRP_UNKNOWN = -1, LOG_GRP_SYSTEM = 1, LOG_GRP_CACHE, LOG_GRP_IO, LOG_GRP_NETWORK, LOG_GRP_TA, LOG_GRP_TLS, LOG_GRP_GNUTLS, LOG_GRP_TLSCLIENT, LOG_GRP_XDP, LOG_GRP_DOH, LOG_GRP_DNSSEC, LOG_GRP_HINT, LOG_GRP_PLAN, LOG_GRP_ITERATOR, LOG_GRP_VALIDATOR, LOG_GRP_RESOLVER, LOG_GRP_SELECTION, LOG_GRP_ZCUT, LOG_GRP_COOKIES, LOG_GRP_STATISTICS, LOG_GRP_REBIND, LOG_GRP_WORKER, LOG_GRP_POLICY, LOG_GRP_TASENTINEL, LOG_GRP_TASIGNALING, LOG_GRP_TAUPDATE, LOG_GRP_DAF, LOG_GRP_DETECTTIMEJUMP, LOG_GRP_DETECTTIMESKEW, LOG_GRP_GRAPHITE, LOG_GRP_PREFILL, LOG_GRP_PRIMING, LOG_GRP_SRVSTALE, LOG_GRP_WATCHDOG, LOG_GRP_NSID, LOG_GRP_DNSTAP, LOG_GRP_TESTS, LOG_GRP_DOTAUTH, LOG_GRP_HTTP, LOG_GRP_CONTROL, LOG_GRP_MODULE, LOG_GRP_DEVEL, LOG_GRP_RENUMBER, LOG_GRP_EDE, LOG_GRP_REQDBG};
+enum kr_log_group {LOG_GRP_UNKNOWN = -1, LOG_GRP_SYSTEM = 1, LOG_GRP_CACHE, LOG_GRP_IO, LOG_GRP_NETWORK, LOG_GRP_TA, LOG_GRP_TLS, LOG_GRP_GNUTLS, LOG_GRP_TLSCLIENT, LOG_GRP_XDP, LOG_GRP_DOH, LOG_GRP_DNSSEC, LOG_GRP_HINT, LOG_GRP_PLAN, LOG_GRP_ITERATOR, LOG_GRP_VALIDATOR, LOG_GRP_RESOLVER, LOG_GRP_SELECTION, LOG_GRP_ZCUT, LOG_GRP_COOKIES, LOG_GRP_STATISTICS, LOG_GRP_REBIND, LOG_GRP_WORKER, LOG_GRP_POLICY, LOG_GRP_TASENTINEL, LOG_GRP_TASIGNALING, LOG_GRP_TAUPDATE, LOG_GRP_DAF, LOG_GRP_DETECTTIMEJUMP, LOG_GRP_DETECTTIMESKEW, LOG_GRP_GRAPHITE, LOG_GRP_PREFILL, LOG_GRP_PRIMING, LOG_GRP_SRVSTALE, LOG_GRP_WATCHDOG, LOG_GRP_NSID, LOG_GRP_DNSTAP, LOG_GRP_TESTS, LOG_GRP_DOTAUTH, LOG_GRP_HTTP, LOG_GRP_CONTROL, LOG_GRP_MODULE, LOG_GRP_DEVEL, LOG_GRP_RENUMBER, LOG_GRP_EDE, LOG_GRP_RULES, LOG_GRP_REQDBG};
 
 kr_layer_t kr_layer_t_static;
 _Bool kr_dbg_assertion_abort;
index 70afb408b297e81185313c50ecc478d2ad030da1..3d6fcfaf10d8788389260ac941dfd7e4a2070e1d 100755 (executable)
@@ -125,6 +125,7 @@ ${CDEFS} ${LIBKRES} types <<-EOF
        kr_qarray_t
        struct kr_rplan
        struct kr_request_qsource_flags
+       kr_rule_tags_t
        struct kr_extended_error
        struct kr_request
        enum kr_rank
index 41a55ad525a8c207b51a61c9d288582b0d8cdb5e..8fac25f6e9b2d143c55343814d170a9e97e602fc 100644 (file)
@@ -13,6 +13,8 @@
 #include "lib/defines.h"
 #include "lib/dnssec.h"
 #include "lib/log.h"
+#include "lib/resolve.h"
+#include "lib/rules/api.h"
 
 #include <arpa/inet.h>
 #include <getopt.h>
@@ -575,6 +577,14 @@ int main(int argc, char **argv)
                goto cleanup;
        }
 
+       ret = kr_rules_init();
+       if (ret) {
+               kr_log_error(RULES, "failed to initialize policy rule engine: %s\n",
+                               kr_strerror(ret));
+               ret = EXIT_FAILURE;
+               goto cleanup;
+       }
+
        for (i = 0; i < the_args->config.len; ++i) {
                const char *config = the_args->config.at[i];
                if (engine_loadconf(&engine, config) != 0) {
@@ -602,6 +612,7 @@ int main(int argc, char **argv)
 cleanup:/* Cleanup. */
        engine_deinit(&engine);
        worker_deinit();
+       kr_rules_deinit();
        if (loop != NULL) {
                uv_loop_close(loop);
        }
index c1db755ed58130087461f7ba1df32ec188862b4f..501d68398fca85ae41b29d6c3de9bd5ec54d7620 100644 (file)
@@ -26,6 +26,7 @@
 #include "lib/generic/trie.h"
 #include "lib/resolve.h"
 #include "lib/rplan.h"
+#include "lib/rules/api.h"
 #include "lib/utils.h"
 
 #include "lib/cache/impl.h"
@@ -124,8 +125,7 @@ int kr_cache_open(struct kr_cache *cache, const struct kr_cdb_api *api, struct k
                 * LMDB only restricts our env without changing the in-file maxsize.
                 * That is worked around by reopening (found no other reliable way). */
                cache->api->close(cache->db, &cache->stats);
-               struct kr_cdb_opts opts2;
-               memcpy(&opts2, opts, sizeof(opts2));
+               struct kr_cdb_opts opts2 = *opts;
                opts2.maxsize = 0;
                ret = cache->api->open(&cache->db, &cache->stats, &opts2, mm);
        }
@@ -326,6 +326,7 @@ int cache_peek(kr_layer_t *ctx, knot_pkt_t *pkt)
        }
        /* ATM cache only peeks for qry->sname and that would be useless
         * to repeat on every iteration, so disable it from now on.
+        * Note that xNAME causes a followup kr_query, so cache will get re-tried.
         * LATER(optim.): assist with more precise QNAME minimization. */
        qry->flags.CACHE_TRIED = true;
 
@@ -337,7 +338,14 @@ int cache_peek(kr_layer_t *ctx, knot_pkt_t *pkt)
                return ctx->state;
        }
 
-       int ret = peek_nosync(ctx, pkt);
+       /* TODO: we _might_ want to process rules here even when some of the cache
+        * exit-conditions happen, though I don't expect these cases to be important. */
+       int ret = kr_rule_local_data_answer(qry, pkt);
+       if (ret != -ENOENT) {
+               return ret;
+       }
+
+       ret = peek_nosync(ctx, pkt);
        kr_cache_commit(&req->ctx->cache);
        return ret;
 }
index 0abe9202ffa1a5888a7a58fe1e13b2ed793af549..454f629e812e1bad5a378216b7152ae946fc2b34 100644 (file)
@@ -105,7 +105,10 @@ KR_EXPORT
 int kr_cache_clear(struct kr_cache *cache);
 
 
-/* ** This interface is temporary. ** */
+/* ** This interface is temporary. **
+ * _peek_exact() doesn't look e.g. at signed wildcards
+ * or at local data defined in rules
+ * */
 
 struct kr_cache_p {
        uint32_t time;  /**< The time of inception. */
index fcca8a9ad56d6342a8541ced5757dc9c1bdcc5c6..f0d810fcc3eb09cbc94d6243c45c1e8911dd37dc 100644 (file)
@@ -4,6 +4,7 @@
 
 #pragma once
 
+#include <stdbool.h>
 #include <stdint.h>
 
 #include <libknot/db/db.h>
@@ -12,6 +13,7 @@
 struct kr_cdb_opts {
        const char *path; /*!< Cache URI path. */
        size_t maxsize;   /*!< Suggested cache size in bytes; pass 0 to keep unchanged/default. */
+       bool is_cache;    /*!< Some behavior changes based on use case.  TODO: details. */
 };
 
 struct kr_cdb_stats {
index 80c73729ea5d548dfbf694490dfb71d85bb929e8..1b6c33f5fc9e60788a50aa90ebf3db79f655f073 100644 (file)
@@ -42,6 +42,8 @@ struct lmdb_env
                MDB_cursor *ro_curs;
        } txn;
 
+       bool is_cache; /**< cache vs. rules; from struct kr_cdb_opts::is_cache */
+
        /* Cached part of struct stat for data.mdb. */
        dev_t st_dev;
        ino_t st_ino;
@@ -336,9 +338,11 @@ static int cdb_open_env(struct lmdb_env *env, const char *path, const size_t map
                if (ret != MDB_SUCCESS) goto error_mdb;
        }
 
-       /* Cache doesn't require durability, we can be
-        * loose with the requirements as a tradeoff for speed. */
-       const unsigned flags = MDB_WRITEMAP | MDB_MAPASYNC | MDB_NOTLS;
+       const unsigned flags = env->is_cache
+               /* Cache doesn't require durability, we can be
+                * loose with the requirements as a tradeoff for speed. */
+               ? MDB_WRITEMAP | MDB_NOTLS | MDB_MAPASYNC
+               : MDB_WRITEMAP | MDB_NOTLS;
        ret = mdb_env_open(env->env, path, flags, LMDB_FILE_MODE);
        if (ret != MDB_SUCCESS) goto error_mdb;
 
@@ -367,14 +371,19 @@ static int cdb_open_env(struct lmdb_env *env, const char *path, const size_t map
        ret = mdb_txn_begin(env->env, NULL, 0, &txn);
        if (ret != MDB_SUCCESS) goto error_mdb;
 
-       ret = mdb_dbi_open(txn, NULL, 0, &env->dbi);
+
+       //FIXME: perhaps we want MDB_DUPSORT in future,
+       //  but for that we'd have to avoid MDB_RESERVE.
+       //  (including a proper assertion, instead of sometimes-crash inside lmdb)
+       const unsigned dbi_flags = 0; //is_cache ? 0 : MDB_DUPSORT;
+       ret = mdb_dbi_open(txn, NULL, dbi_flags, &env->dbi);
        if (ret != MDB_SUCCESS) {
                mdb_txn_abort(txn);
                goto error_mdb;
        }
 
 #if !defined(__MACOSX__) && !(defined(__APPLE__) && defined(__MACH__))
-       if (size_requested) {
+       if (size_requested && env->is_cache) { // prealloc makes no sense for rules
                ret = posix_fallocate(fd, 0, MAX(env->mapsize, env->st_size));
        } else {
                ret = 0;
@@ -424,6 +433,8 @@ static int cdb_init(kr_cdb_pt *db, struct kr_cdb_stats *stats,
        if (!env) {
                return kr_error(ENOMEM);
        }
+       env->is_cache = opts->is_cache;
+
        int ret = cdb_open_env(env, opts->path, opts->maxsize, stats);
        if (ret != 0) {
                free(env);
@@ -542,6 +553,7 @@ static int lockfile_release(int fd)
 
 static int cdb_clear(kr_cdb_pt db, struct kr_cdb_stats *stats)
 {
+       //TODO: adjust logging based on env->is_cache
        struct lmdb_env *env = db2env(db);
        stats->clear++;
        /* First try mdb_drop() to clear the DB; this may fail with ENOSPC. */
@@ -859,10 +871,8 @@ const struct kr_cdb_api *kr_cdb_lmdb(void)
                cdb_readv, cdb_writev, cdb_remove,
                cdb_match,
                cdb_read_leq,
-               cdb_usage_percent,
-               cdb_get_maxsize,
+               cdb_usage_percent, cdb_get_maxsize,
                cdb_check_health,
        };
-
        return &api;
 }
index 988fccf07833ab4652b61016bcb960e3a5739c73..6eb64e04960da1526bb410fab13ddd3e0ed02c40 100644 (file)
@@ -7,6 +7,7 @@
 #include "lib/cache/cdb_api.h"
 #include "lib/defines.h"
 
+/** Get API implementation for LMDB. */
 KR_EXPORT KR_CONST
 const struct kr_cdb_api *kr_cdb_lmdb(void);
 
index 3239e7e54260fd11734106d5d647850df02415cc..5fcdf880ca3765e65a54a00e3043948f73f24204 100644 (file)
@@ -28,10 +28,7 @@ void rdataset_dematerialize(const knot_rdataset_t *rds, uint8_t * restrict data)
        (void)data; // silence analyzers
 }
 
-/** Materialize a knot_rdataset_t from cache with given TTL.
- * Return the number of bytes consumed or an error code.
- */
-static int rdataset_materialize(knot_rdataset_t * restrict rds, const uint8_t * const data,
+int rdataset_materialize(knot_rdataset_t * restrict rds, const uint8_t * const data,
                                const uint8_t *data_bound, knot_mm_t *pool)
 {
        if (kr_fails_assert(rds && data && data_bound && data_bound > data && !rds->rdata
index d650e379fc5fb5986572cbba7e6253f76d1ac326..9b5cb2f453f43e9cc6a931303d8c9f3218fa9e27 100644 (file)
@@ -328,6 +328,11 @@ static inline int rdataset_dematerialized_size(const uint8_t *data, uint16_t *rd
 /** Serialize an rdataset.  It may be NULL as short-hand for empty. */
 void rdataset_dematerialize(const knot_rdataset_t *rds, uint8_t * restrict data);
 
+/** Materialize a knot_rdataset_t from cache.
+ * Return the number of bytes consumed or an error code. */
+int rdataset_materialize(knot_rdataset_t * restrict rds, const uint8_t * const data,
+                               const uint8_t *data_bound, knot_mm_t *pool);
+
 
 /** Partially constructed answer when gathering RRsets from cache. */
 struct answer {
index 1a3d7154172dbfedff585b09001daab06f1e2c12..dd32dcb7e8b52f1ac5002a82b1d7e2b6521f7b2e 100644 (file)
--- a/lib/log.c
+++ b/lib/log.c
@@ -78,6 +78,7 @@ const log_group_names_t log_group_names[] = {
        GRP_NAME_ITEM(LOG_GRP_DEVEL),
        GRP_NAME_ITEM(LOG_GRP_RENUMBER),
        GRP_NAME_ITEM(LOG_GRP_EDE),
+       GRP_NAME_ITEM(LOG_GRP_RULES),
        GRP_NAME_ITEM(LOG_GRP_REQDBG),
        { NULL, LOG_GRP_UNKNOWN },
 };
index 1a0237a18e5c9f1dd5c0220d17f350cb3f5d8f30..d6a91a6b9ca960bdc05684ee6d330eadd83cc12a 100644 (file)
--- a/lib/log.h
+++ b/lib/log.h
@@ -79,6 +79,7 @@ enum kr_log_group {
        LOG_GRP_DEVEL,
        LOG_GRP_RENUMBER,
        LOG_GRP_EDE,
+       LOG_GRP_RULES,
        /* ^^ Add new log groups above ^^. */
        LOG_GRP_REQDBG, /* Must be first non-displayed entry in enum! */
 };
@@ -131,6 +132,7 @@ enum kr_log_group {
 #define LOG_GRP_DEVEL_TAG              "devel"         /**< ``devel``: for development purposes */
 #define LOG_GRP_RENUMBER_TAG           "renum"         /**< ``renum``: operation related to renumber */
 #define LOG_GRP_EDE_TAG                        "exterr"        /**< ``exterr``: extended error module */
+#define LOG_GRP_RULES_TAG              "rules"         /**< ``rules``: new policy rules (their processing) */
 #define LOG_GRP_REQDBG_TAG             "reqdbg"        /**< ``reqdbg``: debug logs enabled by policy actions */
 ///@}
 
index ec11da9f52cc840fda08bd880298644a8d70e862..d3f3772a1416644bb0819a6dcceeb2c8a93d4e4b 100644 (file)
@@ -23,6 +23,8 @@ libkres_src = files([
   'layer/iterate.c',
   'layer/validate.c',
   'log.c',
+  'rules/api.c',
+  'rules/defaults.c',
   'module.c',
   'resolve.c',
   'rplan.c',
@@ -56,6 +58,8 @@ libkres_headers = files([
   'module.h',
   'resolve.h',
   'rplan.h',
+  'rules/api.h',
+  'rules/impl.h',
   'selection.h',
   'selection_forward.h',
   'selection_iter.h',
index 97ba07b7ade9b6a919fd58ead0d40f2e88d114c7..1a10f6aeae71987d999f3da21063bdea57640369 100644 (file)
@@ -16,6 +16,7 @@
 #include "lib/rplan.h"
 #include "lib/module.h"
 #include "lib/cache/api.h"
+#include "lib/rules/api.h"
 
 /**
  * @file resolve.h
@@ -270,6 +271,7 @@ struct kr_request {
        unsigned int count_no_nsaddr;
        unsigned int count_fail_row;
        alloc_wire_f alloc_wire_cb; /**< CB to allocate answer wire (can be NULL). */
+       kr_rule_tags_t rule_tags; /**< TagSet applying to this request. */
        struct kr_extended_error extended_error;  /**< EDE info; don't modify directly, use kr_request_set_extended_error() */
 };
 
diff --git a/lib/rules/api.c b/lib/rules/api.c
new file mode 100644 (file)
index 0000000..1650e6e
--- /dev/null
@@ -0,0 +1,497 @@
+/*  Copyright (C) CZ.NIC, z.s.p.o. <knot-resolver@labs.nic.cz>
+ *  SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include "lib/rules/api.h"
+#include "lib/rules/impl.h"
+
+#include "lib/cache/cdb_lmdb.h"
+
+#include <stdlib.h>
+
+#include "lib/cache/impl.h"
+#undef VERBOSE_MSG
+#define VERBOSE_MSG(qry, ...) kr_log_q((qry), RULES,  ## __VA_ARGS__)
+
+struct kr_rules {
+       /* Database for storing the rules (LMDB). */
+       kr_cdb_pt db;                 /**< Storage instance */
+       const struct kr_cdb_api *api; /**< Storage engine */
+       struct kr_cdb_stats stats;
+};
+
+struct kr_rules *the_rules = NULL;
+#define ruledb_op(op, ...) \
+       the_rules->api->op(the_rules->db, &the_rules->stats, ## __VA_ARGS__)
+
+/* DB key-space summary
+
+ - "\0" starts special keys like "\0rulesets" or "\0stamp"
+ - some future additions?
+ - otherwise it's rulesets - each has a prefix, e.g. RULESET_DEFAULT,
+   its length is bounded by KEY_RULESET_MAXLEN - 1; after that prefix:
+    - KEY_EXACT_MATCH + dname_lf ended by double '\0' + KNOT_RRTYPE_FOO
+       -> exact-match rule (for the given name)
+    - KEY_ZONELIKE_A  + dname_lf (no '\0' at end)
+       -> zone-like apex (on the given name)
+ */
+
+#define KEY_RULESET_MAXLEN 16 /**< max. len of ruleset ID + 1(for kind) */
+static /*const*/ char RULESET_DEFAULT[] = "d";
+
+static const uint8_t KEY_EXACT_MATCH[1] = "e";
+static const uint8_t KEY_ZONELIKE_A [1] = "a";
+
+/** The first byte of zone-like apex value is its type. */
+typedef uint8_t val_zla_type_t;
+enum {
+       /** Empty zone. No data in DB value after this byte. */
+       VAL_ZLAT_EMPTY = 1,
+};
+
+
+static int answer_exact_match(struct kr_query *qry, knot_pkt_t *pkt, uint16_t type,
+               const uint8_t *data, const uint8_t *data_bound);
+static int answer_zla_empty(struct kr_query *qry, knot_pkt_t *pkt,
+               knot_db_val_t zla_lf, knot_db_val_t val);
+
+//TODO later, maybe.  ATM it would be cumbersome to avoid void* arithmetics.
+#pragma GCC diagnostic ignored "-Wpointer-arith"
+
+int kr_rules_init(void)
+{
+       kr_require(!the_rules);
+       the_rules = calloc(1, sizeof(*the_rules));
+       kr_require(the_rules);
+       the_rules->api = kr_cdb_lmdb();
+
+       struct kr_cdb_opts opts = {
+               .is_cache = false,
+               .path = "ruledb", // under current workdir
+               // FIXME: the file will be sparse, but we still need to choose its size somehow.
+               // Later we might improve it to auto-resize in case of running out of space.
+               // Caveat: mdb_env_set_mapsize() can only be called without transactions open.
+               .maxsize = 10 * 1024*(size_t)1024,
+       };
+       int ret = the_rules->api->open(&the_rules->db, &the_rules->stats, &opts, NULL);
+       /* No persistence - we always refill from config for now.
+        * LATER:
+        *  - Make it include versioning?
+        *  - "\0stamp" key when loading config(s)?
+        *  - Don't clear ruleset data that doesn't come directly from config;
+        *    and add marks for that, etc.
+        *    (after there actually are any kinds of rules like that)
+        */
+       if (ret == 0) ret = ruledb_op(clear);
+       if (ret != 0) goto failure;
+       kr_require(the_rules->db);
+
+       ret = rules_defaults_insert();
+       if (ret != 0) goto failure;
+
+       /* Activate one default ruleset. */
+       uint8_t key_rs[] = "\0rulesets";
+       knot_db_val_t key = { .data = key_rs, .len = sizeof(key_rs) };
+       knot_db_val_t rulesets = { .data = &RULESET_DEFAULT, .len = strlen(RULESET_DEFAULT) + 1 };
+       ret = ruledb_op(write, &key, &rulesets, 1);
+       if (ret == 0) ret = ruledb_op(commit);
+       if (ret == 0) return kr_ok();
+failure:
+       free(the_rules);
+       the_rules = NULL;
+       return ret;
+}
+
+void kr_rules_deinit(void)
+{
+       if (!the_rules) return;
+       ruledb_op(close);
+       free(the_rules);
+       the_rules = NULL;
+}
+
+static bool kr_rule_consume_tags(knot_db_val_t *val, const struct kr_request *req)
+{
+       const size_t tl = sizeof(kr_rule_tags_t);
+       if (kr_fails_assert(val->len >= tl)) {
+               val->len = 0;
+               /* We may not fail immediately, but further processing
+                * will fail anyway due to zero remaining length. */
+               return false;
+       }
+       kr_rule_tags_t tags;
+       memcpy(&tags, val->data, tl);
+       val->data += tl;
+       val->len  -= tl;
+       return tags == KR_RULE_TAGS_ALL || (tags & req->rule_tags);
+}
+
+
+
+
+
+
+/** When constructing a key, it's convenient that the dname_lf ends on a fixed offset.
+ * Convention: the end here is before the final '\0' byte (if any). */
+#define KEY_DNAME_END_OFFSET (KEY_RULESET_MAXLEN + KNOT_DNAME_MAXLEN)
+#define KEY_MAXLEN (KEY_DNAME_END_OFFSET + 64) //TODO: most of 64 is unused ATM
+
+/** Add name lookup format on the fixed end-position inside key_data.
+ *
+ * Note: key_data[KEY_DNAME_END_OFFSET] = '\0' even though
+ * not always used as a part of the key. */
+static inline uint8_t * key_dname_lf(const knot_dname_t *name, uint8_t *key_data)
+{
+       return knot_dname_lf(name, key_data + KEY_RULESET_MAXLEN + 1)
+               + 1/*drop length*/;
+}
+
+/** Return length of the common prefix of two strings (knot_db_val_t). */
+static size_t key_common_prefix(knot_db_val_t k1, knot_db_val_t k2)
+{
+       const size_t len = MIN(k1.len, k2.len);
+       const uint8_t *data1 = k1.data, *data2 = k2.data;
+       kr_require(len == 0 || (data1 && data2));
+       for (ssize_t i = 0; i < len; ++i) {
+               if (data1[i] != data2[i])
+                       return i;
+       }
+       return len;
+}
+
+/** Find common "subtree" of two strings that both end in a dname_lf ('\0' terminator excluded).
+ *
+ * Note: return value < lf_start can happen - mismatch happened before LF.
+ * Function reviewed thoroughly, including the dependency.
+ */
+static size_t key_common_subtree(knot_db_val_t k1, knot_db_val_t k2, size_t lf_start_i)
+{
+       ssize_t i = key_common_prefix(k1, k2);
+       const char *data1 = k1.data, *data2 = k2.data;
+       // beware: '\0' at the end is excluded, so we need to handle ends separately
+       if (i == 0
+               || (i == k1.len && i == k2.len)
+               || (i == k1.len && data2[i] == '\0')
+               || (i == k2.len && data1[i] == '\0')) {
+                       return i;
+               }
+       do {
+               --i;
+               if (i < lf_start_i)
+                       return i;
+               if (data2[i] == '\0')
+                       return i;
+       } while (true);
+}
+
+int kr_rule_local_data_answer(struct kr_query *qry, knot_pkt_t *pkt)
+{
+       // TODO: implement EDE codes somehow
+
+       const uint16_t rrtype = qry->stype;
+
+       // LATER(optim.): we might cache the ruleset list a bit
+       uint8_t key_rs[] = "\0rulesets";
+       knot_db_val_t rulesets = { NULL, 0 };
+       int ret;
+       {
+               knot_db_val_t key = { .data = key_rs, .len = sizeof(key_rs) };
+               ret = ruledb_op(read, &key, &rulesets, 1);
+       }
+       if (ret != 0) return ret; /* including ENOENT: no rulesets -> no rule used */
+       const char *rulesets_str = rulesets.data;
+
+       uint8_t key_data[KEY_MAXLEN];
+       knot_db_val_t key;
+       key.data = key_dname_lf(qry->sname, key_data);
+       key_data[KEY_DNAME_END_OFFSET + 1] = '\0'; // double zero
+
+       key.data -= sizeof(KEY_EXACT_MATCH);
+       uint8_t * const key_data_ruleset_end = key.data;
+
+       /* Iterate over all rulesets. */
+       while (rulesets.len > 0) {
+               { /* Write ruleset-specific prefix of the key. */
+                       const size_t rsp_len = strnlen(rulesets_str, rulesets.len);
+                       kr_require(rsp_len <= KEY_RULESET_MAXLEN - 1);
+                       key.data -= rsp_len;
+                       memcpy(key.data, rulesets_str, rsp_len);
+                       rulesets_str += rsp_len + 1;
+                       rulesets.len -= rsp_len + 1;
+               }
+
+               /* Probe for exact and CNAME rule. */
+               memcpy(key_data_ruleset_end, &KEY_EXACT_MATCH, sizeof(KEY_EXACT_MATCH));
+               key.len = key_data + KEY_DNAME_END_OFFSET + 2 + sizeof(rrtype)
+                       - (uint8_t *)key.data;
+               const uint16_t types[] = { rrtype, KNOT_RRTYPE_CNAME };
+               const bool want_CNAME = rrtype != KNOT_RRTYPE_CNAME
+                                       && rrtype != KNOT_RRTYPE_DS;
+               for (int i = 0; i < 1 + want_CNAME; ++i) {
+                       memcpy(key_data + KEY_DNAME_END_OFFSET + 2, &types[i], sizeof(rrtype));
+                       knot_db_val_t val;
+                       // LATER: use cursor to iterate over multiple rules on the same key,
+                       // testing tags on each
+                       ret = ruledb_op(read, &key, &val, 1);
+                       switch (ret) {
+                               case -ENOENT: continue;
+                               case 0: break;
+                               default: return ret;
+                       }
+                       if (!kr_rule_consume_tags(&val, qry->request)) continue;
+
+                       /* We found a rule that applies to the dname+rrtype+req. */
+                       return answer_exact_match(qry, pkt, types[i],
+                                                       val.data, val.data + val.len);
+               }
+
+               /* Find the closest zone-like apex that applies.
+                * Now the key needs one byte change and a little truncation
+                * (we may truncate repeatedly). */
+               static_assert(sizeof(KEY_ZONELIKE_A) == sizeof(KEY_EXACT_MATCH),
+                               "bad combination of constants");
+               memcpy(key_data_ruleset_end, &KEY_ZONELIKE_A, sizeof(KEY_ZONELIKE_A));
+               key.len = key_data + KEY_DNAME_END_OFFSET - (uint8_t *)key.data;
+               const size_t lf_start_i = key_data_ruleset_end + sizeof(KEY_ZONELIKE_A)
+                                       - (const uint8_t *)key.data;
+               kr_require(lf_start_i < KEY_MAXLEN);
+               knot_db_val_t key_leq = key;
+               knot_db_val_t val;
+               if (rrtype == KNOT_RRTYPE_DS)
+                       goto shorten; // parent-side type, belongs into zone closer to root
+               // LATER: again, use cursor to iterate over multiple rules on the same key.
+               do {
+                       ret = ruledb_op(read_leq, &key_leq, &val);
+                       if (ret == -ENOENT) break;
+                       if (ret < 0) return kr_error(ret);
+                       if (ret > 0) { // found a previous key
+                               size_t cs_len = key_common_subtree(key, key_leq, lf_start_i);
+                               if (cs_len < lf_start_i) // no suitable key can exist in DB
+                                       break;
+                               if (cs_len < key_leq.len) { // retry at the common subtree
+                                       key_leq.len = cs_len;
+                                       continue;
+                               }
+                               kr_assert(cs_len == key_leq.len);
+                       }
+                       const knot_db_val_t zla_lf = {
+                               .data = key_leq.data + lf_start_i,
+                               .len  = key_leq.len  - lf_start_i,
+                       };
+                       /* Found some good key, now check tags. */
+                       if (!kr_rule_consume_tags(&val, qry->request)) {
+                               kr_assert(key_leq.len >= lf_start_i);
+                       shorten:
+                               /* Shorten key_leq by one label and retry. */
+                               if (key_leq.len <= lf_start_i) // nowhere to shorten
+                                       break;
+                               const char *data = key_leq.data;
+                               while (key_leq.len > lf_start_i && data[--key_leq.len] != '\0') ;
+                               continue;
+                       }
+                       /* Tags OK; execute the rule. */
+                       val_zla_type_t ztype;
+                       if (val.len < sizeof(ztype))
+                               return kr_error(EILSEQ);
+                       memcpy(&ztype, val.data, sizeof(ztype));
+                       ++val.data; --val.len;
+                       switch (ztype) {
+                       case VAL_ZLAT_EMPTY:
+                               return answer_zla_empty(qry, pkt, zla_lf, val);
+                       default:
+                               return kr_error(EILSEQ);
+                       }
+               } while (true);
+       }
+
+       return kr_error(ENOENT);
+}
+
+#define CHECK_RET(ret) do { \
+       if ((ret) < 0) { kr_assert(false); return kr_error((ret)); } \
+} while (false)
+
+static int answer_exact_match(struct kr_query *qry, knot_pkt_t *pkt, uint16_t type,
+               const uint8_t *data, const uint8_t *data_bound)
+{
+       /* Extract ttl from data. */
+       uint32_t ttl;
+       if (kr_fails_assert(data + sizeof(ttl) <= data_bound))
+               return kr_error(EILSEQ);
+       memcpy(&ttl, data, sizeof(ttl));
+       data += sizeof(ttl);
+
+       /* Start constructing the (pseudo-)packet. */
+       int ret = pkt_renew(pkt, qry->sname, qry->stype);
+       CHECK_RET(ret);
+       struct answer_rrset arrset;
+       memset(&arrset, 0, sizeof(arrset));
+
+       /* Materialize the base RRset.
+        * Error handling: we assume it's OK to leak a bit memory from pkt->mm. */
+       arrset.set.rr = knot_rrset_new(qry->sname, type, KNOT_CLASS_IN, ttl, &pkt->mm);
+       if (kr_fails_assert(arrset.set.rr))
+               return kr_error(ENOMEM);
+       ret = rdataset_materialize(&arrset.set.rr->rrs, data, data_bound, &pkt->mm);
+       CHECK_RET(ret);
+       const size_t data_off = ret;
+       arrset.set.rank = KR_RANK_SECURE | KR_RANK_AUTH; // local data has high trust
+       arrset.set.expiring = false;
+       /* Materialize the RRSIG RRset for the answer in (pseudo-)packet.
+        * (There will almost never be any RRSIG.) */
+       ret = rdataset_materialize(&arrset.sig_rds, data + data_off, data_bound, &pkt->mm);
+       CHECK_RET(ret);
+
+       /* Sanity check: we consumed exactly all data. */
+       const int unused_bytes = data_bound - data - data_off - ret;
+       if (kr_fails_assert(unused_bytes == 0)) {
+               kr_log_error(RULES, "ERROR: unused bytes: %d\n", unused_bytes);
+               return kr_error(EILSEQ);
+       }
+
+       /* Put links to the materialized data into the pkt. */
+       ret = pkt_append(pkt, &arrset);
+       CHECK_RET(ret);
+
+       /* Finishing touches. */
+       qry->flags.EXPIRING = false;
+       qry->flags.CACHED = true;
+       qry->flags.NO_MINIMIZE = true;
+
+       VERBOSE_MSG(qry, "=> satisfied by local data (positive)\n");
+       return kr_ok();
+}
+
+int kr_rule_local_data_ins(const knot_rrset_t *rrs, const knot_rdataset_t *sig_rds,
+                               kr_rule_tags_t tags)
+{
+       uint8_t key_data[KEY_MAXLEN];
+       knot_db_val_t key;
+       key.data = key_dname_lf(rrs->owner, key_data);
+       key_data[KEY_DNAME_END_OFFSET + 1] = '\0'; // double zero
+
+       key.data -= sizeof(KEY_EXACT_MATCH);
+       memcpy(key.data, &KEY_EXACT_MATCH, sizeof(KEY_EXACT_MATCH));
+
+       const size_t rsp_len = strlen(RULESET_DEFAULT);
+       key.data -= rsp_len;
+       memcpy(key.data, RULESET_DEFAULT, rsp_len);
+
+       memcpy(key_data + KEY_DNAME_END_OFFSET + 2, &rrs->type, sizeof(rrs->type));
+       key.len = key_data + KEY_DNAME_END_OFFSET + 2 + sizeof(rrs->type)
+               - (uint8_t *)key.data;
+
+       const int rr_ssize = rdataset_dematerialize_size(&rrs->rrs);
+       const int to_alloc = sizeof(tags) + sizeof(rrs->ttl) + rr_ssize
+                       + rdataset_dematerialize_size(sig_rds);
+       knot_db_val_t val = { .data = NULL, .len = to_alloc };
+       int ret = ruledb_op(write, &key, &val, 1);
+       CHECK_RET(ret);
+
+       memcpy(val.data, &tags, sizeof(tags));
+       val.data += sizeof(tags);
+       memcpy(val.data, &rrs->ttl, sizeof(rrs->ttl));
+       val.data += sizeof(rrs->ttl);
+       rdataset_dematerialize(&rrs->rrs, val.data);
+       val.data += rr_ssize;
+       rdataset_dematerialize(sig_rds, val.data);
+
+       return kr_ok();
+}
+
+
+static int answer_zla_empty(struct kr_query *qry, knot_pkt_t *pkt,
+                               const knot_db_val_t zla_lf, const knot_db_val_t val)
+{
+       if (kr_fails_assert(val.len == 0)) {
+               kr_log_error(RULES, "ERROR: unused bytes: %zu\n", val.len);
+               return kr_error(EILSEQ);
+       }
+
+       knot_dname_t apex_name[KNOT_DNAME_MAXLEN];
+       int ret = knot_dname_lf2wire(apex_name, zla_lf.len, zla_lf.data);
+       CHECK_RET(ret);
+
+       /* Start constructing the (pseudo-)packet. */
+       ret = pkt_renew(pkt, qry->sname, qry->stype);
+       CHECK_RET(ret);
+       struct answer_rrset arrset;
+       memset(&arrset, 0, sizeof(arrset));
+
+       /* Construct SOA or NS data (hardcoded content).  The SOA content is
+        * as recommended except for using a fixed mname (for simplicity):
+               https://tools.ietf.org/html/rfc6303#section-3
+        */
+       static const uint8_t soa_rdata[] = "\x09localhost\0\6nobody\7invalid\0"
+               "\0\0\0\1\0\0\x0e\x10\0\0\4\xb0\0\x09\x3a\x80\0\0\x2a\x30";
+       const bool name_matches = knot_dname_is_equal(qry->sname, apex_name);
+       const bool want_NS = name_matches && qry->stype == KNOT_RRTYPE_NS;
+       arrset.set.rr = knot_rrset_new(apex_name, want_NS ? KNOT_RRTYPE_NS : KNOT_RRTYPE_SOA,
+                                       KNOT_CLASS_IN, RULE_TTL_DEFAULT, &pkt->mm);
+       if (kr_fails_assert(arrset.set.rr))
+               return kr_error(ENOMEM);
+       if (want_NS) {
+               kr_require(zla_lf.len + 2 == knot_dname_size(apex_name));
+               ret = knot_rrset_add_rdata(arrset.set.rr, apex_name, zla_lf.len + 2, &pkt->mm);
+       } else {
+               ret = knot_rrset_add_rdata(arrset.set.rr, soa_rdata,
+                                               sizeof(soa_rdata) - 1, &pkt->mm);
+       }
+       CHECK_RET(ret);
+       arrset.set.rank = KR_RANK_SECURE | KR_RANK_AUTH; // local data has high trust
+       arrset.set.expiring = false;
+
+       /* Small differences if we exactly hit the name or even type. */
+       if (name_matches) {
+               knot_wire_set_rcode(pkt->wire, KNOT_RCODE_NOERROR);
+       } else {
+               knot_wire_set_rcode(pkt->wire, KNOT_RCODE_NXDOMAIN);
+       }
+       if (want_NS || (name_matches && qry->stype == KNOT_RRTYPE_SOA)) {
+               ret = knot_pkt_begin(pkt, KNOT_ANSWER);
+       } else {
+               ret = knot_pkt_begin(pkt, KNOT_AUTHORITY);
+       }
+       CHECK_RET(ret);
+
+       /* Put links to the RR into the pkt. */
+       ret = pkt_append(pkt, &arrset);
+       CHECK_RET(ret);
+
+       /* Finishing touches. */
+       qry->flags.EXPIRING = false;
+       qry->flags.CACHED = true;
+       qry->flags.NO_MINIMIZE = true;
+
+       VERBOSE_MSG(qry, "=> satisfied by local data (empty zone)\n");
+       return kr_ok();
+}
+
+int kr_rule_local_data_emptyzone(const knot_dname_t *apex, kr_rule_tags_t tags)
+{
+       uint8_t key_data[KEY_MAXLEN];
+       knot_db_val_t key;
+       key.data = key_dname_lf(apex, key_data);
+
+       key.data -= sizeof(KEY_ZONELIKE_A);
+       memcpy(key.data, &KEY_ZONELIKE_A, sizeof(KEY_ZONELIKE_A));
+
+       const size_t rsp_len = strlen(RULESET_DEFAULT);
+       key.data -= rsp_len;
+       memcpy(key.data, RULESET_DEFAULT, rsp_len);
+       key.len = key_data + KEY_DNAME_END_OFFSET - (uint8_t *)key.data;
+
+       val_zla_type_t ztype = VAL_ZLAT_EMPTY;
+       knot_db_val_t val = {
+               .data = NULL,
+               .len = sizeof(tags) + sizeof(ztype),
+       };
+       int ret = ruledb_op(write, &key, &val, 1);
+       CHECK_RET(ret);
+       memcpy(val.data, &tags, sizeof(tags));
+       val.data += sizeof(tags);
+       memcpy(val.data, &ztype, sizeof(ztype));
+       val.data += sizeof(ztype);
+       return ret;
+}
+
diff --git a/lib/rules/api.h b/lib/rules/api.h
new file mode 100644 (file)
index 0000000..4173d75
--- /dev/null
@@ -0,0 +1,37 @@
+/*  Copyright (C) CZ.NIC, z.s.p.o. <knot-resolver@labs.nic.cz>
+ *  SPDX-License-Identifier: GPL-3.0-or-later
+ */
+#pragma once
+
+#include "lib/defines.h"
+struct kr_query;
+struct knot_pkt;
+
+typedef uint64_t kr_rule_tags_t;
+#define KR_RULE_TAGS_ALL ((kr_rule_tags_t)0)
+
+KR_EXPORT
+int kr_rules_init(void);
+
+KR_EXPORT
+void kr_rules_deinit(void);
+
+/** Try answering the query from local data.
+ *
+ * FIXME: we probably want to ensure AA flags in answer as appropriate.
+ *   Perhaps approach it like AD?  Tweak flags in ranked_rr_array_entry
+ *   and at the end decide whether to set AA=1?
+ */
+int kr_rule_local_data_answer(struct kr_query *qry, struct knot_pkt *pkt);
+
+/** Insert/overwrite a local data rule.
+ * Into the default rule-set ATM. */
+KR_EXPORT
+int kr_rule_local_data_ins(const knot_rrset_t *rrs, const knot_rdataset_t *sig_rds,
+                               kr_rule_tags_t tags);
+
+/** Insert an empty zone.
+ * Into the default rule-set ATM. */
+KR_EXPORT
+int kr_rule_local_data_emptyzone(const knot_dname_t *apex, kr_rule_tags_t tags);
+
diff --git a/lib/rules/defaults.c b/lib/rules/defaults.c
new file mode 100644 (file)
index 0000000..8c96b56
--- /dev/null
@@ -0,0 +1,173 @@
+/*  Copyright (C) CZ.NIC, z.s.p.o. <knot-resolver@labs.nic.cz>
+ *  SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include "lib/rules/impl.h"
+#include "lib/rules/api.h"
+#include "lib/utils.h"
+
+int rules_defaults_insert(void)
+{
+       static const char * names[] = {
+               /* RFC1918 Private, local, broadcast, test and special zones
+                  Considerations: RFC6761, sec 6.1.
+                  https://www.iana.org/assignments/locally-served-dns-zones
+                */
+               /* RFC6303 */
+               "10.in-addr.arpa.",
+               "16.172.in-addr.arpa.",
+               "17.172.in-addr.arpa.",
+               "18.172.in-addr.arpa.",
+               "19.172.in-addr.arpa.",
+               "20.172.in-addr.arpa.",
+               "21.172.in-addr.arpa.",
+               "22.172.in-addr.arpa.",
+               "23.172.in-addr.arpa.",
+               "24.172.in-addr.arpa.",
+               "25.172.in-addr.arpa.",
+               "26.172.in-addr.arpa.",
+               "27.172.in-addr.arpa.",
+               "28.172.in-addr.arpa.",
+               "29.172.in-addr.arpa.",
+               "30.172.in-addr.arpa.",
+               "31.172.in-addr.arpa.",
+               "168.192.in-addr.arpa.",
+               "0.in-addr.arpa.",
+               "127.in-addr.arpa.",
+               "254.169.in-addr.arpa.",
+               "2.0.192.in-addr.arpa.",
+               "100.51.198.in-addr.arpa.",
+               "113.0.203.in-addr.arpa.",
+               "255.255.255.255.in-addr.arpa.",
+               /* RFC7793 */
+               "64.100.in-addr.arpa.",
+               "65.100.in-addr.arpa.",
+               "66.100.in-addr.arpa.",
+               "67.100.in-addr.arpa.",
+               "68.100.in-addr.arpa.",
+               "69.100.in-addr.arpa.",
+               "70.100.in-addr.arpa.",
+               "71.100.in-addr.arpa.",
+               "72.100.in-addr.arpa.",
+               "73.100.in-addr.arpa.",
+               "74.100.in-addr.arpa.",
+               "75.100.in-addr.arpa.",
+               "76.100.in-addr.arpa.",
+               "77.100.in-addr.arpa.",
+               "78.100.in-addr.arpa.",
+               "79.100.in-addr.arpa.",
+               "80.100.in-addr.arpa.",
+               "81.100.in-addr.arpa.",
+               "82.100.in-addr.arpa.",
+               "83.100.in-addr.arpa.",
+               "84.100.in-addr.arpa.",
+               "85.100.in-addr.arpa.",
+               "86.100.in-addr.arpa.",
+               "87.100.in-addr.arpa.",
+               "88.100.in-addr.arpa.",
+               "89.100.in-addr.arpa.",
+               "90.100.in-addr.arpa.",
+               "91.100.in-addr.arpa.",
+               "92.100.in-addr.arpa.",
+               "93.100.in-addr.arpa.",
+               "94.100.in-addr.arpa.",
+               "95.100.in-addr.arpa.",
+               "96.100.in-addr.arpa.",
+               "97.100.in-addr.arpa.",
+               "98.100.in-addr.arpa.",
+               "99.100.in-addr.arpa.",
+               "100.100.in-addr.arpa.",
+               "101.100.in-addr.arpa.",
+               "102.100.in-addr.arpa.",
+               "103.100.in-addr.arpa.",
+               "104.100.in-addr.arpa.",
+               "105.100.in-addr.arpa.",
+               "106.100.in-addr.arpa.",
+               "107.100.in-addr.arpa.",
+               "108.100.in-addr.arpa.",
+               "109.100.in-addr.arpa.",
+               "110.100.in-addr.arpa.",
+               "111.100.in-addr.arpa.",
+               "112.100.in-addr.arpa.",
+               "113.100.in-addr.arpa.",
+               "114.100.in-addr.arpa.",
+               "115.100.in-addr.arpa.",
+               "116.100.in-addr.arpa.",
+               "117.100.in-addr.arpa.",
+               "118.100.in-addr.arpa.",
+               "119.100.in-addr.arpa.",
+               "120.100.in-addr.arpa.",
+               "121.100.in-addr.arpa.",
+               "122.100.in-addr.arpa.",
+               "123.100.in-addr.arpa.",
+               "124.100.in-addr.arpa.",
+               "125.100.in-addr.arpa.",
+               "126.100.in-addr.arpa.",
+               "127.100.in-addr.arpa.",
+               /* RFC6303 */
+               "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.",
+               "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.",
+                       /* ^ below we inject exact-match PTR over this empty zone */
+               "d.f.ip6.arpa.",
+               "8.e.f.ip6.arpa.",
+               "9.e.f.ip6.arpa.",
+               "a.e.f.ip6.arpa.",
+               "b.e.f.ip6.arpa.",
+               "8.b.d.0.1.0.0.2.ip6.arpa.",
+               /* RFC8375 */
+               "home.arpa.",
+
+               /* More zones - empty-zone subset from:
+                  https://www.iana.org/assignments/special-use-domain-names
+                  TODO: perhaps review the list again.
+                */
+               "test.",
+               "onion.",
+               "invalid.",
+               "local.", // RFC 8375.4
+       };
+
+       const int names_count = sizeof(names) / sizeof(names[0]);
+       for (int i = 0; i < names_count; ++i) {
+               knot_dname_t name_buf[KNOT_DNAME_MAXLEN];
+               const knot_dname_t *dname =
+                       knot_dname_from_str(name_buf, names[i], sizeof(name_buf));
+               int ret = kr_rule_local_data_emptyzone(dname, KR_RULE_TAGS_ALL);
+               if (kr_fails_assert(!ret))
+                       return kr_error(ret);
+               /* The double conversion is perhaps a bit wasteful, but it should be rare. */
+               /* LATER: add extra info with explanation?  policy module had an ADDITIONAL
+                * record with explanation, but perhaps extended errors are more suitable?
+                * Differentiating the message - perhaps splitting VAL_ZLAT_EMPTY into a few?
+                */
+       }
+
+       { // reverse localhost; LATER: the situation isn't ideal with NXDOMAIN + some exact matches
+               knot_dname_t name_buf[KNOT_DNAME_MAXLEN];
+               knot_rrset_t rr = {
+                       .owner = knot_dname_from_str(name_buf,
+                               "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.",
+                               sizeof(name_buf)),
+                       .ttl = RULE_TTL_DEFAULT,
+                       .type = KNOT_RRTYPE_PTR,
+                       .rclass = KNOT_CLASS_IN,
+                       .rrs = { 0 },
+                       .additional = NULL,
+               };
+               int ret = knot_rrset_add_rdata(&rr, (const knot_dname_t *)"\x09localhost\0",
+                                               1+9+1, NULL);
+               if (!ret) ret = kr_rule_local_data_ins(&rr, NULL, KR_RULE_TAGS_ALL);
+
+               rr.owner = knot_dname_from_str(name_buf,
+                               "1.0.0.127.in-addr.arpa.",
+                               sizeof(name_buf));
+               if (!ret) ret = kr_rule_local_data_ins(&rr, NULL, KR_RULE_TAGS_ALL);
+
+               knot_rdataset_clear(&rr.rrs, NULL);
+               if (kr_fails_assert(!ret))
+                       return kr_error(ret);
+       }
+
+       return kr_ok();
+}
+
diff --git a/lib/rules/impl.h b/lib/rules/impl.h
new file mode 100644 (file)
index 0000000..3c4f636
--- /dev/null
@@ -0,0 +1,10 @@
+/*  Copyright (C) CZ.NIC, z.s.p.o. <knot-resolver@labs.nic.cz>
+ *  SPDX-License-Identifier: GPL-3.0-or-later
+ */
+#pragma once
+
+#define RULE_TTL_DEFAULT ((uint16_t)10800)
+
+/** Insert all the default rules. in ./defaults.c */
+int rules_defaults_insert(void);
+
index 34c08b9ff2ae33115870492fc38ebcaadbd55926..a8746a1415aef51a0e10b72bd7e3ab89de7449c7 100644 (file)
@@ -20,6 +20,7 @@
 #include "lib/zonecut.h"
 #include "lib/module.h"
 #include "lib/layer.h"
+#include "lib/rules/api.h"
 
 #include <inttypes.h>
 #include <math.h>
@@ -228,6 +229,22 @@ static const knot_dname_t * addr2reverse(const char *addr)
                                kr_inaddr_family(&ia.ip));
 }
 
+static int add_pair_root(struct kr_zonecut *hints, const char *name, const char *addr)
+{
+       /* Build key */
+       knot_dname_t key[KNOT_DNAME_MAXLEN];
+       if (!knot_dname_from_str(key, name, sizeof(key))) {
+               return kr_error(EINVAL);
+       }
+       knot_dname_to_lower(key);
+
+       union kr_sockaddr ia;
+       if (parse_addr_str(&ia, addr) != 0) {
+               return kr_error(EINVAL);
+       }
+       return kr_zonecut_add(hints, key, kr_inaddr(&ia.ip), kr_inaddr_len(&ia.ip));
+}
+
 static int add_pair(struct kr_zonecut *hints, const char *name, const char *addr)
 {
        /* Build key */
@@ -242,7 +259,20 @@ static int add_pair(struct kr_zonecut *hints, const char *name, const char *addr
                return kr_error(EINVAL);
        }
 
-       return kr_zonecut_add(hints, key, kr_inaddr(&ia.ip), kr_inaddr_len(&ia.ip));
+       uint16_t rrtype = ia.ip.sa_family == AF_INET6 ? KNOT_RRTYPE_AAAA : KNOT_RRTYPE_A;
+       knot_rrset_t rrs;
+       knot_rrset_init(&rrs, key, rrtype, KNOT_CLASS_IN, HINTS_TTL_DEFAULT/*FIXME*/);
+       int ret;
+       if (ia.ip.sa_family == AF_INET6) {
+               ret = knot_rrset_add_rdata(&rrs, (const uint8_t *)&ia.ip6.sin6_addr, 16, NULL);
+       } else {
+               ret = knot_rrset_add_rdata(&rrs, (const uint8_t *)&ia.ip4.sin_addr, 4, NULL);
+       }
+       if (ret == KNOT_EOK) {
+               ret = kr_rule_local_data_ins(&rrs, NULL, KR_RULE_TAGS_ALL);
+       }
+       knot_rdataset_clear(&rrs.rrs, NULL);
+       return ret;
 }
 
 static int add_reverse_pair(struct kr_zonecut *hints, const char *name, const char *addr)
@@ -515,8 +545,12 @@ static void unpack_hint(struct kr_zonecut *root_hints, JsonNode *table, const ch
        JsonNode *node = NULL;
        json_foreach(node, table) {
                switch(node->tag) {
-               case JSON_STRING: add_pair(root_hints, name ? name : node->key, node->string_); break;
-               case JSON_ARRAY: unpack_hint(root_hints, node, name ? name : node->key); break;
+               case JSON_STRING:
+                       add_pair_root(root_hints, name ? name : node->key, node->string_);
+                       break;
+               case JSON_ARRAY:
+                       unpack_hint(root_hints, node, name ? name : node->key);
+                       break;
                default: continue;
                }
        }
index 47e436f027616100d62ec6c1515cdb09beebaa94..4715b007fca58416e6a3c152a8c92629b0775f87 100644 (file)
@@ -298,62 +298,6 @@ local function localhost(_, req)
        return kres.DONE
 end
 
-local dname_rev4_localhost = todname('1.0.0.127.in-addr.arpa');
-local dname_rev4_localhost_apex = todname('127.in-addr.arpa');
-
--- Rule for reverse localhost.
--- Answer with locally served minimal 127.in-addr.arpa domain, only having
--- a PTR record in 1.0.0.127.in-addr.arpa, and with 1.0...0.ip6.arpa. zone.
--- TODO: much of this would better be left to the hints module (or coordinated).
-local function localhost_reversed(_, req)
-       local qry = req:current()
-       local answer = req:ensure_answer()
-       if answer == nil then return nil end
-
-       -- classify qry.sname:
-       local is_exact   -- exact dname for localhost
-       local is_apex    -- apex of a locally-served localhost zone
-       local is_nonterm -- empty non-terminal name
-       if ffi.C.knot_dname_in_bailiwick(qry.sname, todname('ip6.arpa.')) > 0 then
-               -- exact ::1 query (relying on the calling rule)
-               is_exact = true
-               is_apex = true
-       else
-               -- within 127.in-addr.arpa.
-               local labels = ffi.C.knot_dname_labels(qry.sname, nil)
-               if labels == 3 then
-                       is_exact = false
-                       is_apex = true
-               elseif labels == 4+2 and ffi.C.knot_dname_is_equal(
-                                       qry.sname, dname_rev4_localhost) then
-                       is_exact = true
-               else
-                       is_exact = false
-                       is_apex = false
-                       is_nonterm = ffi.C.knot_dname_in_bailiwick(dname_rev4_localhost, qry.sname) > 0
-               end
-       end
-
-       ffi.C.kr_pkt_make_auth_header(answer)
-       answer:rcode(kres.rcode.NOERROR)
-       answer:begin(kres.section.ANSWER)
-       if is_exact and qry.stype == kres.type.PTR then
-               answer:put(qry.sname, 900, answer:qclass(), kres.type.PTR, dname_localhost)
-       elseif is_apex and qry.stype == kres.type.SOA then
-               mkauth_soa(answer, dname_rev4_localhost_apex, dname_localhost)
-       elseif is_apex and qry.stype == kres.type.NS then
-               answer:put(dname_rev4_localhost_apex, 900, answer:qclass(), kres.type.NS,
-                       dname_localhost)
-       else
-               if not is_nonterm then
-                       answer:rcode(kres.rcode.NXDOMAIN)
-               end
-               answer:begin(kres.section.AUTHORITY)
-               mkauth_soa(answer, dname_rev4_localhost_apex, dname_localhost)
-       end
-       return kres.DONE
-end
-
 -- All requests
 function policy.all(action)
        return function(_, _) return action end
@@ -916,154 +860,15 @@ function policy.todnames(names)
        return names
 end
 
--- RFC1918 Private, local, broadcast, test and special zones
--- Considerations: RFC6761, sec 6.1.
--- https://www.iana.org/assignments/locally-served-dns-zones
-local private_zones = {
-       -- RFC6303
-       '10.in-addr.arpa.',
-       '16.172.in-addr.arpa.',
-       '17.172.in-addr.arpa.',
-       '18.172.in-addr.arpa.',
-       '19.172.in-addr.arpa.',
-       '20.172.in-addr.arpa.',
-       '21.172.in-addr.arpa.',
-       '22.172.in-addr.arpa.',
-       '23.172.in-addr.arpa.',
-       '24.172.in-addr.arpa.',
-       '25.172.in-addr.arpa.',
-       '26.172.in-addr.arpa.',
-       '27.172.in-addr.arpa.',
-       '28.172.in-addr.arpa.',
-       '29.172.in-addr.arpa.',
-       '30.172.in-addr.arpa.',
-       '31.172.in-addr.arpa.',
-       '168.192.in-addr.arpa.',
-       '0.in-addr.arpa.',
-       '254.169.in-addr.arpa.',
-       '2.0.192.in-addr.arpa.',
-       '100.51.198.in-addr.arpa.',
-       '113.0.203.in-addr.arpa.',
-       '255.255.255.255.in-addr.arpa.',
-       -- RFC7793
-       '64.100.in-addr.arpa.',
-       '65.100.in-addr.arpa.',
-       '66.100.in-addr.arpa.',
-       '67.100.in-addr.arpa.',
-       '68.100.in-addr.arpa.',
-       '69.100.in-addr.arpa.',
-       '70.100.in-addr.arpa.',
-       '71.100.in-addr.arpa.',
-       '72.100.in-addr.arpa.',
-       '73.100.in-addr.arpa.',
-       '74.100.in-addr.arpa.',
-       '75.100.in-addr.arpa.',
-       '76.100.in-addr.arpa.',
-       '77.100.in-addr.arpa.',
-       '78.100.in-addr.arpa.',
-       '79.100.in-addr.arpa.',
-       '80.100.in-addr.arpa.',
-       '81.100.in-addr.arpa.',
-       '82.100.in-addr.arpa.',
-       '83.100.in-addr.arpa.',
-       '84.100.in-addr.arpa.',
-       '85.100.in-addr.arpa.',
-       '86.100.in-addr.arpa.',
-       '87.100.in-addr.arpa.',
-       '88.100.in-addr.arpa.',
-       '89.100.in-addr.arpa.',
-       '90.100.in-addr.arpa.',
-       '91.100.in-addr.arpa.',
-       '92.100.in-addr.arpa.',
-       '93.100.in-addr.arpa.',
-       '94.100.in-addr.arpa.',
-       '95.100.in-addr.arpa.',
-       '96.100.in-addr.arpa.',
-       '97.100.in-addr.arpa.',
-       '98.100.in-addr.arpa.',
-       '99.100.in-addr.arpa.',
-       '100.100.in-addr.arpa.',
-       '101.100.in-addr.arpa.',
-       '102.100.in-addr.arpa.',
-       '103.100.in-addr.arpa.',
-       '104.100.in-addr.arpa.',
-       '105.100.in-addr.arpa.',
-       '106.100.in-addr.arpa.',
-       '107.100.in-addr.arpa.',
-       '108.100.in-addr.arpa.',
-       '109.100.in-addr.arpa.',
-       '110.100.in-addr.arpa.',
-       '111.100.in-addr.arpa.',
-       '112.100.in-addr.arpa.',
-       '113.100.in-addr.arpa.',
-       '114.100.in-addr.arpa.',
-       '115.100.in-addr.arpa.',
-       '116.100.in-addr.arpa.',
-       '117.100.in-addr.arpa.',
-       '118.100.in-addr.arpa.',
-       '119.100.in-addr.arpa.',
-       '120.100.in-addr.arpa.',
-       '121.100.in-addr.arpa.',
-       '122.100.in-addr.arpa.',
-       '123.100.in-addr.arpa.',
-       '124.100.in-addr.arpa.',
-       '125.100.in-addr.arpa.',
-       '126.100.in-addr.arpa.',
-       '127.100.in-addr.arpa.',
-
-       -- RFC6303
-       -- localhost_reversed handles ::1
-       '0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.',
-       'd.f.ip6.arpa.',
-       '8.e.f.ip6.arpa.',
-       '9.e.f.ip6.arpa.',
-       'a.e.f.ip6.arpa.',
-       'b.e.f.ip6.arpa.',
-       '8.b.d.0.1.0.0.2.ip6.arpa.',
-       -- RFC8375
-       'home.arpa.',
-}
-policy.todnames(private_zones)
-
 -- @var Default rules
 policy.rules = {}
 policy.postrules = {}
 policy.special_names = {
        -- XXX: beware of special_names_optim() when modifying these filters
-       {
-               cb=policy.suffix_common(policy.DENY_MSG(
-                       'Blocking is mandated by standards, see references on '
-                       .. 'https://www.iana.org/assignments/'
-                       .. 'locally-served-dns-zones/locally-served-dns-zones.xhtml',
-                       kres.extended_error.NOTSUP),
-                       private_zones, todname('arpa.')),
-               count=0
-       },
-       {
-               cb=policy.suffix(policy.DENY_MSG(
-                       'Blocking is mandated by standards, see references on '
-                       .. 'https://www.iana.org/assignments/'
-                       .. 'special-use-domain-names/special-use-domain-names.xhtml',
-                       kres.extended_error.NOTSUP),
-                       {
-                               todname('test.'),
-                               todname('onion.'),
-                               todname('invalid.'),
-                               todname('local.'), -- RFC 8375.4
-                       }),
-               count=0
-       },
        {
                cb=policy.suffix(localhost, {dname_localhost}),
                count=0
        },
-       {
-               cb=policy.suffix_common(localhost_reversed, {
-                       todname('127.in-addr.arpa.'),
-                       todname('1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.')},
-                       todname('arpa.')),
-               count=0
-       },
 }
 
 -- Return boolean; false = no special name may apply, true = some might apply.
index 4db7caaba32034c1e281e99e3c829a116e3cbb3c..872ce2e391139938a6e12ec6d2e0622f9a8ff638 100644 (file)
@@ -52,6 +52,7 @@ function reply_result(state, req)
        local result = check_stats(got)
        return result(state, req)
 end
+policy.add(policy.all(policy.FLAGS('PASSTHRU_LEGACY'))) -- the test isn't written with this in mind
 policy.add(policy.pattern(reply_result, 'stats.test.'))
 policy.add(policy.all(FWD_TARGET)) -- avoid iteration
 
index fc4a2fdbacd226b68527add07c6d9dacff8f49ca..76a2b5fac99498cd842ef868a32366c9f344ac35 100644 (file)
@@ -22,7 +22,11 @@ int kr_gc_cache_open(const char *cache_path, struct kr_cache *kres_db,
                return -ENOENT;
        }
 
-       struct kr_cdb_opts opts = { .path = cache_path, .maxsize = 0/*don't resize*/ };
+       struct kr_cdb_opts opts = {
+               .is_cache = true,
+               .path = cache_path,
+               .maxsize = 0,/*don't resize*/
+       };
 
        int ret = kr_cache_open(kres_db, NULL, &opts, NULL);
        if (ret || kres_db->db == NULL) {