Commit 87fffcd3 authored by Marek Vavruša's avatar Marek Vavruša Committed by Marek Vavruša

lib: implemented new zonecut interface

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
parent 1a255568
......@@ -23,8 +23,10 @@
#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.
......
......@@ -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;
......
......@@ -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);
......
......@@ -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"
......@@ -32,61 +32,27 @@
/* 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);
}
......
......@@ -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];
......
......@@ -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;
......
......@@ -25,106 +25,177 @@
#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);
}
......@@ -20,10 +20,8 @@
#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);