]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
modules/hints: reworked, configurable and exposed props
authorMarek Vavruša <marek.vavrusa@nic.cz>
Sun, 19 Apr 2015 19:33:36 +0000 (21:33 +0200)
committerMarek Vavruša <marek.vavrusa@nic.cz>
Sun, 19 Apr 2015 19:35:03 +0000 (21:35 +0200)
* updated documentation
* the props and config api has some workarounds yet

modules/hints/README.rst
modules/hints/hints.c

index 627f79954250b2fe20011b0914906ba842cd288e..b7ddf618a5550440d2dabf16529a40976c7b1e0f 100644 (file)
@@ -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
index aa73c54aafc8475e3e219aa666f780a9e478bc8d..8b8873bbce7ab9b00bac77132d261831bbbe8242 100644 (file)
     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>
 #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);