]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
modules/hints: configurable root hints, part of module
authorMarek Vavruša <marek.vavrusa@nic.cz>
Sat, 13 Jun 2015 17:27:55 +0000 (19:27 +0200)
committerMarek Vavruša <marek.vavrusa@nic.cz>
Sat, 13 Jun 2015 17:27:55 +0000 (19:27 +0200)
before root hints were hardcoded to the resolver,
now they are present in form of a cut in the resolution
context, and the modules can add/remove/replace them
on the fly

daemon/engine.c
lib/resolve.c
lib/resolve.h
lib/zonecut.c
lib/zonecut.h
modules/hints/README.rst
modules/hints/hints.c
tests/test_zonecut.c

index 33699df99904fa142d69b70f8515d9ad23e2095b..c8dfb40d36f0e184072cd33d531e30f7360f0234 100644 (file)
@@ -203,6 +203,9 @@ static int init_resolver(struct engine *engine)
 {
        /* Open resolution context */
        engine->resolver.modules = &engine->modules;
+       /* Set default root hints */
+       kr_zonecut_init(&engine->resolver.root_hints, (const uint8_t *)"", engine->pool);
+       kr_zonecut_set_sbelt(&engine->resolver, &engine->resolver.root_hints);
        /* Open NS rtt + reputation cache */
        engine->resolver.cache_rtt = malloc(lru_size(kr_nsrep_lru_t, LRU_RTT_SIZE));
        if (engine->resolver.cache_rtt) {
@@ -292,6 +295,7 @@ void engine_deinit(struct engine *engine)
        }
 
        network_deinit(&engine->net);
+       kr_zonecut_deinit(&engine->resolver.root_hints);
        kr_cache_close(&engine->resolver.cache);
        lru_deinit(engine->resolver.cache_rtt);
        lru_deinit(engine->resolver.cache_rep);
index 0bc3fbd995017192ab8882deee7d73ce540e9ec2..74e9cf4e7c47a72c643900ac39b8612fd7e7e677 100644 (file)
@@ -54,7 +54,7 @@ static int ns_fetch_cut(struct kr_query *qry, struct kr_request *req)
        struct kr_cache_txn txn;
        int ret = 0;
        if (kr_cache_txn_begin(&req->ctx->cache, &txn, NAMEDB_RDONLY) != 0) {
-               ret = kr_zonecut_set_sbelt(&qry->zone_cut);
+               ret = kr_zonecut_set_sbelt(req->ctx, &qry->zone_cut);
        } else {
                /* If at/subdomain of parent zone cut, start from 'one up' to avoid loops */
                struct kr_query *parent = qry->parent;
index 3411f3fa050893e89cfeda67757c7a793b10d1c8..8a976229d1e38e0c69475ee821b35fae1b28af40 100644 (file)
@@ -104,6 +104,7 @@ typedef array_t(struct kr_module *) module_array_t;
 struct kr_context
 {
        mm_ctx_t *pool;
+       struct kr_zonecut root_hints;
        struct kr_cache cache;
        kr_nsrep_lru_t *cache_rtt;
        kr_nsrep_lru_t *cache_rep;
index 810dc96a305238dbe54ce39eb96ac51cd8e08959..5ea9f0f2cbd20237e2a8604208d654cee09a62aa 100644 (file)
@@ -64,7 +64,7 @@ static void update_cut_name(struct kr_zonecut *cut, const knot_dname_t *name)
 
 int kr_zonecut_init(struct kr_zonecut *cut, const knot_dname_t *name, mm_ctx_t *pool)
 {
-       if (cut == NULL || name == NULL) {
+       if (!cut || !name) {
                return kr_error(EINVAL);
        }
 
@@ -87,7 +87,7 @@ static int free_addr_set(const char *k, void *v, void *baton)
 
 void kr_zonecut_deinit(struct kr_zonecut *cut)
 {
-       if (cut == NULL) {
+       if (!cut) {
                return;
        }
        mm_free(cut->pool, cut->name);
@@ -97,16 +97,51 @@ void kr_zonecut_deinit(struct kr_zonecut *cut)
 
 void kr_zonecut_set(struct kr_zonecut *cut, const knot_dname_t *name)
 {
-       if (cut == NULL || name == NULL) {
+       if (!cut || !name) {
                return;
        }
        kr_zonecut_deinit(cut);
        kr_zonecut_init(cut, name, cut->pool);
 }
 
+static int copy_addr_set(const char *k, void *v, void *baton)
+{
+       pack_t *addr_set = v;
+       struct kr_zonecut *dst = baton;
+       /* Clone addr_set pack */
+       pack_t *new_set = mm_alloc(dst->pool, sizeof(*new_set));
+       if (!new_set) {
+               return kr_error(ENOMEM);
+       }
+       new_set->at = mm_alloc(dst->pool, addr_set->len);
+       if (!new_set->at) {
+               mm_free(dst->pool, new_set);
+               return kr_error(ENOMEM);
+       }
+       memcpy(new_set->at, addr_set->at, addr_set->len);
+       new_set->len = addr_set->len;
+       new_set->cap = new_set->len;
+       /* Reinsert */
+       if (map_set(&dst->nsset, k, new_set) != 0) {
+               pack_clear_mm(*new_set, mm_free, dst->pool);
+               mm_free(dst->pool, new_set);
+               return kr_error(ENOMEM);
+       }
+       return kr_ok();
+}
+
+int kr_zonecut_copy(struct kr_zonecut *dst, const struct kr_zonecut *src)
+{
+       if (!dst || !src) {
+               return kr_error(EINVAL);
+       }
+       /* We're not touching src nsset, I promise */
+       return map_walk((map_t *)&src->nsset, copy_addr_set, dst);
+}
+
 int kr_zonecut_add(struct kr_zonecut *cut, const knot_dname_t *ns, const knot_rdata_t *rdata)
 {
-       if (cut == NULL || ns == NULL) {
+       if (!cut || !ns) {
                return kr_error(EINVAL);
        }
        /* Fetch/insert nameserver. */
@@ -138,7 +173,7 @@ int kr_zonecut_add(struct kr_zonecut *cut, const knot_dname_t *ns, const knot_rd
 
 int kr_zonecut_del(struct kr_zonecut *cut, const knot_dname_t *ns, const knot_rdata_t *rdata)
 {
-       if (cut == NULL || ns == NULL) {
+       if (!cut || !ns) {
                return kr_error(EINVAL);
        }
 
@@ -163,7 +198,7 @@ int kr_zonecut_del(struct kr_zonecut *cut, const knot_dname_t *ns, const knot_rd
 
 pack_t *kr_zonecut_find(struct kr_zonecut *cut, const knot_dname_t *ns)
 {
-       if (cut == NULL || ns == NULL) {
+       if (!cut || !ns) {
                return NULL;
        }
 
@@ -172,13 +207,23 @@ pack_t *kr_zonecut_find(struct kr_zonecut *cut, const knot_dname_t *ns)
        return map_get(nsset, key);
 }
 
-int kr_zonecut_set_sbelt(struct kr_zonecut *cut)
+int kr_zonecut_set_sbelt(struct kr_context *ctx, struct kr_zonecut *cut)
 {
-       if (cut == NULL) {
+       if (!ctx || !cut) {
                return kr_error(EINVAL);
        }
 
        update_cut_name(cut, U8(""));
+
+       /* Copy root hints from resolution context. */
+       if (ctx->root_hints.nsset.root) {
+               int ret = kr_zonecut_copy(cut, &ctx->root_hints);
+               if (ret == 0) {
+                       return ret;
+               }
+       }
+
+       /* Copy compiled-in root hints */
        for (unsigned i = 0; i < HINT_COUNT; ++i) {
                const struct hint_info *hint = &SBELT[i];
                knot_rdata_t rdata[knot_rdata_array_size(HINT_ADDRLEN)];
@@ -239,7 +284,7 @@ static int fetch_ns(struct kr_context *ctx, struct kr_zonecut *cut, const knot_d
 
        /* Always keep SBELT as a backup for root */
        if (name[0] == '\0') {
-               kr_zonecut_set_sbelt(cut);
+               kr_zonecut_set_sbelt(ctx, cut);
        }
 
        return kr_ok();
@@ -269,5 +314,5 @@ int kr_zonecut_find_cached(struct kr_context *ctx, struct kr_zonecut *cut, const
        }
 
        /* Name server not found, start with SBELT. */
-       return kr_zonecut_set_sbelt(cut);
+       return kr_zonecut_set_sbelt(ctx, cut);
 }
index 990220aba43e714456307ff13e18817488300575..c9be6b327666526c4bc130598179908b23583428 100644 (file)
@@ -55,6 +55,14 @@ void kr_zonecut_deinit(struct kr_zonecut *cut);
  */
 void kr_zonecut_set(struct kr_zonecut *cut, const knot_dname_t *name);
 
+/** 
+ * Copy zone cut, including all data.
+ * @param dst destination zone cut
+ * @param src source zone cut
+ * @return 0 or an error code
+ */
+int kr_zonecut_copy(struct kr_zonecut *dst, const struct kr_zonecut *src);
+
 /**
  * Add address record to the zone cut.
  *
@@ -92,10 +100,11 @@ pack_t *kr_zonecut_find(struct kr_zonecut *cut, const knot_dname_t *ns);
 /**
  * Populate zone cut with a root zone using SBELT :rfc:`1034`
  *
+ * @param ctx resolution context (to fetch root hints)
  * @param cut zone cut to be populated
  * @return 0 or error code
  */
-int kr_zonecut_set_sbelt(struct kr_zonecut *cut);
+int kr_zonecut_set_sbelt(struct kr_context *ctx, struct kr_zonecut *cut);
 
 /**
  * Populate zone cut address set from cache.
index 0b83ced7e717c3222486517e219bda680d8d7706..e69bbc5ac850e4430dc76dffe4ba778dc3c7d1ac 100644 (file)
@@ -4,6 +4,8 @@ Static hints
 ------------
 
 This is a module providing static hints from ``/etc/hosts`` like file.
+You can also use it to change root hints that are used as a safety belt or if the root NS
+drops out of cache.
 
 Properties
 ^^^^^^^^^^
@@ -28,3 +30,34 @@ Properties
   :return: ``{ result: bool }``
 
   Set hostname - address pair hint.
+
+.. function:: hints.root()
+
+  :return: ``{ ['a.root-servers.net'] = { '1.2.3.4', '5.6.7.8', ...}, ... }``
+
+  .. tip:: If no parameters are passed, returns current root hints set.
+
+.. function:: hints.root(root_hints)
+
+  :param table root_hints: new set of root hints i.e. ``{['name'] = 'addr', ...}``
+  :return: ``{ ['a.root-servers.net'] = { '1.2.3.4', '5.6.7.8', ...}, ... }``
+
+  Replace current root hints and return the current table of root hints.
+
+  Example:
+
+  .. code-block:: lua
+
+       > hints.root({
+               ['l.root-servers.net.'] = '199.7.83.42',
+               ['m.root-servers.net.'] = '202.12.27.33'
+       })
+       [l.root-servers.net.] => {
+           [1] => 199.7.83.42
+       }
+       [m.root-servers.net.] => {
+           [1] => 202.12.27.33
+       }
+
+  .. tip:: A good rule of thumb is to select only a few fastest root hints. The server learns RTT and NS quality over time, and thus tries all servers available. You can help it by preselecting the candidates.
+  
\ No newline at end of file
index 8d42359480cf4dd34d8a317d50013f3399d71d1a..80f81fb2e80e92fa5511244483d40e835e814a86 100644 (file)
@@ -28,7 +28,7 @@
 #include <ccan/json/json.h>
 #include <ucw/mempool.h>
 
-#include "lib/layer/iterate.h"
+#include "daemon/engine.h"
 #include "lib/zonecut.h"
 #include "lib/module.h"
 #include "lib/layer.h"
@@ -222,6 +222,24 @@ static char* hint_set(void *env, struct kr_module *module, const char *args)
        return result;
 }
 
+/** @internal Pack address list into JSON array. */
+static JsonNode *pack_addrs(pack_t *pack)
+{
+       char buf[SOCKADDR_STRLEN];
+       JsonNode *root = json_mkarray();
+       uint8_t *addr = pack_head(*pack);
+       while (addr != pack_tail(*pack)) {
+               size_t len = pack_obj_len(addr);
+               int family = len == sizeof(struct in_addr) ? AF_INET : AF_INET6;
+               if (!inet_ntop(family, pack_obj_val(addr), buf, sizeof(buf))) {
+                       break;
+               }
+               json_append_element(root, json_mkstring(buf));
+               addr = pack_obj_next(addr);
+       }
+       return root;
+}
+
 /**
  * Retrieve address hint for given name.
  *
@@ -240,21 +258,59 @@ static char* hint_get(void *env, struct kr_module *module, const char *args)
                return NULL;
        }
 
-       char buf[SOCKADDR_STRLEN];
-       JsonNode *root = json_mkarray();
-       uint8_t *addr = pack_head(*pack);
-       while (addr != pack_tail(*pack)) {
-               size_t len = pack_obj_len(addr);
-               int family = len == sizeof(struct in_addr) ? AF_INET : AF_INET6;
-               if (!inet_ntop(family, pack_obj_val(addr), buf, sizeof(buf))) {
-                       break;
-               }
-               json_append_element(root, json_mkstring(buf));
-               addr = pack_obj_next(addr);
+       char *result = NULL;
+       JsonNode *root = pack_addrs(pack);
+       if (root) {
+               result = json_encode(root);
+               json_delete(root);
        }
+       return result;
+}
+
+/** Retrieve hint list. */
+static int pack_hint(const char *k, void *v, void *baton)
+{
+       char nsname_str[KNOT_DNAME_MAXLEN] = {'\0'};
+       knot_dname_to_str(nsname_str, (const uint8_t *)k, sizeof(nsname_str));
+       JsonNode *root_node = baton;
+       JsonNode *addr_list = pack_addrs((pack_t *)v);
+       if (!addr_list) {
+               return kr_error(ENOMEM);
+       }
+       json_append_member(root_node, nsname_str, addr_list);
+       return kr_ok();
+}
 
-       char *result = json_encode(root);
-       json_delete(root);
+/**
+ * Get/set root hints set.
+ *
+ * Input:  { name: [addr_list], ... }
+ * Output: current list
+ *
+ */
+static char* hint_root(void *env, struct kr_module *module, const char *args)
+{
+       struct engine *engine = env;
+       struct kr_context *ctx = &engine->resolver;
+       /* Replace root hints if parameter is set */
+       if (args && strlen(args) > 0) {
+               JsonNode *node = NULL;
+               JsonNode *root_node = json_decode(args);
+               kr_zonecut_set(&ctx->root_hints, (const uint8_t *)"");
+               json_foreach(node, root_node) {
+                       switch(node->tag) {
+                       case JSON_STRING: add_pair(&ctx->root_hints, node->key, node->string_); break;
+                       default: continue;
+                       }
+               }
+       }
+       /* Return current root hints */
+       char *result = NULL;
+       JsonNode *root_node = json_mkobject();
+       if (map_walk(&ctx->root_hints.nsset, pack_hint, root_node) == 0) {
+               result = json_encode(root_node);
+       }
+       json_delete(root_node);
        return result;
 }
 
@@ -298,6 +354,7 @@ struct kr_prop *hints_props(void)
        static struct kr_prop prop_list[] = {
            { &hint_set,    "set", "Set {name, address} hint.", },
            { &hint_get,    "get", "Retrieve hint for given name.", },
+           { &hint_root,   "root", "Replace root hints set (empty value to return current list).", },
            { NULL, NULL, NULL }
        };
        return prop_list;
index 24b9a8fec5408b9b9cf2f7ce0d637c06a6946395..202899c3a4341321802c34b74ccad07e6be2a763 100644 (file)
@@ -29,14 +29,34 @@ static void test_zonecut_params(void **state)
        assert_int_not_equal(kr_zonecut_add(NULL, NULL, NULL), 0);
        assert_null((void *)kr_zonecut_find(NULL, NULL));
        assert_null((void *)kr_zonecut_find(&cut, NULL));
-       assert_int_not_equal(kr_zonecut_set_sbelt(NULL), 0);
+       assert_int_not_equal(kr_zonecut_set_sbelt(NULL, NULL), 0);
        assert_int_not_equal(kr_zonecut_find_cached(NULL, NULL, NULL, NULL, 0), 0);
 }
 
+static void test_zonecut_copy(void **state)
+{
+       const knot_dname_t *root = (const uint8_t *)"";
+       struct kr_zonecut cut1, cut2;
+       kr_zonecut_init(&cut1, root, NULL);
+       kr_zonecut_init(&cut2, root, NULL);
+       /* Insert some values */
+       assert_int_equal(kr_zonecut_add(&cut1, (const uint8_t *)"dead", NULL), 0);
+       assert_int_equal(kr_zonecut_add(&cut1, (const uint8_t *)"beef", NULL), 0);
+       /* Copy */
+       assert_int_equal(kr_zonecut_copy(&cut2, &cut1), 0);
+       /* Check if exist */
+       assert_non_null(kr_zonecut_find(&cut2, (const uint8_t *)"dead"));
+       assert_non_null(kr_zonecut_find(&cut2, (const uint8_t *)"beef"));
+       assert_null(kr_zonecut_find(&cut2, (const uint8_t *)"corn"));
+       kr_zonecut_deinit(&cut1);
+       kr_zonecut_deinit(&cut2);
+}
+
 int main(void)
 {
        const UnitTest tests[] = {
-               unit_test(test_zonecut_params)
+               unit_test(test_zonecut_params),
+               unit_test(test_zonecut_copy)
        };
 
        return run_tests(tests);