Decision function is separated out.
_Bool NONAUTH : 1;
_Bool FORWARD : 1;
_Bool DNS64_MARK : 1;
+ _Bool CACHE_TRIED : 1;
};
typedef struct {
knot_rrset_t **at;
struct timeval last_clear_walltime;
uint64_t last_clear_monotime;
};
+
+typedef int32_t (*kr_stale_cb)(int32_t ttl, const knot_dname_t *owner, uint16_t type,
+ const struct kr_query *qry);
struct knot_rrset {
knot_dname_t *_owner;
uint16_t type;
uint32_t uid;
struct kr_query *cname_parent;
struct kr_request *request;
+ kr_stale_cb stale_cb;
};
struct kr_context {
struct kr_qflags options;
int kr_ranked_rrarray_add(ranked_rr_array_t *, const knot_rrset_t *, uint8_t, _Bool, uint32_t, knot_mm_t *);
void kr_qflags_set(struct kr_qflags *, struct kr_qflags);
void kr_qflags_clear(struct kr_qflags *, struct kr_qflags);
+void kr_query_set_stale_cb(struct kr_query *, kr_stale_cb);
int kr_zonecut_add(struct kr_zonecut *, const knot_dname_t *, const knot_rdata_t *);
void kr_zonecut_set(struct kr_zonecut *, const knot_dname_t *);
uint64_t kr_now();
struct kr_cache
EOF
+printf "
+typedef int32_t (*kr_stale_cb)(int32_t ttl, const knot_dname_t *owner, uint16_t type,
+ const struct kr_query *qry);
+"
+
genResType() {
echo "$1" | ./scripts/gen-cdefs.sh libkres types
}
kr_ranked_rrarray_add
kr_qflags_set
kr_qflags_clear
+ kr_query_set_stale_cb
kr_zonecut_add
kr_zonecut_set
kr_now
.. include:: ../modules/ta_signal_query/README.rst
.. include:: ../modules/ta_sentinel/README.rst
.. include:: ../modules/priming/README.rst
+.. include:: ../modules/serve_stale/README.rst
.. include:: ../modules/detect_time_skew/README.rst
.. include:: ../modules/detect_time_jump/README.rst
diff = 0;
}
int32_t res = entry->ttl - diff;
- if (res < 0 && owner && false/*qry->flags.SERVE_STALE*/) {
+ if (res < 0 && owner && qry->stale_cb) {
/* Stale-serving decision. FIXME: modularize or make configurable, etc. */
- if (res + 3600 * 24 > 0) {
- VERBOSE_MSG(qry, "stale TTL accepted: %d -> 1\n", (int)res);
- return 1;
- }
+ int res_stale = qry->stale_cb(res, owner, type, qry);
+ if (res_stale >= 0)
+ return res_stale;
}
return res;
}
struct kr_query *qry = req->current_query;
if (ctx->state & (KR_STATE_FAIL|KR_STATE_DONE) || qry->flags.NO_CACHE
+ || (qry->flags.CACHE_TRIED && !qry->stale_cb)
|| qry->stype == KNOT_RRTYPE_RRSIG /* LATER: some other behavior for this STYPE? */
|| qry->sclass != KNOT_CLASS_IN) {
return ctx->state; /* Already resolved/failed or already tried, etc. */
/* ATM cache only peeks for qry->sname and that would be useless
* to repeat on every iteration, so disable it from now on.
* LATER(optim.): assist with more precise QNAME minimization. */
- qry->flags.NO_CACHE = true;
+ qry->flags.CACHE_TRIED = true;
struct key k_storage, *k = &k_storage;
if (qry->stype == KNOT_RRTYPE_NSEC) {
#define QUERY_PROVIDES(q, name, cls, type) \
((q)->sclass == (cls) && (q)->stype == type && knot_dname_is_equal((q)->sname, name))
+void kr_query_set_stale_cb(struct kr_query *qry, kr_stale_cb cb)
+{
+ qry->stale_cb = cb;
+}
+
inline static unsigned char chars_or(const unsigned char a, const unsigned char b)
{
return a | b;
* TODO: utilize this also outside cache. */
bool FORWARD : 1; /**< Forward all queries to upstream; validate answers. */
bool DNS64_MARK : 1; /**< Internal mark for dns64 module. */
+ bool CACHE_TRIED : 1; /**< Internal to cache module. */
};
/** Combine flags together. This means set union for simple flags. */
KR_EXPORT
void kr_qflags_clear(struct kr_qflags *fl1, struct kr_qflags fl2);
+/** Callback for serve-stale decisions.
+ * @param ttl the expired TTL (i.e. it's < 0)
+ * @return the adjusted TTL (typically 1) or < 0.
+ */
+typedef int32_t (*kr_stale_cb)(int32_t ttl, const knot_dname_t *owner, uint16_t type,
+ const struct kr_query *qry);
+/** Trivial wrapper to set kr_query::stale_cb.
+ * For some unknown reason, direct setting via lua doesn't work. */
+KR_EXPORT
+void kr_query_set_stale_cb(struct kr_query *qry, kr_stale_cb cb);
+
/**
* Single query representation.
*/
/** Pointer to the query that originated this one because of following a CNAME (or NULL). */
struct kr_query *cname_parent;
struct kr_request *request; /**< Parent resolution request. */
+ kr_stale_cb stale_cb; /**< See the type */
};
/** @cond internal Array of queries. */
version \
ta_signal_query \
priming \
+ serve_stale \
detect_time_skew \
detect_time_jump
endif
--- /dev/null
+.. _mod-serve_stale:
+
+Serve stale
+-----------
+
+Demo module that allows using timed-out records in case kresd is
+unable to contact upstream servers.
+
+By default it allows stale-ness by up to one day,
+after roughly four seconds trying to contact the servers.
+It's quite configurable/flexible; see the beginning of the module source for details.
+See also the RFC draft_ (not fully followed).
+
+Running
+^^^^^^^
+.. code-block:: lua
+
+ modules = { 'serve_stale < cache' }
+
+.. _draft: https://tools.ietf.org/html/draft-ietf-dnsop-serve-stale-00
+
--- /dev/null
+local M = {} -- the module
+
+local ffi = require('ffi')
+
+-- Beware that the timeout is only considered at certain points in time;
+-- approximately at multiples of KR_CONN_RTT_MAX.
+M.timeout = 3*sec
+
+M.callback = ffi.cast("kr_stale_cb",
+ function (ttl) --, name, type, qry)
+ --log('[ ][stal] => called back with TTL: ' .. tostring(ttl))
+ if ttl + 3600 * 24 > 0 then -- at most one day stale
+ return 1
+ else
+ return -1
+ end
+ end)
+
+M.layer = {
+ produce = function (state, req)
+ req = kres.request_t(req)
+ local qry = req:current()
+ -- Don't do anything for priming, prefetching, etc.
+ -- TODO: not all cases detected ATM.
+ if qry.flags.NO_CACHE then return state end
+
+ local now = ffi.C.kr_now()
+ local deadline = qry.creation_time_mono + M.timeout
+ if now > deadline then
+ --log('[ ][stal] => deadline has passed')
+ ffi.C.kr_query_set_stale_cb(qry, M.callback)
+ -- TODO: probably start the same request that doesn't stale-serve,
+ -- but first we need some detection of non-interactive / internal requests.
+ -- resolve(kres.dname2str(qry.sname), qry.stype, qry.sclass)
+ end
+
+ return state
+ end,
+}
+
+return M
+
--- /dev/null
+serve_stale_SOURCES := serve_stale.lua
+$(call make_lua_module,serve_stale)