#include "lib/utils.h"
/* Cache version */
-#define KEY_VERSION "V\x01"
+#define KEY_VERSION "V\x02"
/* Key size */
#define KEY_HSIZE (sizeof(uint8_t) + sizeof(uint16_t))
#define KEY_SIZE (KEY_HSIZE + KNOT_DNAME_MAXLEN)
namedb_val_t entry = { NULL, sizeof(*header) + data.len };
const namedb_api_t *db_api = txn_api(txn);
- /* Do not overwrite entries that are higher ranked and not expired. */
- namedb_val_t old_entry = { NULL, 0 };
- int ret = txn_api(txn)->find(&txn->t, &key, &old_entry, 0);
- if (ret == 0) {
- struct kr_cache_entry *old = old_entry.data;
- uint32_t timestamp = header->timestamp;
- if (kr_cache_rank_cmp(old->rank, header->rank) > 0 && check_lifetime(old, ×tamp) == 0) {
- return kr_error(EPERM);
- }
- }
-
/* LMDB can do late write and avoid copy */
txn->owner->stats.insert += 1;
if (db_api == namedb_lmdb_api()) {
return kr_ok();
}
+int kr_cache_peek_rank(struct kr_cache_txn *txn, uint8_t tag, const knot_dname_t *name, uint16_t type, uint32_t timestamp)
+{
+ struct kr_cache_entry *found = lookup(txn, tag, name, type);
+ if (!found) {
+ return kr_error(ENOENT);
+ }
+ if (check_lifetime(found, ×tamp) != 0) {
+ return kr_error(ESTALE);
+ }
+ return found->rank;
+}
+
int kr_cache_materialize(knot_rrset_t *dst, const knot_rrset_t *src, uint32_t drift, mm_ctx_t *mm)
{
if (!dst || !src) {
KR_CACHE_USER = 0x80
};
-/** Cache entry rank */
+/**
+ * Cache entry rank.
+ * @note Be careful about chosen cache rank nominal values.
+ * - AUTH must be > than NONAUTH
+ * - AUTH INSECURE must be > than AUTH (because it attempted validation)
+ * - NONAUTH SECURE must be > than AUTH (because it's valid)
+ */
enum kr_cache_rank {
KR_RANK_BAD = 0, /* BAD cache, do not use. */
KR_RANK_INSECURE = 1, /* Entry is DNSSEC insecure (e.g. RRSIG not exists). */
- KR_RANK_SECURE = 2, /* Entry is DNSSEC valid (e.g. RRSIG exists). */
- /* <= Lower 3 bits reserved for various flags. */
KR_RANK_NONAUTH = 8, /* Entry from authority section (i.e. parent-side) */
KR_RANK_AUTH = 16, /* Entry from answer (authoritative data) */
+ KR_RANK_SECURE = 256, /* Entry is DNSSEC valid (e.g. RRSIG exists). */
};
-/* Compare ranks (ignore flags) */
-#define kr_cache_rank_cmp(x, y) (((x) >> 2) - ((y) >> 2))
/**
* Serialized form of the RRSet with inception timestamp and maximum TTL.
int kr_cache_peek(struct kr_cache_txn *txn, uint8_t tag, const knot_dname_t *name, uint16_t type,
struct kr_cache_entry **entry, uint32_t *timestamp);
+
+
/**
* Insert asset into cache, replacing any existing data.
* @param txn transaction instance
*/
int kr_cache_clear(struct kr_cache_txn *txn);
+/**
+ * Peek the cache for given key and retrieve it's rank.
+ * @param txn transaction instance
+ * @param tag asset tag
+ * @param name asset name
+ * @param type record type
+ * @param timestamp current time
+ * @return rank (0 or positive), or an error (negative number)
+ */
+int kr_cache_peek_rank(struct kr_cache_txn *txn, uint8_t tag, const knot_dname_t *name, uint16_t type, uint32_t timestamp);
+
/**
* Peek the cache for given RRSet (name, type)
* @note The 'drift' is the time passed between the cache time of the RRSet and now (in seconds).
header.rank = KR_RANK_INSECURE;
}
+ /* Check if we can replace (allow current or better rank, SECURE is always accepted). */
+ if (header.rank < KR_RANK_SECURE) {
+ int cached_rank = kr_cache_peek_rank(&txn, KR_CACHE_PKT, qname, qtype, header.timestamp);
+ if (cached_rank > header.rank) {
+ kr_cache_txn_abort(&txn);
+ return ctx->state;
+ }
+ }
+
/* Stash answer in the cache */
int ret = kr_cache_insert(&txn, KR_CACHE_PKT, qname, qtype, &header, data);
if (ret != 0) {
if (KEY_COVERING_RRSIG(key)) {
return commit_rrsig(baton, rank, rr);
}
+ /* Accept only better rank (if not overriding) */
+ if (!(rank & KR_RANK_SECURE) && !(baton->qry->flags & QUERY_NO_CACHE)) {
+ int cached_rank = kr_cache_peek_rank(baton->txn, KR_CACHE_RR, rr->owner, rr->type, baton->timestamp);
+ if (cached_rank >= rank) {
+ return kr_ok();
+ }
+ }
- /* Check if already cached */
knot_rrset_t query_rr;
knot_rrset_init(&query_rr, rr->owner, rr->type, rr->rclass);
return kr_cache_insert_rr(baton->txn, rr, rank, baton->timestamp);