]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
lib: implemented new zonecut interface
authorMarek Vavruša <marek.vavrusa@nic.cz>
Sat, 18 Apr 2015 21:38:58 +0000 (23:38 +0200)
committerMarek Vavruša <marek.vavrusa@nic.cz>
Sat, 18 Apr 2015 21:40:40 +0000 (23:40 +0200)
instead of keeping just random NS and address,
it keeps a map of nameservers => {addresslist}

this allows scoring and correct NS election, fallback
and evaluation

lib/layer.h
lib/layer/iterate.c
lib/layer/iterate.h
lib/resolve.c
lib/rplan.c
lib/rplan.h
lib/zonecut.c
lib/zonecut.h

index 6dd115d2d878bf118c0a3c763875f42eb48fffab..2189581a8fe12748bcc6e7726dd538a7c9ebfda1 100644 (file)
 #include <libknot/processing/layer.h>
 #include <libknot/packet/pkt.h>
 
-#include "lib/resolve.h"
-#include "lib/rplan.h"
+#include "lib/defines.h"
+
+struct kr_context;
+struct kr_rplan;
 
 /**
  * Processing module parameters.
index 271f9e95e7883e71e994fb98e285fb0421332c40..4e971b56896ddae5a0e20b142020ed53c5e1c51c 100644 (file)
@@ -127,14 +127,10 @@ static void follow_cname_chain(const knot_dname_t **cname, const knot_rrset_t *r
 static int update_nsaddr(const knot_rrset_t *rr, struct kr_query *query, uint16_t index)
 {
        if (rr->type == KNOT_RRTYPE_A || rr->type == KNOT_RRTYPE_AAAA) {
-               if (knot_dname_is_equal(query->zone_cut.ns, rr->owner)) {
-                       /* Set zone cut address. */
-                       int ret = kr_set_zone_cut_addr(&query->zone_cut, rr, index);
-                       if (ret == KNOT_EOK) {
-                               return KNOT_STATE_DONE;
-                       } else {
-                               return KNOT_STATE_FAIL;
-                       }
+               const knot_rdata_t *rdata = knot_rdataset_at(&rr->rrs, index);
+               int ret = kr_zonecut_add(&query->zone_cut, rr->owner, rdata);
+               if (ret != 0) {
+                       return KNOT_STATE_FAIL;
                }
        }
 
@@ -169,99 +165,83 @@ int rr_update_answer(const knot_rrset_t *rr, unsigned hint, struct kr_layer_para
        return KNOT_STATE_DONE;
 }
 
-static bool has_glue(const knot_dname_t *ns_name, knot_pkt_t *pkt)
+/** Attempt to find glue for given nameserver name (best effort). */
+static int fetch_glue(knot_pkt_t *pkt, const knot_dname_t *ns, struct kr_layer_param *param)
 {
+       int result = 0;
        const knot_pktsection_t *ar = knot_pkt_section(pkt, KNOT_ADDITIONAL);
        for (unsigned i = 0; i < ar->count; ++i) {
                const knot_rrset_t *rr = knot_pkt_rr(ar, i);
-               if ((rr->type == KNOT_RRTYPE_A || rr->type == KNOT_RRTYPE_AAAA) &&
-                  (knot_dname_is_equal(ns_name, rr->owner))) {
-                       return true;
+               if (knot_dname_is_equal(ns, rr->owner)) {
+                       (void) update_glue(rr, 0, param);
+                       result += 1;
                }
        }
-       return false;
+       return result;
 }
 
-static int nameserver_score(const knot_rrset_t *rr, unsigned hint, knot_pkt_t *pkt, struct kr_layer_param *param)
+static int update_cut(knot_pkt_t *pkt, const knot_rrset_t *rr, struct kr_layer_param *param)
 {
        struct kr_query *query = kr_rplan_current(param->rplan);
-       const knot_dname_t *ns_name = knot_ns_name(&rr->rrs, hint);
-       int score = kr_nsrep_score(rr->owner, param);
-       if (score < KR_NS_VALID) {
-               return score;
-       }
+       struct kr_zonecut *cut = &query->zone_cut;
+       int state = KNOT_STATE_CONSUME;
 
        /* Authority MUST be at/below the authority of the nameserver, otherwise
         * possible cache injection attempt. */
-       if (!knot_dname_in(query->zone_cut.name, rr->owner)) {
+       if (!knot_dname_in(cut->name, rr->owner)) {
                DEBUG_MSG("<= authority: ns outside bailiwick, rejecting\n");
-               return KR_NS_INVALID;
+               return KNOT_STATE_FAIL;
        }
 
-       /* Ignore already resolved zone cut. */
-       if (knot_dname_is_equal(rr->owner, query->zone_cut.name)) {
-               return KR_NS_VALID;
-       } else {
-               score += 1;
+       /* Update zone cut name */
+       if (!knot_dname_is_equal(rr->owner, cut->name)) {
+               kr_zonecut_set(cut, rr->owner);
+               state = KNOT_STATE_DONE;
        }
 
-       /* Check if contains glue. */
-       if (has_glue(ns_name, pkt)) {
-               score += 1;
+       /* Fetch glue for each NS */
+       kr_zonecut_add(cut, knot_ns_name(&rr->rrs, 0), NULL);
+       for (unsigned i = 0; i < rr->rrs.rr_count; ++i) {
+               const knot_dname_t *ns_name = knot_ns_name(&rr->rrs, i);
+               int glue_records = fetch_glue(pkt, ns_name, param);
+               /* Glue is mandatory for NS below zone */
+               if (knot_dname_in(ns_name, rr->owner) ) {
+                       if (glue_records == 0) {
+                               DEBUG_MSG("<= authority: missing mandatory glue, rejecting\n");
+                               return KNOT_STATE_FAIL;
+                       }
+               }
        }
 
-       return score;
+       return state;
 }
 
 static int process_authority(knot_pkt_t *pkt, struct kr_layer_param *param)
 {
-       struct kr_query *query = kr_rplan_current(param->rplan);
-       const knot_rrset_t *best_ns = NULL;
-       int best_score = 0;
+       int result = KNOT_STATE_CONSUME;
+       const knot_pktsection_t *ns = knot_pkt_section(pkt, KNOT_AUTHORITY);
 
        /* AA, terminate resolution chain. */
        if (knot_wire_get_aa(pkt->wire)) {
                return KNOT_STATE_CONSUME;
        }
 
-       /* Elect best name server candidate. */
-       const knot_pktsection_t *ns = knot_pkt_section(pkt, KNOT_AUTHORITY);
+       /* Update zone cut information. */
        for (unsigned i = 0; i < ns->count; ++i) {
                const knot_rrset_t *rr = knot_pkt_rr(ns, i);
                if (rr->type == KNOT_RRTYPE_NS) {
-                       int score = nameserver_score(rr, 0, pkt, param);
-                       if (score < KR_NS_VALID) {
-                               return KNOT_STATE_FAIL;
+                       int state = update_cut(pkt, rr, param);
+                       switch(state) {
+                       case KNOT_STATE_DONE: result = state; break;
+                       case KNOT_STATE_FAIL: return state; break;
+                       default:              /* continue */ break;
                        }
-                       if (score > best_score) {
-                               best_ns = rr;
-                               best_score = score;
-                       }
-               }
-       }
-
-       /* Update name server candidate. */
-       if (best_ns != NULL) {
-               kr_set_zone_cut(&query->zone_cut, best_ns->owner, knot_ns_name(&best_ns->rrs, 0));
-               return KNOT_STATE_DONE;
-       }
-
-       return KNOT_STATE_CONSUME;
-}
-
-static int process_additional(knot_pkt_t *pkt, struct kr_layer_param *param)
-{
-       /* Attempt to find glue for current nameserver. */
-       const knot_pktsection_t *ar = knot_pkt_section(pkt, KNOT_ADDITIONAL);
-       for (unsigned i = 0; i < ar->count; ++i) {
-               const knot_rrset_t *rr = knot_pkt_rr(ar, i);
-               int state = update_glue(rr, 0, param);
-               if (state != KNOT_STATE_CONSUME) {
-                       return state;
                }
        }
 
-       return KNOT_STATE_DONE;
+       /* CONSUME => Unhelpful referral.
+        * DONE    => Zone cut updated. */
+       return result;
 }
 
 static void finalize_answer(knot_pkt_t *pkt, struct kr_layer_param *param)
@@ -388,9 +368,10 @@ static int prepare_query(knot_layer_t *ctx, knot_pkt_t *pkt)
        }
 
 #ifndef NDEBUG
-       char qname_str[KNOT_DNAME_MAXLEN], zonecut_str[KNOT_DNAME_MAXLEN], ns_str[KNOT_DNAME_MAXLEN];
+       char qname_str[KNOT_DNAME_MAXLEN], zonecut_str[KNOT_DNAME_MAXLEN], ns_str[SOCKADDR_STRLEN];
        knot_dname_to_str(qname_str, qname, sizeof(qname_str));
-       knot_dname_to_str(ns_str, query->zone_cut.ns, sizeof(ns_str));
+       struct sockaddr *addr = &query->ns.addr.ip;
+       inet_ntop(addr->sa_family, kr_nsrep_inaddr(query->ns.addr), ns_str, sizeof(ns_str));
        knot_dname_to_str(zonecut_str, query->zone_cut.name, sizeof(zonecut_str));
        DEBUG_MSG("=> querying: '%s' zone cut: '%s' m12n: '%s'\n", ns_str, zonecut_str, qname_str);
 #endif
@@ -454,9 +435,8 @@ static int resolve(knot_layer_t *ctx, knot_pkt_t *pkt)
                DEBUG_MSG("<= rcode: %s\n", rcode ? rcode->name : "??");
                state = process_answer(pkt, param);
                break;
-       case KNOT_STATE_DONE: /* Referral, try to find glue. */
+       case KNOT_STATE_DONE: /* Referral */
                DEBUG_MSG("<= referral response, follow\n");
-               state = process_additional(pkt, param);
                break;
        default:
                break;
index 489e15a16d684b310338fa754079e5982171e5e0..3716e791161bc554a92b3bdf7fd38fff50fe0beb 100644 (file)
@@ -17,6 +17,7 @@
 #pragma once
 
 #include "lib/layer.h"
+#include "lib/rplan.h"
 
 /* Processing module implementation. */
 extern const knot_layer_api_t *iterate_layer(void);
index c836908a8f0c8135f1eaaf1408719b5c13219c29..3f76aa61fe43ed01cb8c20d09c1ccfbd89efa173 100644 (file)
@@ -22,8 +22,8 @@
 #include <libknot/descriptor.h>
 #include <dnssec/random.h>
 
+#include "lib/rplan.h"
 #include "lib/resolve.h"
-#include "lib/defines.h"
 #include "lib/layer/itercache.h"
 #include "lib/layer/iterate.h"
 
 /* Defines */
 #define ITER_LIMIT 50
 
-/** Invalidate current NS in cache. */
+/** Invalidate current NS/addr pair. */
 static int invalidate_ns(struct kr_rplan *rplan, struct kr_query *qry)
 {
-       namedb_txn_t *txn = kr_rplan_txn_acquire(rplan, 0);
-       if (txn == NULL) {
-               return KNOT_EOK;
-       }
-
-       /* Fetch current nameserver cache. */
-       uint32_t drift = qry->timestamp.tv_sec;
-       knot_rrset_t cached;
-       knot_rrset_init(&cached, qry->zone_cut.name, KNOT_RRTYPE_NS, KNOT_CLASS_IN);
-       if (kr_cache_peek(txn, &cached, &drift) != KNOT_EOK) {
-               kr_init_zone_cut(&qry->zone_cut);
-               return KNOT_EOK;
-       }
-       cached = kr_cache_materialize(&cached, drift, rplan->pool);
-
-       /* Find a matching RD. */
-       knot_rdataset_t to_remove;
-       knot_rdataset_init(&to_remove);
-       for (unsigned i = 0; i < cached.rrs.rr_count; ++i) {
-               knot_rdata_t *rd = knot_rdataset_at(&cached.rrs, i);
-               if (knot_dname_is_equal(knot_rdata_data(rd), qry->zone_cut.ns)) {
-                       knot_rdataset_add(&to_remove, rd, rplan->pool);
-               }
-       }
-       knot_rdataset_subtract(&cached.rrs, &to_remove, rplan->pool);
-       knot_rdataset_clear(&to_remove, rplan->pool);
-
-       /* Remove record(s) */
-       int ret = KNOT_EOK;
-       if (cached.rrs.rr_count == 0) {
-               (void) kr_cache_remove(txn, &cached);
-               ret = KNOT_ENOENT;
-       } else {
-               (void) kr_cache_insert(txn, &cached, qry->timestamp.tv_sec);
-               kr_set_zone_cut(&qry->zone_cut, cached.owner, knot_ns_name(&cached.rrs, 0));
-       }
-
-       knot_rrset_clear(&cached, rplan->pool);
-       return ret;
+       uint8_t *addr = kr_nsrep_inaddr(qry->ns.addr);
+       size_t addr_len = kr_nsrep_inaddr_len(qry->ns.addr);
+       knot_rdata_t rdata[knot_rdata_array_size(addr_len)];
+       knot_rdata_init(rdata, addr_len, addr, 0);
+       return kr_zonecut_del(&qry->zone_cut, qry->ns.name, rdata);
 }
 
 static int ns_resolve_addr(struct kr_query *cur, struct kr_layer_param *param)
 {
-       if (kr_rplan_satisfies(cur, cur->zone_cut.ns, KNOT_CLASS_IN, KNOT_RRTYPE_A) ||
-           kr_rplan_satisfies(cur, cur->zone_cut.ns, KNOT_CLASS_IN, KNOT_RRTYPE_AAAA)) {
+       if (kr_rplan_satisfies(cur, cur->ns.name, KNOT_CLASS_IN, KNOT_RRTYPE_A) ||
+           kr_rplan_satisfies(cur, cur->ns.name, KNOT_CLASS_IN, KNOT_RRTYPE_AAAA) ||
                DEBUG_MSG("=> dependency loop, bailing out\n");
                kr_rplan_pop(param->rplan, cur);
                return KNOT_EOK;
        }
 
-       (void) kr_rplan_push(param->rplan, cur, cur->zone_cut.ns, KNOT_CLASS_IN, KNOT_RRTYPE_AAAA);
-       (void) kr_rplan_push(param->rplan, cur, cur->zone_cut.ns, KNOT_CLASS_IN, KNOT_RRTYPE_A);
+       (void) kr_rplan_push(param->rplan, cur, cur->ns.name, KNOT_CLASS_IN, KNOT_RRTYPE_AAAA);
+       (void) kr_rplan_push(param->rplan, cur, cur->ns.name, KNOT_CLASS_IN, KNOT_RRTYPE_A);
        return KNOT_EOK;
 }
 
@@ -112,9 +78,8 @@ static int iterate(struct knot_requestor *requestor, struct kr_layer_param *para
 
        /* Prepare query resolution. */
        int mode = (cur->flags & QUERY_TCP) ? 0 : KNOT_RQ_UDP;
-       struct sockaddr *ns_addr = (struct sockaddr *)&cur->zone_cut.addr;
        knot_pkt_t *query = knot_pkt_new(NULL, KNOT_WIRE_MIN_PKTSIZE, requestor->mm);
-       struct knot_request *tx = knot_request_make(requestor->mm, ns_addr, NULL, query, mode);
+       struct knot_request *tx = knot_request_make(requestor->mm, &cur->ns.addr.ip, NULL, query, mode);
        knot_requestor_enqueue(requestor, tx);
 
        /* Resolve and check status. */
@@ -128,17 +93,14 @@ static int iterate(struct knot_requestor *requestor, struct kr_layer_param *para
                }
                /* Resolution failed, invalidate current NS and reset to UDP. */
                DEBUG_MSG("=> resolution failed: '%s', invalidating\n", knot_strerror(ret));
-               if (invalidate_ns(rplan, cur) == KNOT_EOK) {
+               if (invalidate_ns(rplan, cur) == 0) {
                        cur->flags &= ~QUERY_TCP;
-               } else {
-                       DEBUG_MSG("=> no ns left to ask\n");
-                       kr_rplan_pop(rplan, cur);
                }
                return KNOT_EOK;
        }
 
        /* Pop query if resolved. */
-       if (cur->resolved) {
+       if (cur->flags & QUERY_RESOLVED) {
                kr_rplan_pop(rplan, cur);
        }
 
index bab21484b73c0fcf056d93d6fe8b28fb03af1f0e..a66b16c1f123a36d60476c45c09d1905ab717bf7 100644 (file)
@@ -19,6 +19,7 @@
 #include <libknot/errcode.h>
 
 #include "lib/rplan.h"
+#include "lib/resolve.h"
 #include "lib/cache.h"
 #include "lib/defines.h"
 #include "lib/layer.h"
@@ -50,6 +51,7 @@ static struct kr_query *query_create(mm_ctx_t *pool, const knot_dname_t *name)
 
 static void query_free(mm_ctx_t *pool, struct kr_query *qry)
 {
+       kr_zonecut_deinit(&qry->zone_cut);
        mm_free(pool, qry->sname);
        mm_free(pool, qry);
 }
@@ -118,7 +120,8 @@ struct kr_query *kr_rplan_push(struct kr_rplan *rplan, struct kr_query *parent,
 
        /* Find closest zone cut for this query. */
        namedb_txn_t *txn = kr_rplan_txn_acquire(rplan, NAMEDB_RDONLY);
-       kr_find_zone_cut(&qry->zone_cut, name, txn, qry->timestamp.tv_sec);
+       kr_zonecut_init(&qry->zone_cut, name, rplan->pool);
+       kr_zonecut_find_cached(&qry->zone_cut, txn, qry->timestamp.tv_sec);
 
 #ifndef NDEBUG
        char name_str[KNOT_DNAME_MAXLEN], type_str[16];
index 74ff9a96fab3e4d65b8d607becfddb66f52c5260..e780f28d11ed49da257893b75c3ee16080aa9dc2 100644 (file)
@@ -29,6 +29,7 @@
 
 #include "lib/cache.h"
 #include "lib/zonecut.h"
+#include "lib/nsrep.h"
 
 /** Query flags */
 enum kr_query_flag {
@@ -42,6 +43,7 @@ enum kr_query_flag {
 struct kr_query {
        node_t node;
        struct kr_query *parent;
+       struct kr_nsrep ns;
        struct kr_zonecut zone_cut;
        struct timeval timestamp;
        knot_dname_t *sname;
index 2f9e12d5e23d9ef43a9a9f91c516e5150f4963f1..5e229e6dda5991a96cd45755f4030d4c313c5b10 100644 (file)
 #include "lib/rplan.h"
 #include "lib/defines.h"
 #include "lib/layer.h"
+#include "lib/generic/pack.h"
 
 /* Root hint descriptor. */
 struct hint_info {
        const knot_dname_t *name;
-       const char *addr;
+       const uint8_t *addr;
 };
 
 /* Initialize with SBELT name servers. */
 #define U8(x) (const uint8_t *)(x)
 #define HINT_COUNT 13
+#define HINT_ADDRLEN sizeof(struct in_addr)
 static const struct hint_info SBELT[HINT_COUNT] = {
-        { U8("\x01""a""\x0c""root-servers""\x03""net"), "198.41.0.4" },
-        { U8("\x01""b""\x0c""root-servers""\x03""net"), "192.228.79.201" },
-        { U8("\x01""c""\x0c""root-servers""\x03""net"), "192.33.4.12" },
-        { U8("\x01""d""\x0c""root-servers""\x03""net"), "199.7.91.13" },
-        { U8("\x01""e""\x0c""root-servers""\x03""net"), "192.203.230.10" },
-        { U8("\x01""f""\x0c""root-servers""\x03""net"), "192.5.5.241" },
-        { U8("\x01""g""\x0c""root-servers""\x03""net"), "192.112.36.4" },
-        { U8("\x01""h""\x0c""root-servers""\x03""net"), "128.63.2.53" },
-        { U8("\x01""i""\x0c""root-servers""\x03""net"), "192.36.148.17" },
-        { U8("\x01""j""\x0c""root-servers""\x03""net"), "192.58.128.30" },
-        { U8("\x01""k""\x0c""root-servers""\x03""net"), "193.0.14.129" },
-        { U8("\x01""l""\x0c""root-servers""\x03""net"), "199.7.83.42" },
-        { U8("\x01""m""\x0c""root-servers""\x03""net"), "202.12.27.33" }
+        { U8("\x01""a""\x0c""root-servers""\x03""net"), U8("\xc6)\x00\x04")    }, /* 198.41.0.4 */
+        { U8("\x01""b""\x0c""root-servers""\x03""net"), U8("\xc0\xe4O\xc9")    }, /* 192.228.79.201 */
+        { U8("\x01""c""\x0c""root-servers""\x03""net"), U8("\xc6)\x00\x04")    }, /* 192.33.4.12 */
+        { U8("\x01""d""\x0c""root-servers""\x03""net"), U8("\xc7\x07[\r")      }, /* 199.7.91.13 */
+        { U8("\x01""e""\x0c""root-servers""\x03""net"), U8("\xc0\xcb\xe6\n")   }, /* 192.203.230.10 */
+        { U8("\x01""f""\x0c""root-servers""\x03""net"), U8("\xc0\x05\x05\xf1") }, /* 192.5.5.241 */
+        { U8("\x01""g""\x0c""root-servers""\x03""net"), U8("\xc0p$\x04")       }, /* 192.112.36.4 */
+        { U8("\x01""h""\x0c""root-servers""\x03""net"), U8("\x80?\x025")       }, /* 128.63.2.53 */
+        { U8("\x01""i""\x0c""root-servers""\x03""net"), U8("\xc0$\x94\x11")    }, /* 192.36.148.17 */
+        { U8("\x01""j""\x0c""root-servers""\x03""net"), U8("\xc0:\x80\x1e")    }, /* 192.58.128.30 */
+        { U8("\x01""k""\x0c""root-servers""\x03""net"), U8("\xc1\x00\x0e\x81") }, /* 193.0.14.129 */
+        { U8("\x01""l""\x0c""root-servers""\x03""net"), U8("\xc7\x07S*")       }, /* 199.7.83.42 */
+        { U8("\x01""m""\x0c""root-servers""\x03""net"), U8("\xca\x0c\x1b!")    }, /* 202.12.27.33 */
 };
 
-int kr_init_zone_cut(struct kr_zonecut *cut)
+static void update_cut_name(struct kr_zonecut *cut, const knot_dname_t *name)
+{
+       if (knot_dname_is_equal(name, cut->name)) {
+               return;
+       }
+       knot_dname_t *next_name = knot_dname_copy(name, cut->pool);
+       mm_free(cut->pool, cut->name);
+       cut->name = next_name;
+}
+
+int kr_zonecut_init(struct kr_zonecut *cut, const knot_dname_t *name, mm_ctx_t *pool)
 {
        if (cut == NULL) {
-               return KNOT_EINVAL;
+               return kr_error(EINVAL);
        }
 
-       const unsigned hint_id = dnssec_random_uint16_t() % HINT_COUNT;
-       const struct hint_info *hint = &SBELT[hint_id];
+       cut->name = knot_dname_copy(name, pool);
+       cut->pool = pool;
+       cut->nsset = map_make();
+       cut->nsset.malloc = (map_alloc_f) mm_alloc;
+       cut->nsset.free = (map_free_f) mm_free;
+       cut->nsset.baton = pool;
+       return kr_ok();
+}
+
+static int free_addr_set(const char *k, void *v, void *baton)
+{
+       pack_t *pack = v;
+       pack_clear_mm(*pack, mm_free, baton);
+       mm_free(baton, pack);
+       return kr_ok();
+}
 
-       kr_set_zone_cut(cut, U8(""), hint->name);
+void kr_zonecut_deinit(struct kr_zonecut *cut)
+{
+       if (cut == NULL) {
+               return;
+       }
+       mm_free(cut->pool, cut->name);
+       map_walk(&cut->nsset, free_addr_set, cut->pool);
+       map_clear(&cut->nsset);
+}
 
-       /* Prefetch address. */
-       return sockaddr_set(&cut->addr, AF_INET, hint->addr, 53);
+void kr_zonecut_set(struct kr_zonecut *cut, const knot_dname_t *name)
+{
+       if (cut == NULL) {
+               return;
+       }
+       kr_zonecut_deinit(cut);
+       kr_zonecut_init(cut, name, cut->pool);
 }
 
-int kr_set_zone_cut(struct kr_zonecut *cut, const knot_dname_t *name, const knot_dname_t *ns)
+int kr_zonecut_add(struct kr_zonecut *cut, const knot_dname_t *ns, const knot_rdata_t *rdata)
 {
-       if (cut == NULL || name == NULL) {
-               return KNOT_EINVAL;
+       if (cut == NULL || ns == NULL) {
+               return kr_error(EINVAL);
        }
 
-       /* Set current NS and zone cut. */
-       knot_dname_to_wire(cut->name, name, KNOT_DNAME_MAXLEN);
-       knot_dname_to_wire(cut->ns, ns, KNOT_DNAME_MAXLEN);
+       /* Fetch/insert nameserver. */
+       const char *key = (const char *)ns;
+       pack_t *pack = map_get(&cut->nsset, key);
+       if (pack == NULL) {
+               pack = mm_alloc(cut->pool, sizeof(*pack));
+               if (!pack || (map_set(&cut->nsset, key, pack) != 0)) {
+                       mm_free(cut->pool, pack);
+                       return kr_error(ENOMEM);
+               }
+               pack_init(*pack);
+       }
 
-       /* Invalidate address. */
-       cut->addr.ss_family = AF_UNSPEC;
+       /* Insert data (if has any) */
+       if (rdata == NULL) {
+               return kr_ok();
+       }
+       uint16_t rdlen = knot_rdata_rdlen(rdata);
+       int ret = pack_reserve(*pack, 1, rdlen);
+       if (ret != 0) {
+               return kr_error(ENOMEM);
+       }
 
-       return KNOT_EOK;
+       return pack_obj_push(pack, knot_rdata_data(rdata), rdlen);
 }
 
-int kr_set_zone_cut_addr(struct kr_zonecut *cut, const knot_rrset_t *rr, uint16_t i)
+int kr_zonecut_del(struct kr_zonecut *cut, const knot_dname_t *ns, const knot_rdata_t *rdata)
 {
-       int ret = KNOT_EOK;
+       if (cut == NULL || ns == NULL) {
+               return kr_error(EINVAL);
+       }
 
-       switch(rr->type) {
-       case KNOT_RRTYPE_A:
-               ret = knot_a_addr(&rr->rrs, i, (struct sockaddr_in *)&cut->addr);
-               break;
-       case KNOT_RRTYPE_AAAA:
-               ret = knot_aaaa_addr(&rr->rrs, i, (struct sockaddr_in6 *)&cut->addr);
-               break;
-       default:
-               return KNOT_EINVAL;
+       /* Find the address list. */
+       const char *key = (const char *)ns;
+       map_t *nsset = &cut->nsset;
+       pack_t *pack = map_get(nsset, key);
+       if (pack == NULL) {
+               return kr_error(ENOENT);
        }
 
-       sockaddr_port_set(&cut->addr, KR_DNS_PORT);
+       /* Remove address from the pack. */
+       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);
+       }
 
        return ret;
 }
 
+int kr_zonecut_set_sbelt(struct kr_zonecut *cut)
+{
+       if (cut == NULL) {
+               return kr_error(EINVAL);
+       }
+
+       update_cut_name(cut, U8(""));
+       for (unsigned i = 0; i < HINT_COUNT; ++i) {
+               const struct hint_info *hint = &SBELT[i];
+               knot_rdata_t rdata[knot_rdata_array_size(HINT_ADDRLEN)];
+               knot_rdata_init(rdata, HINT_ADDRLEN, hint->addr, 0);
+               int ret = kr_zonecut_add(cut, hint->name, rdata);
+               if (ret != 0) {
+                       return ret;
+               }
+       }
+
+       return kr_ok();
+}
+
 /** Fetch address for zone cut. */
-static int fetch_addr(struct kr_zonecut *cut, namedb_txn_t *txn, uint32_t timestamp)
+static void fetch_addr(struct kr_zonecut *cut, const knot_dname_t *ns, uint16_t rrtype, namedb_txn_t *txn, uint32_t timestamp)
 {
-       /* Fetch nameserver address from cache. */
        knot_rrset_t cached_rr;
-       knot_rrset_init(&cached_rr, cut->ns, 0, KNOT_CLASS_IN);
-       cached_rr.type = KNOT_RRTYPE_A;
-       if (kr_cache_peek(txn, &cached_rr, &timestamp) != KNOT_EOK) {
-               cached_rr.type = KNOT_RRTYPE_AAAA;
-               if (kr_cache_peek(txn, &cached_rr, &timestamp) != KNOT_EOK) {
-                       return KNOT_ENOENT;
-               }
+       knot_rrset_init(&cached_rr, (knot_dname_t *)ns, rrtype, KNOT_CLASS_IN);
+       if (kr_cache_peek(txn, &cached_rr, &timestamp) != 0) {
+               return;
        }
-       /* Find first valid record. */
-       uint16_t i = 0;
-       for (; i < cached_rr.rrs.rr_count; ++i) {
+
+       for (uint16_t i = 0; i < cached_rr.rrs.rr_count; ++i) {
                knot_rdata_t *rd = knot_rdataset_at(&cached_rr.rrs, i);
                if (knot_rdata_ttl(rd) > timestamp) {
-                       break;
+                       (void) kr_zonecut_add(cut, ns, rd);
                }
        }
-
-       return kr_set_zone_cut_addr(cut, &cached_rr, i);
 }
 
 /** Fetch best NS for zone cut. */
@@ -134,37 +205,33 @@ static int fetch_ns(struct kr_zonecut *cut, const knot_dname_t *name, namedb_txn
        knot_rrset_t cached_rr;
        knot_rrset_init(&cached_rr, (knot_dname_t *)name, KNOT_RRTYPE_NS, KNOT_CLASS_IN);
        int ret = kr_cache_peek(txn, &cached_rr, &drift);
-       if (ret != KNOT_EOK) {
+       if (ret != 0) {
                return ret;
        }
 
-       /* Accept only if has address records cached. */
+       /* Fetch address records for this nameserver */
        for (unsigned i = 0; i < cached_rr.rrs.rr_count; ++i) {
-               kr_set_zone_cut(cut, name, knot_ns_name(&cached_rr.rrs, i));
-               ret = fetch_addr(cut, txn, timestamp);
-               if (ret == KNOT_EOK) {
-                       break;
-               }
+               const knot_dname_t *ns_name = knot_ns_name(&cached_rr.rrs, i);
+               kr_zonecut_add(cut, ns_name, NULL);
+               fetch_addr(cut, ns_name, KNOT_RRTYPE_A, txn, timestamp);
+               fetch_addr(cut, ns_name, KNOT_RRTYPE_AAAA, txn, timestamp);
        }
 
-       return ret;
+       return kr_ok();
 }
 
-int kr_find_zone_cut(struct kr_zonecut *cut, const knot_dname_t *name, namedb_txn_t *txn, uint32_t timestamp)
+int kr_zonecut_find_cached(struct kr_zonecut *cut, namedb_txn_t *txn, uint32_t timestamp)
 {
-       if (cut == NULL || name == NULL) {
-               return KNOT_EINVAL;
-       }
-
-       /* No cache, start with SBELT. */
-       if (txn == NULL) {
-               return kr_init_zone_cut(cut);
+       if (cut == NULL) {
+               return kr_error(EINVAL);
        }
 
        /* Start at QNAME. */
-       while (true) {
-               if (fetch_ns(cut, name, txn, timestamp) == KNOT_EOK) {
-                       return KNOT_EOK;
+       const knot_dname_t *name = cut->name;
+       while (txn) {
+               if (fetch_ns(cut, name, txn, timestamp) == 0) {
+                       update_cut_name(cut, name);
+                       return kr_ok();
                }
                /* Subtract label from QNAME. */
                if (name[0] == '\0') {
@@ -174,5 +241,5 @@ int kr_find_zone_cut(struct kr_zonecut *cut, const knot_dname_t *name, namedb_tx
        }
 
        /* Name server not found, start with SBELT. */
-       return kr_init_zone_cut(cut);
+       return kr_zonecut_set_sbelt(cut);
 }
index 0af033a6f06bc4fa840b7a8c9293090d5f8c61ed..d7e4163d8e61123e28ab10f4d829449b922f75a5 100644 (file)
 
 #pragma once
 
-#include <libknot/dname.h>
-#include <libknot/rrset.h>
-#include <libknot/internal/sockaddr.h>
-#include <libknot/internal/namedb/namedb.h>
+#include "lib/cache.h"
+#include "lib/generic/map.h"
 
 struct kr_rplan;
 
@@ -31,45 +29,73 @@ struct kr_rplan;
  * Current zone cut representation.
 */
 struct kr_zonecut {
-       knot_dname_t name[KNOT_DNAME_MAXLEN]; /**< Current zone cut */
-       knot_dname_t ns[KNOT_DNAME_MAXLEN];   /**< Authoritative NS */
-       struct sockaddr_storage addr;         /**< Authoritative NS address. */
+       knot_dname_t *name; /**< Zone cut name. */
+    mm_ctx_t *pool;     /**< Memory pool. */
+    map_t nsset;        /**< Map of nameserver => address_set. */
 };
 
 /**
- * Initialize zone cut with SBELT.
- * @param cut zone cut to be set
- * @return KNOT_E*
+ * Populate root zone cut with SBELT.
+ * @param cut zone cut
+ * @param name
+ * @param pool
+ * @return 0 or error code
  */
-int kr_init_zone_cut(struct kr_zonecut *cut);
+int kr_zonecut_init(struct kr_zonecut *cut, const knot_dname_t *name, mm_ctx_t *pool);
 
 /**
- * Set zone cut to given name and name server.
- * @note Name server address is blanked.
- * @param cut zone cut to be set
- * @param name zone cut name
- * @param ns   zone cut nameserver
- * @return KNOT_E*
+ * Clear the structure and free the address set.
+ * @param cut zone cut
  */
-int kr_set_zone_cut(struct kr_zonecut *cut, const knot_dname_t *name, const knot_dname_t *ns);
+void kr_zonecut_deinit(struct kr_zonecut *cut);
 
 /**
- * Convert A/AAAA RRs to address with DNS port.
- * @param cut zone cut to be set
- * @param rr resource record
- * @param i  index of the set address in the rr
- * @return KNOT_E*
+ * 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
  */
-int kr_set_zone_cut_addr(struct kr_zonecut *cut, const knot_rrset_t *rr, uint16_t i);
+void kr_zonecut_set(struct kr_zonecut *cut, const knot_dname_t *name);
 
 /**
- * Find the closest enclosing zone cut/nameserver from the cache.
- * @param cut zone cut to be set
- * @param name zone cut name
- * @param txn cache transaction
+ * Add address record to the zone cut.
+ *
+ * The record will be merged with existing data,
+ * it may be either A/AAAA type.
+ *
+ * @param cut    zone cut to be populated
+ * @param ns     nameserver name
+ * @param rdata  nameserver address (as rdata)
+ * @return 0 or error code
+ */
+int kr_zonecut_add(struct kr_zonecut *cut, const knot_dname_t *ns, const knot_rdata_t *rdata);
+
+/**
+ * Delete nameserver/address pair from the zone cut.
+ * @param  cut
+ * @param  ns    name server name
+ * @param  rdata name server address
+ * @return       0 or error code
+ */
+int kr_zonecut_del(struct kr_zonecut *cut, const knot_dname_t *ns, const knot_rdata_t *rdata);
+
+/**
+ * Populate zone cut with a root zone using SBELT :rfc:`1034`
+ *
+ * @param cut zone cut to be populated
+ * @return 0 or error code
+ */
+int kr_zonecut_set_sbelt(struct kr_zonecut *cut);
+
+/**
+ * Populate zone cut address set from cache.
+ * 
+ * @param cut       zone cut to be populated
+ * @param txn       cache transaction (read)
  * @param timestamp transaction timestamp
- * @return KNOT_E*
+ * @return 0 or error code
  */
-int kr_find_zone_cut(struct kr_zonecut *cut, const knot_dname_t *name, namedb_txn_t *txn, uint32_t timestamp);
+int kr_zonecut_find_cached(struct kr_zonecut *cut, namedb_txn_t *txn, uint32_t timestamp);
 
 /** @} */