Commit 5e79d7d6 authored by Marek Vavruša's avatar Marek Vavruša

lib/resolve: produce() layers can generate answer, not just query

previously producers in overlay could only generate query or
update the final answer - this required exported callbacks
from iterate.c and specific processing. this wouldn’t work
for negative cache, as it would be required to reimplement
whole iterate layer for cached data

the new workflow allows produce layers to generate answer
instead, this answer is then consumed in the next step
by the iterate module in unified fashion for all caches/generators
parent 70a2a046
......@@ -30,14 +30,6 @@
#define SEED_SIZE 256
#define DEBUG_MSG(fmt...) QRDEBUG(kr_rplan_current(&req->rplan), "iter", fmt)
/* Packet classification. */
enum {
PKT_NOERROR = 1 << 0, /* Positive response */
PKT_NODATA = 1 << 1, /* No data response */
PKT_NXDOMAIN = 1 << 2, /* Negative response */
PKT_ERROR = 1 << 3 /* Refused or server failure */
};
/* Iterator often walks through packet section, this is an abstraction. */
typedef int (*rr_callback_t)(const knot_rrset_t *, unsigned, struct kr_request *);
......@@ -108,8 +100,7 @@ static bool is_authoritative(const knot_pkt_t *answer, struct kr_query *query)
return false;
}
/** Return response class. */
static int response_classify(knot_pkt_t *pkt)
int kr_response_classify(knot_pkt_t *pkt)
{
const knot_pktsection_t *an = knot_pkt_section(pkt, KNOT_ANSWER);
switch (knot_wire_get_rcode(pkt->wire)) {
......@@ -287,7 +278,7 @@ static int process_answer(knot_pkt_t *pkt, struct kr_request *req)
* NXDOMAIN => parent is zone cut, retry as a workaround for bad authoritatives
*/
bool is_final = (query->parent == NULL);
int pkt_class = response_classify(pkt);
int pkt_class = kr_response_classify(pkt);
if (!knot_dname_is_equal(knot_pkt_qname(pkt), query->sname) &&
(pkt_class & (PKT_NOERROR|PKT_NXDOMAIN|PKT_NODATA))) {
DEBUG_MSG("<= found cut, retrying with non-minimized name\n");
......
......@@ -22,17 +22,16 @@
/* Processing module implementation. */
extern const knot_layer_api_t *iterate_layer(void);
/**
* Result updates the query parent.
* @note Hint is an index of chosen RR in the set.
*/
int rr_update_parent(const knot_rrset_t *rr, unsigned hint, struct kr_request *param);
/**
* Result updates the original query response.
* @note When \a hint is KNOT_PF_FREE, RR is treated as a copy and answer takes its ownership.
*/
int rr_update_answer(const knot_rrset_t *rr, unsigned hint, struct kr_request *param);
/* Packet classification. */
enum {
PKT_NOERROR = 1 << 0, /* Positive response */
PKT_NODATA = 1 << 1, /* No data response */
PKT_NXDOMAIN = 1 << 2, /* Negative response */
PKT_ERROR = 1 << 3 /* Refused or server failure */
};
/** Classify response by type. */
int kr_response_classify(knot_pkt_t *pkt);
/* Processing module implementation. */
const knot_layer_api_t *iterate_layer(void);
\ No newline at end of file
......@@ -24,61 +24,53 @@
#include "lib/cache.h"
#include "lib/module.h"
#define DEBUG_MSG(fmt...) QRDEBUG(kr_rplan_current(rplan), " cc ", fmt)
#define DEBUG_MSG(fmt...) QRDEBUG(kr_rplan_current(rplan), " rc ", fmt)
typedef int (*rr_callback_t)(const knot_rrset_t *, unsigned, struct kr_request *);
static int update_parent(const knot_rrset_t *rr, unsigned drift, struct kr_request *req)
static int begin(knot_layer_t *ctx, void *module_param)
{
/* Find a first non-expired record. */
uint16_t i = 0;
for (; i < rr->rrs.rr_count; ++i) {
knot_rdata_t *rd = knot_rdataset_at(&rr->rrs, i);
if (knot_rdata_ttl(rd) > drift) {
break;
}
}
return rr_update_parent(rr, i, req);
ctx->data = module_param;
return ctx->state;
}
static int update_answer(const knot_rrset_t *rr, unsigned drift, struct kr_request *req)
static int loot_rr(namedb_txn_t *txn, knot_pkt_t *pkt, const knot_dname_t *name,
uint16_t rrtype, uint16_t rrclass, uint32_t timestamp)
{
knot_pkt_t *answer = req->answer;
/* Materialize RR set */
knot_rrset_t rr_copy = kr_cache_materialize(rr, drift, &answer->mm);
knot_rrset_t cache_rr;
knot_rrset_init(&cache_rr, (knot_dname_t *)name, rrtype, rrclass);
int ret = kr_cache_peek_rr(txn, &cache_rr, &timestamp);
if (ret != 0) {
return ret;
}
knot_rrset_t rr_copy = kr_cache_materialize(&cache_rr, timestamp, &pkt->mm);
if (rr_copy.rrs.rr_count == 0) {
return KNOT_STATE_FAIL;
return kr_error(ENOENT);
}
return rr_update_answer(&rr_copy, 0, req);
}
static int read_cache_rr(namedb_txn_t *txn, knot_rrset_t *cache_rr, uint32_t timestamp,
rr_callback_t cb, struct kr_request *req)
{
/* Query cache for requested record */
if (kr_cache_peek_rr(txn, cache_rr, &timestamp) != KNOT_EOK) {
return KNOT_STATE_NOOP;
ret = knot_pkt_put(pkt, KNOT_COMPR_HINT_NONE, &rr_copy, KNOT_PF_FREE);
if (ret != 0) {
knot_rrset_clear(&rr_copy, &pkt->mm);
return ret;
}
return cb(cache_rr, timestamp, req);
return kr_ok();
}
static int begin(knot_layer_t *ctx, void *module_param)
static int loot_cache(namedb_txn_t *txn, knot_pkt_t *pkt, uint32_t timestamp)
{
ctx->data = module_param;
return ctx->state;
const knot_dname_t *qname = knot_pkt_qname(pkt);
uint16_t rrclass = knot_pkt_qclass(pkt);
uint16_t rrtype = knot_pkt_qtype(pkt);
int ret = loot_rr(txn, pkt, qname, rrtype, rrclass, timestamp);
if (ret == kr_error(ENOENT) && rrtype != KNOT_RRTYPE_CNAME) { /* Chase CNAME if no direct hit */
ret = loot_rr(txn, pkt, qname, KNOT_RRTYPE_CNAME, rrclass, timestamp);
}
return ret;
}
static int read_cache(knot_layer_t *ctx, knot_pkt_t *pkt)
static int peek(knot_layer_t *ctx, knot_pkt_t *pkt)
{
assert(pkt && ctx);
struct kr_request *req = ctx->data;
struct kr_rplan *rplan = &req->rplan;
struct kr_query *cur = kr_rplan_current(rplan);
if (cur == NULL) {
struct kr_query *qry = kr_rplan_current(rplan);
if (!qry || ctx->state & (KNOT_STATE_FAIL|KNOT_STATE_DONE)) {
return ctx->state;
}
......@@ -87,45 +79,21 @@ static int read_cache(knot_layer_t *ctx, knot_pkt_t *pkt)
if (kr_cache_txn_begin(cache, &txn, NAMEDB_RDONLY) != 0) {
return KNOT_STATE_CONSUME;
}
uint32_t timestamp = cur->timestamp.tv_sec;
knot_rrset_t cache_rr;
knot_rrset_init(&cache_rr, cur->sname, cur->stype, cur->sclass);
/* Check if updating parent zone cut. */
rr_callback_t callback = &update_parent;
if (cur->parent == NULL) {
callback = &update_answer;
}
/* Try to find expected record first. */
int state = read_cache_rr(&txn, &cache_rr, timestamp, callback, req);
if (state == KNOT_STATE_DONE) {
/* Reconstruct the answer from the cache,
* it may either be a CNAME chain or direct answer.
* Only one step of the chain is resolved at a time.
*/
uint32_t timestamp = qry->timestamp.tv_sec;
int ret = loot_cache(&txn, pkt, timestamp);
kr_cache_txn_abort(&txn);
if (ret == 0) {
DEBUG_MSG("=> satisfied from cache\n");
cur->flags |= QUERY_RESOLVED;
kr_cache_txn_abort(&txn);
return state;
}
/* Check if CNAME chain exists. */
cache_rr.type = KNOT_RRTYPE_CNAME;
state = read_cache_rr(&txn, &cache_rr, timestamp, callback, req);
if (state != KNOT_STATE_NOOP) {
if (cur->stype != KNOT_RRTYPE_CNAME) {
const knot_dname_t *cname = knot_cname_name(&cache_rr.rrs);
if (kr_rplan_push(rplan, cur->parent, cname, cur->sclass, cur->stype) == NULL) {
kr_cache_txn_abort(&txn);
return KNOT_STATE_FAIL;
}
}
cur->flags |= QUERY_RESOLVED;
kr_cache_txn_abort(&txn);
qry->flags |= QUERY_CACHED;
knot_wire_set_qr(pkt->wire);
return KNOT_STATE_DONE;
}
/* Not resolved. */
kr_cache_txn_abort(&txn);
return KNOT_STATE_CONSUME;
return ctx->state;
}
/** Merge-in record if same type and owner. */
......@@ -236,20 +204,17 @@ static int write_cache_authority(knot_pkt_t *pkt, namedb_txn_t *txn, mm_ctx_t *p
return write_cache_rr(ns, &cache_rr, txn, pool, timestamp);
}
static int write_cache(knot_layer_t *ctx, knot_pkt_t *pkt)
static int stash(knot_layer_t *ctx, knot_pkt_t *pkt)
{
struct kr_request *req = ctx->data;
struct kr_rplan *rplan = &req->rplan;
struct kr_query *query = kr_rplan_current(rplan);
/* Don't cache anything if failed. */
if (query == NULL || ctx->state == KNOT_STATE_FAIL) {
if (!query || ctx->state & KNOT_STATE_FAIL) {
return ctx->state;
}
/* Cache only positive answers. */
/** \todo Negative answers cache support */
if (knot_wire_get_rcode(pkt->wire) != KNOT_RCODE_NOERROR) {
if (query->flags & QUERY_CACHED || knot_wire_get_rcode(pkt->wire) != KNOT_RCODE_NOERROR) {
return ctx->state;
}
......@@ -286,8 +251,8 @@ const knot_layer_api_t *itercache_layer(void)
{
static const knot_layer_api_t _layer = {
.begin = &begin,
.consume = &write_cache,
.produce = &read_cache
.produce = &peek,
.consume = &stash
};
return &_layer;
......
......@@ -333,7 +333,6 @@ int kr_resolve_consume(struct kr_request *request, knot_pkt_t *packet)
/* Pop query if resolved. */
if (qry->flags & QUERY_RESOLVED) {
kr_rplan_pop(rplan, qry);
}
knot_overlay_reset(&request->overlay);
......@@ -359,9 +358,15 @@ int kr_resolve_produce(struct kr_request *request, struct sockaddr **dst, int *t
/* Resolve current query and produce dependent or finish */
int state = knot_overlay_produce(&request->overlay, packet);
if (state != KNOT_STATE_FAIL && knot_wire_get_qr(packet->wire)) {
/* Produced an answer, consume it. */
request->overlay.state = KNOT_STATE_CONSUME;
state = knot_overlay_consume(&request->overlay, packet);
}
switch(state) {
case KNOT_STATE_FAIL: return state; break;
case KNOT_STATE_CONSUME: break;
case KNOT_STATE_DONE:
default: /* Current query is done */
knot_overlay_reset(&request->overlay);
kr_rplan_pop(rplan, qry);
......
......@@ -32,7 +32,8 @@ enum kr_query_flag {
QUERY_TCP = 1 << 1, /**< Use TCP for this query. */
QUERY_RESOLVED = 1 << 2, /**< Query is resolved. */
QUERY_AWAIT_ADDR = 1 << 3, /**< Query is waiting for NS address. */
QUERY_SAFEMODE = 1 << 4 /**< Don't use fancy stuff (EDNS...) */
QUERY_SAFEMODE = 1 << 4, /**< Don't use fancy stuff (EDNS...) */
QUERY_CACHED = 1 << 5 /**< Query response is cached. */
};
/**
......
......@@ -35,7 +35,6 @@
/* Defaults */
#define DEFAULT_FILE "/etc/hosts"
#define DEBUG_MSG(qry, fmt...) QRDEBUG(qry, "hint", fmt)
typedef int (*rr_callback_t)(const knot_rrset_t *, unsigned, struct kr_request *);
/** @todo Hack until layers can store userdata. */
static struct kr_zonecut *g_map = NULL;
......@@ -46,41 +45,35 @@ static int begin(knot_layer_t *ctx, void *module_param)
return ctx->state;
}
static int answer_query(pack_t *addr_set, struct kr_request *param)
static int answer_query(knot_pkt_t *pkt, pack_t *addr_set, struct kr_request *param)
{
struct kr_query *qry = kr_rplan_current(&param->rplan);
assert(qry);
knot_dname_t *qname = knot_dname_copy(knot_pkt_qname(pkt), &pkt->mm);
uint16_t rrtype = knot_pkt_qtype(pkt);
uint16_t rrclass = knot_pkt_qclass(pkt);
knot_rrset_t rr;
knot_rrset_init(&rr, qry->sname, qry->stype, KNOT_CLASS_IN);
knot_rrset_init(&rr, qname, rrtype, rrclass);
int family_len = sizeof(struct in_addr);
if (rr.type == KNOT_RRTYPE_AAAA) {
family_len = sizeof(struct in6_addr);
}
/* Update addresses */
/* Append address records from hints */
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);
knot_rrset_add_rdata(&rr, addr_val, len, 0, &pkt->mm);
}
addr = pack_obj_next(addr);
}
/* Process callbacks */
rr_callback_t callback = &rr_update_parent;
if (!qry->parent) {
callback = &rr_update_answer;
/* Append to packet */
int ret = knot_pkt_put(pkt, KNOT_COMPR_HINT_NONE, &rr, KNOT_PF_FREE);
if (ret != 0) {
knot_rrset_clear(&rr, &pkt->mm);
}
callback(&rr, 0, param);
/* Finalize */
DEBUG_MSG(qry, "<= answered from hints\n");
knot_rdataset_clear(&rr.rrs, NULL);
qry->flags |= QUERY_RESOLVED;
return KNOT_STATE_DONE;
return ret;
}
static int query(knot_layer_t *ctx, knot_pkt_t *pkt)
......@@ -88,6 +81,9 @@ static int query(knot_layer_t *ctx, knot_pkt_t *pkt)
assert(pkt && ctx);
struct kr_request *param = ctx->data;
struct kr_query *qry = kr_rplan_current(&param->rplan);
if (!qry || ctx->state & (KNOT_STATE_DONE|KNOT_STATE_FAIL)) {
return ctx->state;
}
if (qry->stype != KNOT_RRTYPE_A && qry->stype != KNOT_RRTYPE_AAAA) {
return ctx->state;
}
......@@ -98,7 +94,15 @@ static int query(knot_layer_t *ctx, knot_pkt_t *pkt)
return ctx->state;
}
return answer_query(pack, param);
/* Write to packet */
int ret = answer_query(pkt, pack, param);
if (ret != 0) {
return ctx->state;
}
DEBUG_MSG(qry, "<= answered from hints\n");
qry->flags |= QUERY_CACHED;
knot_wire_set_qr(pkt->wire);
return KNOT_STATE_DONE;
}
static int parse_addr_str(struct sockaddr_storage *sa, const char *addr)
......
......@@ -171,7 +171,7 @@ www.example.com. IN A
SECTION ANSWER
www.example.com. IN CNAME www.next.com.
SECTION AUTHORITY
;next.com. IN SOA next.com. next.com. 2007090400 28800 7200 604800 18000
next.com. IN SOA next.com. next.com. 2007090400 28800 7200 604800 18000
SECTION ADDITIONAL
ENTRY_END
......
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