]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
.
authorVladimír Čunát <vladimir.cunat@nic.cz>
Wed, 27 Sep 2017 16:44:15 +0000 (18:44 +0200)
committerVladimír Čunát <vladimir.cunat@nic.cz>
Wed, 27 Sep 2017 16:44:15 +0000 (18:44 +0200)
lib/cache.c
lib/cache.h
lib/cdb.h
lib/cdb_lmdb.c

index 77ae4d3634f2340c5d574affdd97beeaba0aeab0..5de459a3cc86d87984dd7ce2b32d66310049c293 100644 (file)
@@ -481,55 +481,151 @@ int kr_cache_insert_rrsig(struct kr_cache *cache, const knot_rrset_t *rr, uint8_
 
 #include "lib/rplan.h"
 
+
+/** Cache entry header */
+struct entry_h {
+       uint32_t time;  /**< The time of inception. */
+       uint32_t ttl;   /**< TTL at inception moment. */
+       uint8_t  rank;  /**< See enum kr_rank */
+       uint8_t  flags; /**< TODO */
+       uint8_t data[];
+};
+
+
+struct key {
+       const knot_dname_t *dname; /**< corresponding dname (points within qry->sname) */
+       uint8_t name_len; /**< current length of the name in buf */
+       uint8_t buf[KR_CACHE_KEY_MAXLEN];
+};
+
+static int closest_NS(struct kr_cache *cache, struct key *k);
+
+/** TODO */
+static knot_db_val_t key_exact_type(struct key *k, uint16_t ktype)
+{
+       k->buf[k->name_len + 1] = 0; /* make sure different names can never match */
+       k->buf[k->name_len + 2] = 'E'; /* tag for exact name+type matches */
+       memcpy(k->buf + k->name_len + 3, &ktype, 2);
+       /* key == dname_lf + '0' + 'E' + RRTYPE */
+       return (knot_db_val_t){ k->buf + 1, k->name_len + 4 };
+}
+
 int read_lmdb(struct kr_cache *cache, const struct kr_query *qry) {
-       uint8_t keybuf[KNOT_DNAME_MAXLEN + 100]; // TODO
-       int ret = knot_dname_lf(keybuf, qry->sname, NULL);
+       struct key k_storage, *k = &k_storage;
+       int ret = knot_dname_lf(k->buf, qry->sname, NULL);
        if (ret) {
                return kr_error(ret);
        }
-       uint8_t kname_len = keybuf[0]; /**< current length of the name in keybuf */
-       keybuf[kname_len + 1] = 0; /* make sure different names can never match */
+       k->name_len = k->buf[0];
 
        /** 1. check if the name exists in the cache, not considering wildcards
-        *  1a. exact name+type match
+        *  1a. exact name+type match (can be negative answer in insecure zones)
         */
        uint16_t ktype = qry->stype;
        if (ktype == KNOT_RRTYPE_CNAME || ktype == KNOT_RRTYPE_DNAME) {
                ktype = KNOT_RRTYPE_NS;
        }
-       memcpy(keybuf + kname_len + 2, &ktype, 2);
-       /* dname_lf + 0 + RRTYPE */
-       knot_db_val_t key = { keybuf + 1, kname_len + 3 };
-       knot_db_val_t val = { NULL, 0 };
+       knot_db_val_t key = key_exact_type(k, ktype);
+       knot_db_val_t val = { };
        ret = cache_op(cache, read, &key, &val, 1);
        switch (ret) {
-       case 0:
+       case 0: {
+               if (val.len < sizeof(struct entry_h)) {
+                       assert(false); // TODO: correct length, recovery, etc.
+                       return kr_error(EILSEQ);
+               }
+               const struct entry_h *eh = val.data;
                // FIXME: check time and rank (and type if stype == xNAME),
                // return result if OK, otherwise fall through to break;
+               // insecure zones might have a negative-answer packet here
+               }
        case (-abs(ENOENT)):
                break;
        default:
                return kr_error(ret);
        }
 
-       /** 1b. otherwise, find the longest prefix NS/xNAME (with OK time+rank).
-        *      We store xNAME at NS type to lower the number of searches.
-        *      CNAME is only considered for equal name, of course.
-        *      We also store NSEC* parameters at NS type; probably the latest two will be kept.
+       /** 1b. otherwise, find the longest prefix NS/xNAME (with OK time+rank). [...] */
+       k->dname = qry->sname;
+       ret = closest_NS(cache, k);
+       if (ret != kr_ok()) {
+               return ret;
+       }
+
+       /* Note: up to here we can run on any cache backend,
+        * without touching the code. */
+
+       /* FIXME:
+        *      - Update the notion of current zone cut accordingly.
+        *      - find NSEC* parameters
+        *      - insecure zone -> return (nothing more to find)
         */
-       const knot_dname_t *cut = qry->sname;
+
+       /** 2. closest (provable) encloser.
+        * iterate over all NSEC* chain parameters
+        */
+       while (true) { //for (int i_nsecp = 0; i
+               int nsec = 1;
+               switch (nsec) {
+               case 1: {
+                       /* find a previous-or-equal name+NSEC in cache covering
+                        * the QNAME, checking TTL etc. */
+                       
+                       //nsec_leq()
+                       /* we basically need dname_lf with two bytes added
+                        * on a correct place within the name (the cut) */
+                       int ret = knot_dname_lf(k->buf, qry->sname, NULL);
+                       if (ret) {
+                               return kr_error(ret);
+                       }
+                       uint8_t *begin = k->buf + k->name_len + 1; /* one byte after zone's zero */
+                       uint8_t *end = k->buf + k->buf[0] - 1; /* we don't need final zero */
+                       memmove(begin + 2, begin, end - begin);
+                       begin[0] = 0;
+                       begin[1] = '1'; /* tag for NSEC1 */
+                       knot_db_val_t key = { k->buf + 1, k->buf[0] + 1 };
+                       knot_db_val_t val = { };
+                       /* key == zone's dname_lf + 0 + '1' + dname_lf of the name
+                        * within the zone without the final 0 */
+                       ret = cache_op(cache, read_leq, &key, &val);
+                       // FIXME: check that it covers the name
+                       //      - check validity, etc.
+
+                       break;
+                       }
+               case 3:
+                       //FIXME
+                       break;
+               default:
+                       assert(false);
+               }
+       }
+       
+       return kr_ok();
+}
+
+
+
+
+/** Find the longest prefix NS/xNAME (with OK time+rank).
+ * We store xNAME at NS type to lower the number of searches.
+ * CNAME is only considered for equal name, of course.
+ * We also store NSEC* parameters at NS type; probably the latest two will be kept.
+ */
+static int closest_NS(struct kr_cache *cache, struct key *k)
+{
        bool exact_match = true;
+       // LATER(optim): if stype is NS, we check the same value again
        do {
-               ktype = KNOT_RRTYPE_NS;
-               memcpy(keybuf + kname_len + 2, &ktype, 2);
-
-               key = { keybuf + 1, kname_len + 3 };
-               val = { NULL, 0 };
-               ret = cache_op(cache, read, &key, &val, 1);
+               knot_db_val_t key = key_exact_type(k, KNOT_RRTYPE_NS);
+               knot_db_val_t val = { };
+               int ret = cache_op(cache, read, &key, &val, 1);
                switch (ret) {
-               case 0:
+               case 0: {
                        // FIXME: check time and rank, and more complex stuff
-                       ktype = exact_match ? KNOT_RRTYPE_CNAME : KNOT_RRTYPE_DNAME;
+                       uint16_t ktype = exact_match ? KNOT_RRTYPE_CNAME : KNOT_RRTYPE_DNAME;
+                       // return kr_ok();
+                       }
                case (-abs(ENOENT)):
                        break;
                default:
@@ -538,21 +634,16 @@ int read_lmdb(struct kr_cache *cache, const struct kr_query *qry) {
 
                /* remove one more label */
                exact_match = false;
-               if (cut[0] == 0) {
+               if (k->dname[0] == 0) {
                        // FIXME: missing root NS
                }
-               kname_len -= (cut[0] + 1);
-               cut += (cut[0] + 1);
-               keybuf[kname_len + 1] = 0
+               k->name_len -= (k->dname[0] + 1);
+               k->dname += (k->dname[0] + 1);
+               k->buf[k->name_len + 1] = 0;
        } while (true);
-cut_found:
-
-       return kr_ok();
 }
 
 
 
 
 
-
-
index 8552575af978f08a3be9251c3b41ab28d0409277..3c7ed74f0f035dab22f993778d59209490a79cad 100644 (file)
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <libknot/consts.h>
 #include <libknot/rrset.h>
 #include "lib/cdb.h"
 #include "lib/defines.h"
@@ -24,6 +25,9 @@
 /** When knot_pkt is passed from cache without ->wire, this is the ->size. */
 static const size_t PKT_SIZE_NOWIRE = -1;
 
+// TODO
+#define KR_CACHE_KEY_MAXLEN (KNOT_DNAME_MAXLEN + 100)
+
 /** Cache entry tag */
 enum kr_cache_tag {
        KR_CACHE_RR   = 'R',
index daf1731092a51279d0fa2edbd1515ebef405311f..3a30a2bfab6de2ca5d7f71310305b0e612b227f4 100644 (file)
--- a/lib/cdb.h
+++ b/lib/cdb.h
@@ -51,4 +51,9 @@ struct kr_cdb_api {
 
        int (*match)(knot_db_t *db, knot_db_val_t *key, knot_db_val_t *val, int maxcount);
        int (*prune)(knot_db_t *db, int maxcount);
+
+       /* new WIP
+        * On successful return, key->data and val->data point to DB-owned data.
+        * return: 0 for equality, > 0 for less, < 0 kr_error */
+       int (*read_leq)(knot_db_t *db, knot_db_val_t *key, knot_db_val_t *val);
 };
index 2f912307b074ff88675af2ac676f5b9ff9e6ac88..256fcc0c395c3f85fe273d13663def32b335464b 100644 (file)
@@ -47,8 +47,9 @@ struct lmdb_env
         * - non-NULL .rw is always active
         */
        struct {
-               bool ro_active;
+               bool ro_active, ro_curs_active;
                MDB_txn *ro, *rw;
+               MDB_cursor *ro_curs;
        } txn;
 };
 
@@ -151,6 +152,7 @@ static int txn_get(struct lmdb_env *env, MDB_txn **txn, bool rdonly)
                if (env->txn.ro && env->txn.ro_active) {
                        mdb_txn_reset(env->txn.ro);
                        env->txn.ro_active = false;
+                       env->txn.ro_curs_active = false;
                }
                int ret = txn_get_noresize(env, 0/*RW*/, &env->txn.rw);
                if (ret == MDB_SUCCESS) {
@@ -190,10 +192,53 @@ static int cdb_sync(knot_db_t *db)
        } else if (env->txn.ro && env->txn.ro_active) {
                mdb_txn_reset(env->txn.ro);
                env->txn.ro_active = false;
+               env->txn.ro_curs_active = false;
        }
        return ret;
 }
 
+/** Obtain a read-only cursor (and a read-only transaction). */
+static int txn_curs_get(struct lmdb_env *env, MDB_cursor **curs)
+{
+       assert(env && curs);
+       if (env->txn.ro_curs_active) {
+               goto success;
+       }
+       /* Only in a read-only txn; TODO: it's a bit messy/coupled */
+       if (env->txn.rw) {
+               int ret = cdb_sync(env);
+               if (ret) return ret;
+       }
+       MDB_txn *txn = NULL;
+       int ret = txn_get(env, &txn, true);
+       if (ret) return ret;
+
+       if (env->txn.ro_curs) {
+               ret = mdb_cursor_renew(txn, env->txn.ro_curs);
+       } else {
+               ret = mdb_cursor_open(txn, env->dbi, &env->txn.ro_curs);
+       }
+       if (ret) return ret;
+success:
+       assert(env->txn.ro_curs_active && env->txn.ro && env->txn.ro_active
+               && !env->txn.rw);
+       *curs = env->txn.ro_curs;
+       assert(*curs);
+       return kr_ok();
+}
+
+static void free_txn_ro(struct lmdb_env *env)
+{
+       if (env->txn.ro) {
+               mdb_txn_abort(env->txn.ro);
+               env->txn.ro = NULL;
+       }
+       if (env->txn.ro_curs) {
+               mdb_cursor_close(env->txn.ro_curs);
+               env->txn.ro_curs = NULL;
+       }
+}
+
 /*! \brief Close the database. */
 static void cdb_close_env(struct lmdb_env *env)
 {
@@ -201,10 +246,7 @@ static void cdb_close_env(struct lmdb_env *env)
 
        /* Get rid of any transactions. */
        cdb_sync(env);
-       if (env->txn.ro) {
-               mdb_txn_abort(env->txn.ro);
-               env->txn.ro = NULL;
-       }
+       free_txn_ro(env);
 
        mdb_env_sync(env->env, 1);
        mdb_dbi_close(env->env, env->dbi);
@@ -359,10 +401,7 @@ static int cdb_clear(knot_db_t *db)
 
        /* We are about to switch to a different file, so end all txns, to be sure. */
        (void) cdb_sync(db);
-       if (env->txn.ro) {
-               mdb_txn_abort(env->txn.ro);
-               env->txn.ro = NULL;
-       }
+       free_txn_ro(db);
 
        /* Since there is no guarantee that there will be free
         * pages to hold whole dirtied db for transaction-safe clear,
@@ -612,13 +651,43 @@ static int cdb_prune(knot_db_t *db, int limit)
        return ret < 0 ? ret : results;
 }
 
+static int cdb_read_leq(knot_db_t *env, knot_db_val_t *key, knot_db_val_t *val)
+{
+       assert(env && key && key->data && val);
+       MDB_cursor *curs = NULL;
+       int ret = txn_curs_get(env, &curs);
+       if (ret) return ret;
+
+       MDB_val key2_m = val_knot2mdb(*key);
+       MDB_val val2_m = { };
+       ret = mdb_cursor_get(curs, &key2_m, &val2_m, MDB_SET_RANGE);
+       if (ret) return lmdb_error(ret);
+       /* test for equality //:unlikely */
+       if (key2_m.mv_size == key->len
+           && memcmp(key2_m.mv_data, key->data, key->len) == 0) {
+               ret = 0; /* equality */
+               goto success;
+       }
+       /* we must be greater than key; do one step to smaller */
+       ret = mdb_cursor_get(curs, &key2_m, &val2_m, MDB_PREV);
+       if (ret) return lmdb_error(ret);
+       ret = 1;
+success:
+       /* finalize the output */
+       *key = val_mdb2knot(key2_m);
+       *val = val_mdb2knot(val2_m);
+       return ret;
+}
+
+
 const struct kr_cdb_api *kr_cdb_lmdb(void)
 {
        static const struct kr_cdb_api api = {
                "lmdb",
                cdb_init, cdb_deinit, cdb_count, cdb_clear, cdb_sync,
                cdb_readv, cdb_writev, cdb_remove,
-               cdb_match, cdb_prune
+               cdb_match, cdb_prune,
+               cdb_read_leq
        };
 
        return &api;