diff --git a/modules/hints/README.rst b/modules/hints/README.rst index 627f79954250b2fe20011b0914906ba842cd288e..b7ddf618a5550440d2dabf16529a40976c7b1e0f 100644 --- a/modules/hints/README.rst +++ b/modules/hints/README.rst @@ -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 diff --git a/modules/hints/hints.c b/modules/hints/hints.c index aa73c54aafc8475e3e219aa666f780a9e478bc8d..8b8873bbce7ab9b00bac77132d261831bbbe8242 100644 --- a/modules/hints/hints.c +++ b/modules/hints/hints.c @@ -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, ¶m->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);