]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
lib/resolve: produce() layers can generate answer, not just query
authorMarek Vavruša <marek.vavrusa@nic.cz>
Sun, 3 May 2015 18:21:17 +0000 (20:21 +0200)
committerMarek Vavruša <marek.vavrusa@nic.cz>
Sun, 3 May 2015 18:21:17 +0000 (20:21 +0200)
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

lib/layer/iterate.c
lib/layer/iterate.h
lib/layer/itercache.c
lib/resolve.c
lib/rplan.h
modules/hints/hints.c
tests/testdata/iter_cname_qnamecopy.rpl

index 7e182b95274035aaf2f93175dc765b91b526f16a..9b69beb57f704bab2ced6f00ab20beb10bcacb9e 100644 (file)
 #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");
index db7de8bc1df5da3341042352de2ca569ff6092ef..4ae49a217c2308b15e097c027e4e019efb29b4f7 100644 (file)
 /* 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
index bbd588a885f4f5b5bc7963e7b4a1ce6ac7d0a427..f1283ab4d32b6b6ac56353ecd2aa58d38339500a 100644 (file)
 #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;
index 285e50817efaadcec1451042a0c338d8f1a3b2f4..d3bb967f30792b7ff6972266052c4dcc3a5a67bb 100644 (file)
@@ -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);
index c21d75d298363db71ee9f1b215f5100b736c944b..35c6c99c94e3e18908963253339a77c7dfbd50d9 100644 (file)
@@ -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. */
 };
 
 /**
index ed4e931cc795938c654640ae00a2010872ce6c67..8a0b2a9cf0a2efb6deb74d6de1539bd507d86b2d 100644 (file)
@@ -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)
index 9539d34ced4b1ea6c7e3a368264cd57a77005e7e..12019816ac16d2e2095b586cd9e3e40c2bf620bb 100644 (file)
@@ -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