{
/* 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) {
}
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);
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;
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;
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);
}
void kr_zonecut_deinit(struct kr_zonecut *cut)
{
- if (cut == NULL) {
+ if (!cut) {
return;
}
mm_free(cut->pool, cut->name);
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. */
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);
}
pack_t *kr_zonecut_find(struct kr_zonecut *cut, const knot_dname_t *ns)
{
- if (cut == NULL || ns == NULL) {
+ if (!cut || !ns) {
return NULL;
}
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)];
/* 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();
}
/* Name server not found, start with SBELT. */
- return kr_zonecut_set_sbelt(cut);
+ return kr_zonecut_set_sbelt(ctx, 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.
*
/**
* 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.
------------
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
^^^^^^^^^^
: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
#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"
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.
*
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;
}
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;
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);