From: Vladimír Čunát Date: Wed, 27 Sep 2017 16:44:15 +0000 (+0200) Subject: . X-Git-Tag: v2.0.0~6^2~96 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=423c06300d9f7c804cff1d7f0437dde0917ea526;p=thirdparty%2Fknot-resolver.git . --- diff --git a/lib/cache.c b/lib/cache.c index 77ae4d363..5de459a3c 100644 --- a/lib/cache.c +++ b/lib/cache.c @@ -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(); } - - diff --git a/lib/cache.h b/lib/cache.h index 8552575af..3c7ed74f0 100644 --- a/lib/cache.h +++ b/lib/cache.h @@ -16,6 +16,7 @@ #pragma once +#include #include #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', diff --git a/lib/cdb.h b/lib/cdb.h index daf173109..3a30a2bfa 100644 --- 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); }; diff --git a/lib/cdb_lmdb.c b/lib/cdb_lmdb.c index 2f912307b..256fcc0c3 100644 --- a/lib/cdb_lmdb.c +++ b/lib/cdb_lmdb.c @@ -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;