This fixes lower hit rate in NSEC3 zones (since 2.4.0).
- minor TCP and TLS fixes (!623, !624, !626)
+Improvements
+------------
+- lua: resurrect cache.get('name') and cache.clear('name'), with caveats
Knot Resolver 2.4.0 (2018-07-03)
================================
.. function:: cache.prune([max_count])
- :param number max_count: maximum number of items to be pruned at once (default: 65536)
- :return: ``{ pruned: int }``
-
- Prune expired/invalid records.
+ Not implemented (anymore).
.. function:: cache.get([domain])
- :return: list of matching records in cache
-
- Fetches matching records from cache. The **domain** can either be:
-
- - a domain name (e.g. ``"domain.cz"``)
- - a wildcard (e.g. ``"*.domain.cz"``)
+ :return: table of records in cache matching the prefix
- The domain name fetches all records matching this name, while the wildcard matches all records at or below that name.
+ .. error:: **Caveats:**
- You can also use a special namespace ``"P"`` to purge NODATA/NXDOMAIN matching this name (e.g. ``"domain.cz P"``).
+ - the count of scanned keys is limited by 100,
+ so for large subtrees you may be getting just some prefix
+ (exact name matches go first);
+ - validated NSEC and NSEC3 records are not put into the table;
+ - CNAME and DNAME types are printed as NS;
+ - outdated records and unvalidated non-existence records are still output as ``true``.
.. note:: This is equivalent to ``cache['domain']`` getter.
.. code-block:: lua
- -- Query cache for 'domain.cz'
- cache['domain.cz']
- -- Query cache for all records at/below 'insecure.net'
- cache['*.insecure.net']
+ -- Query cache for 'nic.cz'
+ cache['nic.cz']
+
+ [nic.cz.] => {
+ [DNSKEY] => true
+ [SOA] => true
+ [AAAA] => true
+ [NS] => true
+ [A] => true
+ [DS] => true
+ }
+ [c.ns.nic.cz.] => {
+ [A] => true
+ [AAAA] => true
+ }
.. function:: cache.clear([domain])
- :return: ``bool``
+ :return: ``bool`` or ``int``
- Purge cache records. If the domain isn't provided, whole cache is purged. See *cache.get()* documentation for subtree matching policy.
+ Purge cache records.
+ If the domain isn't provided, whole cache is purged and ``bool`` is returned (denoting success).
+
+ If you provide a name, only records in that subtree are purged,
+ and the number of removed records is returned.
Examples:
.. code-block:: lua
- -- Clear records at/below 'bad.cz'
- cache.clear('*.bad.cz')
- -- Clear packet cache
- cache.clear('*. P')
+ -- Clear records at and below 'bad.cz'
+ cache.clear('bad.cz')
-- Clear whole cache
cache.clear()
+ .. attention:: In case you provide a name:
+
+ - The number of removed records is limited to 1000.
+ The purpose is not to block the resolver for long; if you need larger purges,
+ you may e.g. repeat the command with a 1ms timer until it returns a lower value.
+ - To minimize surprises, you may prefer to specify names that have NS/SOA records,
+ e.g. ``example.com``. Details: validated NSEC and NSEC3 records
+ (which are used for aggressive non-existence proofs)
+ will be removed only for zones whose **apex** is at or below the specified name.
+
Timers and events
^^^^^^^^^^^^^^^^^
if (!cache || !cache->api || !cache->api->remove) {
return kr_error(ENOSYS);
}
- static knot_db_val_t result_set[1000];
+ knot_db_val_t result_set[1000];
int ret = cache_prefixed(cache, args, result_set, 1000);
if (ret < 0) {
return ret;
}
/* Duplicate result set as we're going to remove it
* which will invalidate result set. */
- for (int i = 0; i < ret; ++i) {
+ int i;
+ for (i = 0; i < ret; ++i) {
void *dst = malloc(result_set[i].len);
if (!dst) {
- return kr_error(ENOMEM);
+ ret = kr_error(ENOMEM);
+ goto cleanup;
}
memcpy(dst, result_set[i].data, result_set[i].len);
result_set[i].data = dst;
}
cache->api->remove(cache->db, result_set, ret);
kr_cache_sync(cache);
+cleanup:
/* Free keys */
- for (int i = 0; i < ret; ++i) {
+ while (--i >= 0) {
free(result_set[i].data);
}
return ret;
}
/** @internal Dump cache key into table on Lua stack. */
-static void cache_dump_key(lua_State *L, knot_db_val_t *key)
+static void cache_dump_key(lua_State *L, knot_db_val_t key)
{
knot_dname_t dname[KNOT_DNAME_MAXLEN];
- char name[KNOT_DNAME_MAXLEN];
+ char name[KNOT_DNAME_TXT_MAXLEN];
uint16_t type;
int ret = kr_unpack_cache_key(key, dname, &type);
return;
}
- knot_dname_to_str(name, dname, sizeof(dname));
+ ret = !knot_dname_to_str(name, dname, sizeof(name));
+ assert(!ret);
+ if (ret) return;
/* If name typemap doesn't exist yet, create it */
lua_getfield(L, -1, name);
lua_newtable(L);
}
/* Append to typemap */
- char type_buf[16] = { '\0' };
+ char type_buf[KR_RRTYPE_STR_MAXLEN] = { '\0' };
knot_rrtype_to_string(type, type_buf, sizeof(type_buf));
lua_pushboolean(L, true);
lua_setfield(L, -2, type_buf);
lua_setfield(L, -2, name);
}
-/** Query cached records. */
+/** Query cached records. TODO: fix caveats in ./README.rst documentation? */
static int cache_get(lua_State *L)
{
//struct kr_cache *cache = cache_assert_open(L); // to be fixed soon
/* Retrieve set of keys */
const char *args = lua_tostring(L, 1);
- static knot_db_val_t result_set[100];
+ knot_db_val_t result_set[100];
int ret = cache_prefixed(cache, args, result_set, 100);
if (ret < 0) {
format_error(L, kr_strerror(ret));
/* Format output */
lua_newtable(L);
for (int i = 0; i < ret; ++i) {
- cache_dump_key(L, &result_set[i]);
+ cache_dump_key(L, result_set[i]);
}
return 1;
}
return ret;
}
-int kr_cache_remove(struct kr_cache *cache, const knot_dname_t *name,
- uint16_t type)
+int kr_cache_remove(struct kr_cache *cache, const knot_dname_t *name, uint16_t type)
{
if (!cache_isvalid(cache)) {
return kr_error(EINVAL);
// use a mock type
knot_db_val_t key = key_exact_type(k, KNOT_RRTYPE_A);
- key.len -= sizeof((char)('E')) + sizeof(uint16_t);
+ key.len -= 2 /* '\0' 'E' */ + sizeof(uint16_t); /* CACHE_KEY_DEF */
+ if (name[0] == '\0') ++key.len; /* the root name is special ATM */
return cache_op(cache, match, &key, keys, max);
}
-int kr_unpack_cache_key(knot_db_val_t *key, knot_dname_t *buf, uint16_t *type)
+int kr_unpack_cache_key(knot_db_val_t key, knot_dname_t *buf, uint16_t *type)
{
- if (key == NULL || buf == NULL) {
+ if (key.data == NULL || buf == NULL || type == NULL) {
return kr_error(EINVAL);
}
int len = -1;
- const void *endp;
- for (endp = key->data + 1;
- endp < key->data + key->len; endp++) {
- if (*(const char *)(endp-1) == '\0' &&
- (*(const char *)endp == 'E')) {
- len = endp - key->data - 1;
+ const char *tag, *key_data = key.data;
+ for (tag = key_data + 1; tag < key_data + key.len; ++tag) {
+ /* CACHE_KEY_DEF */
+ if (tag[-1] == '\0' && (tag == key_data + 1 || tag[-2] == '\0')) {
+ if (tag[0] != 'E') return kr_error(EINVAL);
+ len = tag - 1 - key_data;
break;
}
}
return kr_error(EINVAL);
}
- int ret = knot_dname_lf2wire(buf, len, key->data);
+ int ret = knot_dname_lf2wire(buf, len, key.data);
if (ret < 0) {
return kr_error(ret);
}
- // jump over "\0 E/1"
- memcpy(type, key->data + len + 2, sizeof(uint16_t));
+ /* CACHE_KEY_DEF: jump over "\0 E/1" */
+ memcpy(type, tag + 1, sizeof(uint16_t));
return kr_ok();
}
* @param name dname
* @param type rr type
* @return 0 or an errcode
+ * @note only "exact hits" are considered ATM, moreover xNAME records
+ * are "hidden" as NS. (see comments in struct entry_h)
*/
KR_EXPORT
-int kr_cache_remove(struct kr_cache *cache, const knot_dname_t *name,
- uint16_t type);
+int kr_cache_remove(struct kr_cache *cache, const knot_dname_t *name, uint16_t type);
/**
* Get keys matching a dname lf prefix
* @param name dname
* @param keys matched keys
* @return result count or an errcode
+ * @note the cache keys are matched by prefix, i.e. it very much depends
+ * on their structure; CACHE_KEY_DEF.
*/
KR_EXPORT
int kr_cache_match(struct kr_cache *cache, const knot_dname_t *name,
* @param buf output buffer of domain name in dname format
* @param type output for type
* @return length of dname or an errcode
+ * @note only "exact hits" are considered ATM, moreover xNAME records
+ * are "hidden" as NS. (see comments in struct entry_h)
*/
KR_EXPORT
-int kr_unpack_cache_key(knot_db_val_t *key, knot_dname_t *buf, uint16_t *type);
+int kr_unpack_cache_key(knot_db_val_t key, knot_dname_t *buf, uint16_t *type);
return ret;
}
+ /* LATER(optim.): use txn_curs_get() instead, to save resources. */
MDB_cursor *cur = NULL;
ret = mdb_cursor_open(txn, env->dbi, &cur);
if (ret != 0) {
/* Cache entry values - binary layout.
*
* It depends on type which is recognizable by the key.
- * Code depending on the meaning of the key is marked by CACHE_KEY_DEF.
+ * Code depending on the contents of the key is marked by CACHE_KEY_DEF.
*
* 'E' entry (exact hit):
- * - ktype == NS: entry_apex and multiple chained entry_h, based on has_* flags;
- * - is_packet: uint16_t length, otherwise opaque and handled by ./entry_pkt.c
- * - otherwise RRset + its RRSIG set (possibly empty).
+ * - ktype == NS: struct entry_apex - multiple types inside (NS and xNAME);
+ * - ktype != NS: struct entry_h
+ * * is_packet: uint16_t length, the rest is opaque and handled by ./entry_pkt.c
+ * * otherwise RRset + its RRSIG set (possibly empty).
* '1' or '3' entry (NSEC or NSEC3)
- * - contents is the same as for exact hit
+ * - struct entry_h, contents is the same as for exact hit
* - flags don't make sense there
*/