Skip to content
Snippets Groups Projects
Commit d5112708 authored by Marek Vavruša's avatar Marek Vavruša
Browse files

Merge branch 'resumable-layers'

parents af6b296c c83b7817
No related branches found
No related tags found
No related merge requests found
......@@ -157,6 +157,21 @@ the modules use as the :ref:`input configuration <mod-properties>`.
hints = '/etc/hosts'
}
.. warning:: Modules specified including their configuration may not load exactly in the same order as specified.
Modules are inherently ordered by their declaration. Some modules are built-in, so it would be normally impossible to place for example *hints* before *rrcache*. You can enforce specific order by precedence operators **>** and **<**.
.. code-block:: lua
modules = {
'hints > iterate', -- Hints AFTER iterate
'policy > hints', -- Policy AFTER hints
'view < rrcache' -- View BEFORE rrcache
}
modules.list() -- Check module call order
This is useful if you're writing a module with a layer, that evaluates an answer before writing it into cache for example.
.. tip:: The configuration and CLI syntax is Lua language, with which you may already be familiar with.
If not, you can read the `Learn Lua in 15 minutes`_ for a syntax overview. Spending just a few minutes
will allow you to break from static configuration, write more efficient configuration with iteration, and
......
......@@ -65,11 +65,23 @@ static int mod_load(lua_State *L)
format_error(L, "expected 'load(string name)'");
lua_error(L);
}
/* Parse precedence declaration */
auto_free char *declaration = strdup(lua_tostring(L, 1));
if (!declaration) {
return kr_error(ENOMEM);
}
const char *name = strtok(declaration, " ");
const char *precedence = strtok(NULL, " ");
const char *ref = strtok(NULL, " ");
/* Load engine module */
struct engine *engine = engine_luaget(L);
int ret = engine_register(engine, lua_tostring(L, 1));
int ret = engine_register(engine, name, precedence, ref);
if (ret != 0) {
format_error(L, kr_strerror(ret));
if (ret == kr_error(EIDRM)) {
format_error(L, "referenced module not found");
} else {
format_error(L, kr_strerror(ret));
}
lua_error(L);
}
......
......@@ -214,39 +214,46 @@ static void l_unpack_json(lua_State *L, JsonNode *table)
}
}
/** @internal Recursive Lua/JSON serialization. */
static JsonNode *l_pack_elem(lua_State *L, int top)
{
if (lua_isstring(L, top)) {
return json_mkstring(lua_tostring(L, top));
}
if (lua_isnumber(L, top)) {
return json_mknumber(lua_tonumber(L, top));
}
if (lua_isboolean(L, top)) {
return json_mkbool(lua_toboolean(L, top));
switch(lua_type(L, top)) {
case LUA_TSTRING: return json_mkstring(lua_tostring(L, top));
case LUA_TNUMBER: return json_mknumber(lua_tonumber(L, top));
case LUA_TBOOLEAN: return json_mkbool(lua_toboolean(L, top));
case LUA_TTABLE: break; /* Table, iterate it. */
default: return json_mknull();
}
/* Use absolute indexes here, as the table may be nested. */
JsonNode *node = NULL;
lua_pushnil(L);
while(lua_next(L, top) != 0) {
JsonNode *val = l_pack_elem(L, top + 2);
const bool no_key = lua_isnumber(L, top + 1);
if (!node) {
node = no_key ? json_mkarray() : json_mkobject();
if (!node) {
return NULL;
}
}
/* Insert to array/table */
if (no_key) {
json_append_element(node, val);
} else {
json_append_member(node, lua_tostring(L, top + 1), val);
}
lua_pop(L, 1);
}
return json_mknull();
return node;
}
/** @internal Serialize to string */
static char *l_pack_json(lua_State *L, int top)
{
JsonNode *root = json_mkobject();
JsonNode *root = l_pack_elem(L, top);
if (!root) {
return NULL;
}
/* Iterate table on stack */
lua_pushnil(L);
while(lua_next(L, top)) {
JsonNode *val = l_pack_elem(L, -1);
if (lua_isstring(L, -2)) {
json_append_member(root, lua_tostring(L, -2), val);
} else {
json_append_element(root, val);
}
lua_pop(L, 1);
}
lua_pop(L, 1);
/* Serialize to string */
char *result = json_encode(root);
json_delete(root);
return result;
......@@ -341,10 +348,10 @@ static int init_resolver(struct engine *engine)
}
/* Load basic modules */
engine_register(engine, "iterate");
engine_register(engine, "validate");
engine_register(engine, "rrcache");
engine_register(engine, "pktcache");
engine_register(engine, "iterate", NULL, NULL);
engine_register(engine, "validate", NULL, NULL);
engine_register(engine, "rrcache", NULL, NULL);
engine_register(engine, "pktcache", NULL, NULL);
/* Initialize storage backends */
struct storage_api lmdb = {
......@@ -482,7 +489,7 @@ int engine_cmd(struct engine *engine, const char *str)
#define l_dosandboxfile(L, filename) \
(luaL_loadfile((L), (filename)) || engine_pcall((L), 0))
static int engine_loadconf(struct engine *engine)
static int engine_loadconf(struct engine *engine, const char *config_path)
{
/* Use module path for including Lua scripts */
static const char l_paths[] = "package.path = package.path..';" PREFIX MODULEDIR "/?.lua'";
......@@ -500,8 +507,8 @@ static int engine_loadconf(struct engine *engine)
return kr_error(ENOEXEC);
}
/* Load config file */
if(access("config", F_OK ) != -1 ) {
ret = l_dosandboxfile(engine->L, "config");
if(access(config_path, F_OK ) != -1 ) {
ret = l_dosandboxfile(engine->L, config_path);
}
if (ret == 0) {
/* Load defaults */
......@@ -519,10 +526,10 @@ static int engine_loadconf(struct engine *engine)
return ret;
}
int engine_start(struct engine *engine)
int engine_start(struct engine *engine, const char *config_path)
{
/* Load configuration. */
int ret = engine_loadconf(engine);
int ret = engine_loadconf(engine, config_path);
if (ret != 0) {
return ret;
}
......@@ -565,19 +572,36 @@ static int register_properties(struct engine *engine, struct kr_module *module)
return kr_ok();
}
int engine_register(struct engine *engine, const char *name)
/** @internal Find matching module */
static size_t module_find(module_array_t *mod_list, const char *name)
{
size_t found = mod_list->len;
for (size_t i = 0; i < mod_list->len; ++i) {
struct kr_module *mod = mod_list->at[i];
if (strcmp(mod->name, name) == 0) {
found = i;
break;
}
}
return found;
}
int engine_register(struct engine *engine, const char *name, const char *precedence, const char* ref)
{
if (engine == NULL || name == NULL) {
return kr_error(EINVAL);
}
/* Check priority modules */
bool is_priority = false;
if (name[0] == '<') {
is_priority = true;
name += 1;
}
/* Make sure module is unloaded */
(void) engine_unregister(engine, name);
/* Find the index of referenced module. */
module_array_t *mod_list = &engine->modules;
size_t ref_pos = mod_list->len;
if (precedence && ref) {
ref_pos = module_find(mod_list, ref);
if (ref_pos >= mod_list->len) {
return kr_error(EIDRM);
}
}
/* Attempt to load binary module */
struct kr_module *module = malloc(sizeof(*module));
if (!module) {
......@@ -597,11 +621,22 @@ int engine_register(struct engine *engine, const char *name)
engine_unload(engine, module);
return kr_error(ENOMEM);
}
/* Push to front if priority module */
if (is_priority) {
struct kr_module **arr = engine->modules.at;
memmove(&arr[1], &arr[0], sizeof(*arr) * (engine->modules.len - 1));
arr[0] = module;
/* Evaluate precedence operator */
if (precedence) {
struct kr_module **arr = mod_list->at;
size_t emplacement = mod_list->len;
if (strcasecmp(precedence, ">") == 0) {
if (ref_pos + 1 < mod_list->len)
emplacement = ref_pos + 1; /* Insert after target */
}
if (strcasecmp(precedence, "<") == 0) {
emplacement = ref_pos; /* Insert at target */
}
/* Move the tail if it has some elements. */
if (emplacement + 1 < mod_list->len) {
memmove(&arr[emplacement + 1], &arr[emplacement], sizeof(*arr) * (mod_list->len - (emplacement + 1)));
arr[emplacement] = module;
}
}
/* Register properties */
......@@ -614,16 +649,8 @@ int engine_register(struct engine *engine, const char *name)
int engine_unregister(struct engine *engine, const char *name)
{
/* Find matching module. */
module_array_t *mod_list = &engine->modules;
size_t found = mod_list->len;
for (size_t i = 0; i < mod_list->len; ++i) {
struct kr_module *mod = mod_list->at[i];
if (strcmp(mod->name, name) == 0) {
found = i;
break;
}
}
size_t found = module_find(mod_list, name);
if (found < mod_list->len) {
engine_unload(engine, mod_list->at[found]);
array_del(*mod_list, found);
......
......@@ -61,9 +61,9 @@ int engine_init(struct engine *engine, mm_ctx_t *pool);
void engine_deinit(struct engine *engine);
/** @warning This function leaves 1 string result on stack. */
int engine_cmd(struct engine *engine, const char *str);
int engine_start(struct engine *engine);
int engine_start(struct engine *engine, const char *config_path);
void engine_stop(struct engine *engine);
int engine_register(struct engine *engine, const char *module);
int engine_register(struct engine *engine, const char *module, const char *precedence, const char* ref);
int engine_unregister(struct engine *engine, const char *module);
void engine_lualib(struct engine *engine, const char *name, int (*lib_cb) (struct lua_State *));
......
......@@ -52,6 +52,7 @@ setmetatable(modules, {
if type(k) == 'number' then k = v end
if not rawget(_G, k) then
modules.load(k)
k = string.match(k, '%w+')
local mod = _G[k]
local config = rawget(mod, 'config')
if mod and config then
......
......@@ -130,6 +130,7 @@ static void help(int argc, char *argv[])
printf("Usage: %s [parameters] [rundir]\n", argv[0]);
printf("\nParameters:\n"
" -a, --addr=[addr] Server address (default: localhost#53).\n"
" -c, --config=[path] Config file path (relative to [rundir]) (default: config).\n"
" -k, --keyfile=[path] File containing trust anchors (DS or DNSKEY).\n"
" -f, --forks=N Start N forks sharing the configuration.\n"
" -v, --verbose Run in verbose mode.\n"
......@@ -202,12 +203,14 @@ int main(int argc, char **argv)
array_t(char*) addr_set;
array_init(addr_set);
char *keyfile = NULL;
const char *config = NULL;
static char keyfile_buf[PATH_MAX + 1];
/* Long options. */
int c = 0, li = 0, ret = 0;
struct option opts[] = {
{"addr", required_argument, 0, 'a'},
{"config", required_argument, 0, 'c'},
{"keyfile",required_argument, 0, 'k'},
{"forks",required_argument, 0, 'f'},
{"verbose", no_argument, 0, 'v'},
......@@ -215,12 +218,15 @@ int main(int argc, char **argv)
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}
};
while ((c = getopt_long(argc, argv, "a:f:k:vVh", opts, &li)) != -1) {
while ((c = getopt_long(argc, argv, "a:c:f:k:vVh", opts, &li)) != -1) {
switch (c)
{
case 'a':
array_push(addr_set, optarg);
break;
case 'c':
config = optarg;
break;
case 'f':
g_interactive = 0;
forks = atoi(optarg);
......@@ -262,7 +268,7 @@ int main(int argc, char **argv)
if (optind < argc) {
const char *rundir = argv[optind];
if (access(rundir, W_OK) != 0) {
log_error("[system] rundir '%s': not writeable\n", rundir);
log_error("[system] rundir '%s': %s\n", rundir, strerror(errno));
return EXIT_FAILURE;
}
ret = chdir(rundir);
......@@ -270,6 +276,11 @@ int main(int argc, char **argv)
log_error("[system] rundir '%s': %s\n", rundir, strerror(errno));
return EXIT_FAILURE;
}
if(config && access(config, R_OK) != 0) {
log_error("[system] rundir '%s'\n", rundir);
log_error("[system] config '%s': %s\n", config, strerror(errno));
return EXIT_FAILURE;
}
}
kr_crypto_init();
......@@ -324,7 +335,7 @@ int main(int argc, char **argv)
}
/* Start the scripting engine */
if (ret == 0) {
ret = engine_start(&engine);
ret = engine_start(&engine, config ? config : "config");
if (ret == 0) {
if (keyfile) {
auto_free char *cmd = afmt("trust_anchors.file = '%s'", keyfile);
......
......@@ -3,10 +3,26 @@
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
This is a module providing static hints from ``/etc/hosts`` like file for forward records (A/AAAA) and reverse records (PTR).
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.
Examples
^^^^^^^^
.. code-block:: lua
-- Load hints after iterator
modules = { 'hints > iterate' }
-- Load hints before rrcache, custom hosts file
modules = { ['hints < rrcache'] = 'hosts.custom' }
-- Add root hints
hints.root({
['j.root-servers.net.'] = { '2001:503:c27::2:30', '192.58.128.30' }
})
-- Set custom hint
hints['localhost'] = '127.0.0.1'
Properties
^^^^^^^^^^
......
......@@ -36,24 +36,130 @@
#define DEFAULT_FILE "/etc/hosts"
#define DEBUG_MSG(qry, fmt...) QRDEBUG(qry, "hint", fmt)
/* Structure for reverse search (address to domain) */
struct rev_search_baton {
knot_pkt_t *pkt;
const knot_dname_t *name;
union {
struct in_addr ip4;
struct in6_addr ip6;
} addr;
size_t addr_len;
};
static int begin(knot_layer_t *ctx, void *module_param)
{
ctx->data = module_param;
return ctx->state;
}
static int answer_query(knot_pkt_t *pkt, pack_t *addr_set, struct kr_query *qry)
static int put_answer(knot_pkt_t *pkt, knot_rrset_t *rr)
{
uint16_t rrtype = qry->stype;
uint16_t rrclass = qry->sclass;
if (rrtype != KNOT_RRTYPE_A && rrtype != KNOT_RRTYPE_AAAA) {
return kr_error(ENOENT);
int ret = 0;
if (!knot_rrset_empty(rr)) {
/* Update packet question */
if (!knot_dname_is_equal(knot_pkt_qname(pkt), rr->owner)) {
KR_PKT_RECYCLE(pkt);
knot_pkt_put_question(pkt, rr->owner, rr->rclass, rr->type);
}
/* Append to packet */
ret = knot_pkt_put(pkt, KNOT_COMPR_HINT_QNAME, rr, KNOT_PF_FREE);
} else {
ret = kr_error(ENOENT);
}
/* Clear RR if failed */
if (ret != 0) {
knot_rrset_clear(rr, &pkt->mm);
}
return ret;
}
static int find_reverse(const char *k, void *v, void *baton)
{
const knot_dname_t *domain = (const knot_dname_t *)k;
pack_t *addr_set = (pack_t *)v;
struct rev_search_baton *search = baton;
/* Check if it matches any of the addresses. */
bool matches = false;
uint8_t *addr = pack_head(*addr_set);
while (!matches && addr != pack_tail(*addr_set)) {
size_t len = pack_obj_len(addr);
void *addr_val = pack_obj_val(addr);
matches = (len == search->addr_len && memcmp(addr_val, (void *)&search->addr, len) == 0);
addr = pack_obj_next(addr);
}
/* Synthesise PTR record */
if (!matches) {
return 0;
}
knot_pkt_t *pkt = search->pkt;
knot_dname_t *qname = knot_dname_copy(search->name, &pkt->mm);
knot_rrset_t rr;
knot_rrset_init(&rr, qname, KNOT_RRTYPE_PTR, KNOT_CLASS_IN);
knot_rrset_add_rdata(&rr, domain, knot_dname_size(domain), 0, &pkt->mm);
/* Insert into packet */
int ret = put_answer(pkt, &rr);
if (ret == 0) {
return 1;
}
return ret;
}
static inline uint8_t label2num(const uint8_t **src, int base)
{
uint8_t ret = strtoul((const char *)(*src + 1), NULL, base) & 0xff; /* ord(0-64) => labels are separators */
*src = knot_wire_next_label(*src, NULL);
return ret;
}
static int satisfy_reverse(struct kr_zonecut *hints, knot_pkt_t *pkt, struct kr_query *qry)
{
struct rev_search_baton baton = {
.pkt = pkt,
.name = qry->sname,
.addr_len = sizeof(struct in_addr)
};
/* Check if it is IPv6/IPv4 query. */
size_t need_labels = baton.addr_len;
if (knot_dname_in((const uint8_t *)"\3ip6\4arpa", qry->sname)) {
baton.addr_len = sizeof(struct in6_addr);
need_labels = baton.addr_len * 2; /* Each label is a nibble */
}
/* Make address from QNAME (reverse order). */
int labels = knot_dname_labels(qry->sname, NULL);
if (labels != need_labels + 2) {
return kr_error(EINVAL);
}
const uint8_t *src = qry->sname;
uint8_t *dst = (uint8_t *)&baton.addr.ip4 + baton.addr_len - 1;
for (size_t i = 0; i < baton.addr_len; ++i) {
if (baton.addr_len == sizeof(struct in_addr)) { /* IPv4, 1 label = 1 octet */
*dst = label2num(&src, 10);
} else { /* IPv4, 1 label = 1 nibble */
*dst = label2num(&src, 16);
*dst |= label2num(&src, 16) << 4;
}
dst -= 1;
}
/* Try to find matching domains. */
int ret = map_walk(&hints->nsset, find_reverse, &baton);
if (ret > 0) {
return kr_ok(); /* Found */
}
return kr_error(ENOENT);
}
static int satisfy_forward(struct kr_zonecut *hints, knot_pkt_t *pkt, struct kr_query *qry)
{
/* Find a matching name */
pack_t *addr_set = kr_zonecut_find(hints, qry->sname);
if (!addr_set || addr_set->len == 0) {
return kr_error(ENOENT);
}
knot_dname_t *qname = knot_dname_copy(qry->sname, &pkt->mm);
knot_rrset_t rr;
knot_rrset_init(&rr, qname, rrtype, rrclass);
int family_len = sizeof(struct in_addr);
knot_rrset_init(&rr, qname, qry->stype, qry->sclass);
size_t family_len = sizeof(struct in_addr);
if (rr.type == KNOT_RRTYPE_AAAA) {
family_len = sizeof(struct in6_addr);
}
......@@ -69,46 +175,33 @@ static int answer_query(knot_pkt_t *pkt, pack_t *addr_set, struct kr_query *qry)
addr = pack_obj_next(addr);
}
int ret = 0;
if (!knot_rrset_empty(&rr)) {
/* Update packet question */
if (!knot_dname_is_equal(knot_pkt_qname(pkt), qname)) {
KR_PKT_RECYCLE(pkt);
knot_pkt_put_question(pkt, qname, rrclass, rrtype);
}
/* Append to packet */
ret = knot_pkt_put(pkt, KNOT_COMPR_HINT_QNAME, &rr, KNOT_PF_FREE);
} else {
ret = kr_error(ENOENT);
}
/* Clear RR if failed */
if (ret != 0) {
knot_rrset_clear(&rr, &pkt->mm);
}
return ret;
return put_answer(pkt, &rr);
}
static int query(knot_layer_t *ctx, knot_pkt_t *pkt)
{
struct kr_request *req = ctx->data;
struct kr_query *qry = req->current_query;
if (!qry || ctx->state & (KNOT_STATE_DONE|KNOT_STATE_FAIL)) {
if (!qry || ctx->state & (KNOT_STATE_FAIL)) {
return ctx->state;
}
/* Find a matching name */
struct kr_module *module = ctx->api->data;
struct kr_zonecut *hint_map = module->data;
pack_t *pack = kr_zonecut_find(hint_map, qry->sname);
if (!pack || pack->len == 0) {
return ctx->state;
switch(qry->stype) {
case KNOT_RRTYPE_A:
case KNOT_RRTYPE_AAAA: /* Find forward record hints */
if (satisfy_forward(hint_map, pkt, qry) != 0)
return ctx->state;
break;
case KNOT_RRTYPE_PTR: /* Find PTR record */
if (satisfy_reverse(hint_map, pkt, qry) != 0)
return ctx->state;
break;
default:
return ctx->state; /* Ignore */
}
/* Write to packet */
int ret = answer_query(pkt, pack, qry);
if (ret != 0) {
return ctx->state;
}
DEBUG_MSG(qry, "<= answered from hints\n");
qry->flags &= ~QUERY_DNSSEC_WANT; /* Never authenticated */
qry->flags |= QUERY_CACHED|QUERY_NO_MINIMIZE;
......@@ -292,6 +385,18 @@ static int pack_hint(const char *k, void *v, void *baton)
return kr_ok();
}
static void unpack_hint(struct kr_zonecut *root_hints, JsonNode *table, const char *name)
{
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;
default: continue;
}
}
}
/**
* Get/set root hints set.
*
......@@ -303,23 +408,18 @@ static char* hint_root(void *env, struct kr_module *module, const char *args)
{
struct engine *engine = env;
struct kr_context *ctx = &engine->resolver;
struct kr_zonecut *root_hints = &ctx->root_hints;
/* 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;
}
}
kr_zonecut_set(root_hints, (const uint8_t *)"");
unpack_hint(root_hints, root_node, NULL);
json_delete(root_node);
}
/* Return current root hints */
char *result = NULL;
JsonNode *root_node = json_mkobject();
if (map_walk(&ctx->root_hints.nsset, pack_hint, root_node) == 0) {
if (map_walk(&root_hints->nsset, pack_hint, root_node) == 0) {
result = json_encode(root_node);
}
json_delete(root_node);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment