Commit de7f12b7 authored by Marek Vavruša's avatar Marek Vavruša Committed by Marek Vavruša

modules/hints: reworked, configurable and exposed props

* updated documentation
* the props and config api has some workarounds yet
parent ae7b572d
......@@ -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