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

Merge branch 'hints_reworked'

parents c382a077 de7f12b7
......@@ -259,6 +259,30 @@ void engine_stop(struct engine *engine)
uv_stop(uv_default_loop());
}
/** Register module properties in Lua environment */
static int register_properties(struct engine *engine, struct kr_module *module)
{
lua_newtable(engine->L);
if (module->config != NULL) {
REGISTER_MODULE_CALL(engine->L, module, module->config, "config");
}
for (struct kr_prop *p = module->props; p->name; ++p) {
if (p->cb != NULL && p->name != NULL) {
REGISTER_MODULE_CALL(engine->L, module, p->cb, p->name);
}
}
lua_setglobal(engine->L, module->name);
/* Register module in Lua env */
lua_getglobal(engine->L, "modules_register");
lua_getglobal(engine->L, module->name);
if (l_sandboxcall(engine->L, 1) != 0) {
lua_pop(engine->L, 1);
}
return kr_ok();
}
int engine_register(struct engine *engine, const char *name)
{
if (engine == NULL || name == NULL) {
......@@ -278,16 +302,7 @@ int engine_register(struct engine *engine, const char *name)
/* Register properties */
if (module->props) {
lua_newtable(engine->L);
if (module->config != NULL) {
REGISTER_MODULE_CALL(engine->L, module, module->config, "config");
}
for (struct kr_prop *p = module->props; p->name; ++p) {
if (p->cb != NULL && p->name != NULL) {
REGISTER_MODULE_CALL(engine->L, module, p->cb, p->name);
}
}
lua_setglobal(engine->L, module->name);
return register_properties(engine, module);
}
return kr_ok();
......
......@@ -35,6 +35,25 @@ setmetatable(modules, {
end
})
-- Register module in Lua environment
function modules_register(module)
-- Syntactic sugar for get() and set() properties
setmetatable(module, {
__index = function (t, k)
local v = rawget(t, k)
if v then return v
elseif t.get then return t.get(k)
end
end,
__newindex = function (t, k, v)
local old_v = rawget(t, k)
if not old_v and t.set then
t.set(k..' '..v)
end
end
})
end
-- Make sandboxed environment
local function make_sandbox(defined)
local __protected = { modules = true, cache = true, net = true }
......
......@@ -4,7 +4,6 @@
*/
/**
*
* @file map.h
* @brief A Crit-bit tree key-value map implementation.
*
......@@ -95,6 +94,8 @@ void map_clear(map_t *map);
/**
* Calls callback for all strings in map with the given prefix
* @param map
* @param prefix required string prefix (empty => all strings)
* @param callback callback parameters are (key, value, baton)
* @param baton passed uservalue
*/
......
......@@ -52,6 +52,8 @@
* @{
*/
#pragma once
#include <stdint.h>
#include <string.h>
#include "array.h"
......
......@@ -112,11 +112,10 @@ int kr_zonecut_add(struct kr_zonecut *cut, const knot_dname_t *ns, const knot_rd
}
/* Fetch/insert nameserver. */
const char *key = (const char *)ns;
pack_t *pack = map_get(&cut->nsset, key);
pack_t *pack = kr_zonecut_find(cut, ns);
if (pack == NULL) {
pack = mm_alloc(cut->pool, sizeof(*pack));
if (!pack || (map_set(&cut->nsset, key, pack) != 0)) {
if (!pack || (map_set(&cut->nsset, (const char *)ns, pack) != 0)) {
mm_free(cut->pool, pack);
return kr_error(ENOMEM);
}
......@@ -143,9 +142,7 @@ int kr_zonecut_del(struct kr_zonecut *cut, const knot_dname_t *ns, const knot_rd
}
/* Find the address list. */
const char *key = (const char *)ns;
map_t *nsset = &cut->nsset;
pack_t *pack = map_get(nsset, key);
pack_t *pack = kr_zonecut_find(cut, ns);
if (pack == NULL) {
return kr_error(ENOENT);
}
......@@ -154,13 +151,24 @@ int kr_zonecut_del(struct kr_zonecut *cut, const knot_dname_t *ns, const knot_rd
int ret = pack_obj_del(pack, knot_rdata_data(rdata), knot_rdata_rdlen(rdata));
if (pack->len == 0) {
/* No servers left, remove NS from the set. */
free_addr_set(key, pack, cut->pool);
return map_del(nsset, key);
free_addr_set((const char *)ns, pack, cut->pool);
return map_del(&cut->nsset, (const char *)ns);
}
return ret;
}
pack_t *kr_zonecut_find(struct kr_zonecut *cut, const knot_dname_t *ns)
{
if (cut == NULL || ns == NULL) {
return NULL;
}
const char *key = (const char *)ns;
map_t *nsset = &cut->nsset;
return map_get(nsset, key);
}
int kr_zonecut_set_sbelt(struct kr_zonecut *cut)
{
if (cut == NULL) {
......
......@@ -22,6 +22,7 @@
#include "lib/cache.h"
#include "lib/generic/map.h"
#include "lib/generic/pack.h"
struct kr_rplan;
......@@ -51,7 +52,6 @@ void kr_zonecut_deinit(struct kr_zonecut *cut);
/**
* Reset zone cut to given name and clear address list.
* @note This preserves already-allocated memory.
* @note This clears the address list even if the name doesn't change.
* @param cut zone cut to be set
* @param name new zone cut name
......@@ -80,6 +80,18 @@ 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);
/**
* Find nameserver address list in the zone cut.
*
* @note This can be used for membership test, a non-null pack is returned
* if the nameserver name exists.
*
* @param cut
* @param ns name server name
* @return pack of addresses or NULL
*/
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`
*
......
......@@ -247,6 +247,15 @@ Once you load the module, you can call the module property from the interactive
*Note* |---| this relies on function pointers, so the same ``static inline`` trick as for the ``Layer()`` is required for C/Go.
Special properties
------------------
If the module declares properties ``get`` or ``set``, they can be used in the Lua interpreter as
regular tables.
.. warning: This is not yet completely implemented, as the module I/O format may change to map_t a/o
embedded JSON tokenizer.
.. _`not present in Go`: http://blog.golang.org/gos-declaration-syntax
.. _CGO: http://golang.org/cmd/cgo/
.. _GCCGO: https://golang.org/doc/install/gccgo
......
......@@ -3,4 +3,25 @@
Static hints
------------
This is a module providing static hints from ``/etc/hosts``, document me.
\ No newline at end of file
This is a module providing static hints from ``/etc/hosts`` like file.
Properties
^^^^^^^^^^
``config``
Load specified hosts file.
:Input: ``path`` i.e. ``"/etc/hosts"``
:Output: ``{ result: bool }``
``get``
Return list of address record matching given name.
:Input: ``hostname`` i.e. ``"localhost"``
:Output: ``{ result: [address1, address2, ...] }``
``set``
Set hostname - address hint.
:Input: ``hostname address`` i.e. ``"localhost 127.0.0.1"``
:Output: ``{ result: bool }``
\ No newline at end of file
......@@ -14,6 +14,13 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file hints.h
* @brief Constructed zone cut from the hosts-like file, see @zonecut.h
*
* The module provides an override for queried address records.
*/
#include <libknot/packet/pkt.h>
#include <libknot/descriptor.h>
#include <libknot/internal/lists.h>
......@@ -21,32 +28,17 @@
#include <libknot/rrtype/aaaa.h>
#include "lib/layer/iterate.h"
#include "lib/utils.h"
#include "lib/defines.h"
#include "lib/zonecut.h"
#include "lib/module.h"
#include "lib/layer.h"
/* Defaults */
#define DEFAULT_FILE "/etc/hosts"
#define DEBUG_MSG(fmt...) QRDEBUG(NULL, "hint", fmt)
/* TODO: this is an experimental (slow) proof-of-concept,
* this will be rewritten with namedb API
*/
typedef int (*rr_callback_t)(const knot_rrset_t *, unsigned, struct kr_layer_param *);
struct hint_map {
list_t list;
mm_ctx_t pool;
};
struct hint_pair {
node_t n;
knot_dname_t *name;
char *addr;
};
static struct hint_map *g_map = NULL;
/** @todo Hack until layers can store userdata. */
static struct kr_zonecut *g_map = NULL;
static int begin(knot_layer_t *ctx, void *module_param)
{
......@@ -54,59 +46,93 @@ static int begin(knot_layer_t *ctx, void *module_param)
return ctx->state;
}
static int answer_query(pack_t *addr_set, struct kr_layer_param *param)
{
struct kr_query *qry = kr_rplan_current(param->rplan);
knot_rrset_t rr;
knot_rrset_init(&rr, qry->sname, qry->stype, KNOT_CLASS_IN);
int family_len = sizeof(struct in_addr);
if (rr.type == KNOT_RRTYPE_AAAA) {
family_len = sizeof(struct in6_addr);
}
/* Update addresses */
uint8_t *addr = pack_head(*addr_set);
while (addr != pack_tail(*addr_set)) {
size_t len = pack_obj_len(addr);
void *addr_val = pack_obj_val(addr);
if (len == family_len) {
knot_rrset_add_rdata(&rr, addr_val, len, 0, NULL);
}
addr = pack_obj_next(addr);
}
/* Process callbacks */
rr_callback_t callback = &rr_update_parent;
if (!qry->parent) {
callback = &rr_update_answer;
}
callback(&rr, 0, param);
/* Finalize */
DEBUG_MSG("<= answered from hints\n");
knot_rdataset_clear(&rr.rrs, NULL);
qry->flags |= QUERY_RESOLVED;
return KNOT_STATE_DONE;
}
static int query(knot_layer_t *ctx, knot_pkt_t *pkt)
{
assert(pkt && ctx);
struct kr_layer_param *param = ctx->data;
struct kr_query *cur = kr_rplan_current(param->rplan);
if (cur == NULL) {
struct kr_query *qry = kr_rplan_current(param->rplan);
if (qry->stype != KNOT_RRTYPE_A && qry->stype != KNOT_RRTYPE_AAAA) {
return ctx->state;
}
const knot_dname_t *qname = knot_pkt_qname(pkt);
uint16_t qtype = knot_pkt_qtype(pkt);
if (qtype != KNOT_RRTYPE_A && qtype != KNOT_RRTYPE_AAAA) {
/* Find a matching name */
pack_t *pack = kr_zonecut_find(g_map, qry->sname);
if (!pack || pack->len == 0) {
return ctx->state;
}
/* Check if updating parent zone cut. */
rr_callback_t callback = &rr_update_parent;
if (cur->parent == NULL) {
callback = &rr_update_answer;
}
return answer_query(pack, param);
}
struct hint_pair *pair = NULL;
WALK_LIST(pair, g_map->list) {
if (knot_dname_is_equal(qname, pair->name)) {
DEBUG_MSG("found hint '%s'\n", pair->addr);
int addr_type = strchr(pair->addr, ':') ? AF_INET6 : AF_INET;
if ((addr_type == AF_INET) != (qtype == KNOT_RRTYPE_A)) {
continue;
}
static int parse_addr_str(struct sockaddr_storage *sa, const char *addr)
{
int family = strchr(addr, ':') ? AF_INET6 : AF_INET;
return sockaddr_set(sa, family, addr, 0);
}
knot_rrset_t rr;
knot_rrset_init(&rr, pair->name, qtype, KNOT_CLASS_IN);
struct sockaddr_storage addr;
sockaddr_set(&addr, addr_type, pair->addr, 0);
size_t addr_len = 0;
uint8_t *raw_addr = sockaddr_raw(&addr, &addr_len);
knot_rrset_add_rdata(&rr, raw_addr, addr_len, 0, &param->answer->mm);
callback(&rr, 0, param);
cur->flags |= QUERY_RESOLVED;
return KNOT_STATE_DONE;
}
static int add_pair(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);
}
return ctx->state;
/* Parse address string */
struct sockaddr_storage ss;
if (parse_addr_str(&ss, addr) != 0) {
return kr_error(EINVAL);
}
/* Build rdata */
size_t addr_len = 0;
uint8_t *raw_addr = sockaddr_raw(&ss, &addr_len);
knot_rdata_t rdata[knot_rdata_array_size(addr_len)];
knot_rdata_init(rdata, addr_len, raw_addr, 0);
return kr_zonecut_add(hints, key, rdata);
}
static int load_map(struct hint_map *map, FILE *fp)
static int load_map(struct kr_zonecut *hints, FILE *fp)
{
knot_dname_t name_buf[KNOT_DNAME_MAXLEN];
size_t line_len = 0;
size_t count = 0;
auto_free char *line = NULL;
init_list(&map->list);
while(getline(&line, &line_len, fp) > 0) {
char *saveptr = NULL;
......@@ -116,35 +142,121 @@ static int load_map(struct hint_map *map, FILE *fp)
}
char *name_tok = strtok_r(NULL, " \t\n", &saveptr);
while (name_tok != NULL) {
struct hint_pair *pair = mm_alloc(&map->pool, sizeof(struct hint_pair));
if (pair == NULL) {
return kr_error(ENOMEM);
}
pair->name = knot_dname_from_str(name_buf, name_tok, sizeof(name_buf));
if (pair->name == NULL) {
continue;
}
pair->name = knot_dname_copy(pair->name, &map->pool);
if (pair->name == NULL) {
return kr_error(ENOMEM);
}
pair->addr = mm_alloc(&map->pool, strlen(tok) + 1);
if (pair->addr == NULL) {
return kr_error(ENOMEM);
if (add_pair(hints, name_tok, tok) == 0) {
count += 1;
}
strcpy(pair->addr, tok);
add_tail(&map->list, &pair->n);
name_tok = strtok_r(NULL, " \t\n", &saveptr);
}
}
DEBUG_MSG("loaded %zu hints\n", list_size(&map->list));
DEBUG_MSG("loaded %zu hints\n", count);
return kr_ok();
}
static int load(struct kr_module *module, const char *path)
{
auto_fclose FILE *fp = fopen(path, "r");
if (fp == NULL) {
DEBUG_MSG("reading '%s' failed: %s\n", path, strerror(errno));
return kr_error(errno);
} else {
DEBUG_MSG("reading '%s'\n", path);
}
/* Create pool and copy itself */
mm_ctx_t _pool;
mm_ctx_mempool(&_pool, MM_DEFAULT_BLKSIZE);
mm_ctx_t *pool = mm_alloc(&_pool, sizeof(*pool));
if (!pool) {
return kr_error(ENOMEM);
}
memcpy(pool, &_pool, sizeof(*pool));
/* Load file to map */
struct kr_zonecut *hints = mm_alloc(pool, sizeof(*hints));
kr_zonecut_init(hints, (const uint8_t *)(""), pool);
module->data = hints;
g_map = hints;
return load_map(hints, fp);
}
static void unload(struct kr_module *module)
{
struct kr_zonecut *hints = module->data;
if (hints) {
mp_delete(hints->pool->ctx);
module->data = NULL;
}
}
/**
* Set name => address hint.
*
* Input: { name, address }
* Output: { result: bool }
*
*/
static char* hint_set(void *env, struct kr_module *module, const char *args)
{
struct kr_zonecut *hints = module->data;
auto_free char *args_copy = strdup(args);
int ret = -1;
char *addr = strchr(args_copy, ' ');
if (addr) {
*addr = '\0';
ret = add_pair(hints, args_copy, addr + 1);
}
char *result = NULL;
asprintf(&result, "{ \"result\": %s }", ret == 0 ? "true" : "false");
return result;
}
/**
* Retrieve address hint for given name.
*
* Input: name
* Output: { address1, address2, ... }
*/
static char* hint_get(void *env, struct kr_module *module, const char *args)
{
struct kr_zonecut *hints = module->data;
knot_dname_t key[KNOT_DNAME_MAXLEN];
pack_t *pack = NULL;
size_t bufsize = 4096;
if (knot_dname_from_str(key, args, sizeof(key))) {
pack = kr_zonecut_find(hints, key);
}
if (!pack || pack->len == 0) {
return NULL;
}
auto_free char *hint_buf = malloc(bufsize);
if (hint_buf == NULL) {
return NULL;
}
char *p = hint_buf, *endp = hint_buf + bufsize;
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), p, endp - p)) {
break;
}
p += strlen(p);
addr = pack_obj_next(addr);
if (p + 2 < endp && addr != pack_tail(*pack)) {
strcpy(p, " ");
p += 1;
}
}
char *result = NULL;
asprintf(&result, "{ \"result\": [ %s ] }", hint_buf ? hint_buf : "");
return result;
}
/*
* Module implementation.
*/
......@@ -160,31 +272,32 @@ const knot_layer_api_t *hints_layer(void)
int hints_init(struct kr_module *module)
{
auto_fclose FILE *fp = fopen(DEFAULT_FILE, "r");
if (fp == NULL) {
DEBUG_MSG("reading %s failed", DEFAULT_FILE);
return kr_error(errno);
}
mm_ctx_t pool;
mm_ctx_mempool(&pool, MM_DEFAULT_BLKSIZE);
struct hint_map *map = mm_alloc(&pool, sizeof(struct hint_map));
map->pool = pool;
module->data = map;
g_map = map;
return load_map(map, fp);
return load(module, DEFAULT_FILE);
}
int hints_deinit(struct kr_module *module)
int hints_config(struct kr_module *module, const char *conf)
{
struct hint_map *map = module->data;
if (map) {
mp_delete(map->pool.ctx);
unload(module);
if (!conf || strlen(conf) < 1) {
conf = DEFAULT_FILE;
}
return load(module, conf);
}
int hints_deinit(struct kr_module *module)
{
unload(module);
return kr_ok();
}
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.", },
{ NULL, NULL, NULL }
};
return prop_list;
}
KR_MODULE_EXPORT(hints);
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