Commit a69c55f9 authored by Marek Vavruša's avatar Marek Vavruša

modules/hints: configurable root hints, part of module

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
parent 98b7b5a9
......@@ -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);
......
......@@ -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;
......
......@@ -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;
......
......@@ -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);
}
......@@ -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.
......
......@@ -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
......@@ -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;
......
......@@ -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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment