1 /* Copyright (C) 2015-2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
2 * SPDX-License-Identifier: GPL-3.0-or-later
3 */
4
5 #include "daemon/bindings/impl.h"
6
7 #include "daemon/zimport.h"
8
9 /** @internal return cache, or throw lua error if not open */
cache_assert_open(lua_State * L)10 static struct kr_cache * cache_assert_open(lua_State *L)
11 {
12 struct kr_cache *cache = &the_worker->engine->resolver.cache;
13 if (kr_fails_assert(cache) || !kr_cache_is_open(cache))
14 lua_error_p(L, "no cache is open yet, use cache.open() or cache.size, etc.");
15 return cache;
16 }
17
18 /** Return available cached backends. */
cache_backends(lua_State * L)19 static int cache_backends(lua_State *L)
20 {
21 struct engine *engine = the_worker->engine;
22
23 lua_newtable(L);
24 for (unsigned i = 0; i < engine->backends.len; ++i) {
25 const struct kr_cdb_api *api = engine->backends.at[i];
26 lua_pushboolean(L, api == engine->resolver.cache.api);
27 lua_setfield(L, -2, api->name);
28 }
29 return 1;
30 }
31
32 /** Return number of cached records. */
cache_count(lua_State * L)33 static int cache_count(lua_State *L)
34 {
35 struct kr_cache *cache = cache_assert_open(L);
36
37 int count = cache->api->count(cache->db, &cache->stats);
38 if (count >= 0) {
39 /* First key is a version counter, omit it if nonempty. */
40 lua_pushinteger(L, count ? count - 1 : 0);
41 return 1;
42 }
43 return 0;
44 }
45
46 /** Return time of last checkpoint, or re-set it if passed `true`. */
cache_checkpoint(lua_State * L)47 static int cache_checkpoint(lua_State *L)
48 {
49 struct kr_cache *cache = cache_assert_open(L);
50
51 if (lua_gettop(L) == 0) { /* Return the current value. */
52 lua_newtable(L);
53 lua_pushnumber(L, cache->checkpoint_monotime);
54 lua_setfield(L, -2, "monotime");
55 lua_newtable(L);
56 lua_pushnumber(L, cache->checkpoint_walltime.tv_sec);
57 lua_setfield(L, -2, "sec");
58 lua_pushnumber(L, cache->checkpoint_walltime.tv_usec);
59 lua_setfield(L, -2, "usec");
60 lua_setfield(L, -2, "walltime");
61 return 1;
62 }
63
64 if (lua_gettop(L) != 1 || !lua_isboolean(L, 1) || !lua_toboolean(L, 1))
65 lua_error_p(L, "cache.checkpoint() takes no parameters or a true value");
66
67 kr_cache_make_checkpoint(cache);
68 return 1;
69 }
70
71 /** Return cache statistics. */
cache_stats(lua_State * L)72 static int cache_stats(lua_State *L)
73 {
74 struct kr_cache *cache = cache_assert_open(L);
75 lua_newtable(L);
76 #define add_stat(name) \
77 lua_pushinteger(L, (cache->stats.name)); \
78 lua_setfield(L, -2, #name)
79 add_stat(open);
80 add_stat(close);
81 add_stat(count);
82 cache->stats.count_entries = cache->api->count(cache->db, &cache->stats);
83 add_stat(count_entries);
84 add_stat(clear);
85 add_stat(commit);
86 add_stat(read);
87 add_stat(read_miss);
88 add_stat(write);
89 add_stat(remove);
90 add_stat(remove_miss);
91 add_stat(match);
92 add_stat(match_miss);
93 add_stat(read_leq);
94 add_stat(read_leq_miss);
95 /* usage_percent statistics special case - double */
96 cache->stats.usage_percent = cache->api->usage_percent(cache->db);
97 lua_pushnumber(L, cache->stats.usage_percent);
98 lua_setfield(L, -2, "usage_percent");
99 #undef add_stat
100
101 return 1;
102 }
103
cache_select(struct engine * engine,const char ** conf)104 static const struct kr_cdb_api *cache_select(struct engine *engine, const char **conf)
105 {
106 /* Return default backend */
107 if (*conf == NULL || !strstr(*conf, "://")) {
108 return engine->backends.at[0];
109 }
110
111 /* Find storage backend from config prefix */
112 for (unsigned i = 0; i < engine->backends.len; ++i) {
113 const struct kr_cdb_api *api = engine->backends.at[i];
114 if (strncmp(*conf, api->name, strlen(api->name)) == 0) {
115 *conf += strlen(api->name) + strlen("://");
116 return api;
117 }
118 }
119
120 return NULL;
121 }
122
cache_max_ttl(lua_State * L)123 static int cache_max_ttl(lua_State *L)
124 {
125 struct kr_cache *cache = cache_assert_open(L);
126
127 int n = lua_gettop(L);
128 if (n > 0) {
129 if (!lua_isnumber(L, 1) || n > 1)
130 lua_error_p(L, "expected 'max_ttl(number ttl)'");
131 uint32_t min = cache->ttl_min;
132 int64_t ttl = lua_tointeger(L, 1);
133 if (ttl < 1 || ttl < min || ttl > UINT32_MAX) {
134 lua_error_p(L,
135 "max_ttl must be larger than minimum TTL, and in range <1, "
136 STR(UINT32_MAX) ">'");
137 }
138 cache->ttl_max = ttl;
139 }
140 lua_pushinteger(L, cache->ttl_max);
141 return 1;
142 }
143
144
cache_min_ttl(lua_State * L)145 static int cache_min_ttl(lua_State *L)
146 {
147 struct kr_cache *cache = cache_assert_open(L);
148
149 int n = lua_gettop(L);
150 if (n > 0) {
151 if (!lua_isnumber(L, 1))
152 lua_error_p(L, "expected 'min_ttl(number ttl)'");
153 uint32_t max = cache->ttl_max;
154 int64_t ttl = lua_tointeger(L, 1);
155 if (ttl < 0 || ttl > max || ttl > UINT32_MAX) {
156 lua_error_p(L,
157 "min_ttl must be smaller than maximum TTL, and in range <0, "
158 STR(UINT32_MAX) ">'");
159 }
160 cache->ttl_min = ttl;
161 }
162 lua_pushinteger(L, cache->ttl_min);
163 return 1;
164 }
165
166 /** Open cache */
cache_open(lua_State * L)167 static int cache_open(lua_State *L)
168 {
169 /* Check parameters */
170 int n = lua_gettop(L);
171 if (n < 1 || !lua_isnumber(L, 1))
172 lua_error_p(L, "expected 'open(number max_size, string config = \"\")'");
173
174 /* Select cache storage backend */
175 struct engine *engine = the_worker->engine;
176
177 lua_Integer csize_lua = lua_tointeger(L, 1);
178 if (!(csize_lua >= 8192 && csize_lua < SIZE_MAX)) { /* min. is basically arbitrary */
179 lua_error_p(L, "invalid cache size specified, it must be in range <8192, "
180 STR(SIZE_MAX) ">");
181 }
182 size_t cache_size = csize_lua;
183
184 const char *conf = n > 1 ? lua_tostring(L, 2) : NULL;
185 const char *uri = conf;
186 const struct kr_cdb_api *api = cache_select(engine, &conf);
187 if (!api)
188 lua_error_p(L, "unsupported cache backend");
189
190 /* Close if already open */
191 kr_cache_close(&engine->resolver.cache);
192
193 /* Reopen cache */
194 struct kr_cdb_opts opts = {
195 (conf && strlen(conf)) ? conf : ".",
196 cache_size
197 };
198 int ret = kr_cache_open(&engine->resolver.cache, api, &opts, engine->pool);
199 if (ret != 0) {
200 char cwd[PATH_MAX];
201 get_workdir(cwd, sizeof(cwd));
202 return luaL_error(L, "can't open cache path '%s'; working directory '%s'; %s",
203 opts.path, cwd, kr_strerror(ret));
204 }
205 /* Let's check_health() every five seconds to avoid keeping old cache alive
206 * even in case of not having any work to do. */
207 ret = kr_cache_check_health(&engine->resolver.cache, 5000);
208 if (ret != 0) {
209 kr_log_error(CACHE, "periodic health check failed (ignored): %s\n",
210 kr_strerror(ret));
211 }
212
213 /* Store current configuration */
214 lua_getglobal(L, "cache");
215 lua_pushstring(L, "current_size");
216 lua_pushnumber(L, cache_size);
217 lua_rawset(L, -3);
218 lua_pushstring(L, "current_storage");
219 lua_pushstring(L, uri);
220 lua_rawset(L, -3);
221 lua_pop(L, 1);
222
223 lua_pushboolean(L, 1);
224 return 1;
225 }
226
cache_close(lua_State * L)227 static int cache_close(lua_State *L)
228 {
229 struct kr_cache *cache = &the_worker->engine->resolver.cache;
230 if (!kr_cache_is_open(cache)) {
231 return 0;
232 }
233
234 kr_cache_close(cache);
235 lua_getglobal(L, "cache");
236 lua_pushstring(L, "current_size");
237 lua_pushnumber(L, 0);
238 lua_rawset(L, -3);
239 lua_pop(L, 1);
240 lua_pushboolean(L, 1);
241 return 1;
242 }
243
244 #if 0
245 /** @internal Prefix walk. */
246 static int cache_prefixed(struct kr_cache *cache, const char *prefix, bool exact_name,
247 knot_db_val_t keyval[][2], int maxcount)
248 {
249 /* Convert to domain name */
250 uint8_t buf[KNOT_DNAME_MAXLEN];
251 if (!knot_dname_from_str(buf, prefix, sizeof(buf))) {
252 return kr_error(EINVAL);
253 }
254 /* Start prefix search */
255 return kr_cache_match(cache, buf, exact_name, keyval, maxcount);
256 }
257 #endif
258
259 /** Clear everything. */
cache_clear_everything(lua_State * L)260 static int cache_clear_everything(lua_State *L)
261 {
262 struct kr_cache *cache = cache_assert_open(L);
263
264 /* Clear records and packets. */
265 int ret = kr_cache_clear(cache);
266 lua_error_maybe(L, ret);
267
268 /* Clear reputation tables */
269 struct kr_context *ctx = &the_worker->engine->resolver;
270 lru_reset(ctx->cache_cookie);
271 lua_pushboolean(L, true);
272 return 1;
273 }
274
275 #if 0
276 /** @internal Dump cache key into table on Lua stack. */
277 static void cache_dump(lua_State *L, knot_db_val_t keyval[])
278 {
279 knot_dname_t dname[KNOT_DNAME_MAXLEN];
280 char name[KNOT_DNAME_TXT_MAXLEN];
281 uint16_t type;
282
283 int ret = kr_unpack_cache_key(keyval[0], dname, &type);
284 if (ret < 0) {
285 return;
286 }
287
288 ret = !knot_dname_to_str(name, dname, sizeof(name));
289 if (kr_fails_assert(!ret)) return;
290
291 /* If name typemap doesn't exist yet, create it */
292 lua_getfield(L, -1, name);
293 if (lua_isnil(L, -1)) {
294 lua_pop(L, 1);
295 lua_newtable(L);
296 }
297 /* Append to typemap */
298 char type_buf[KR_RRTYPE_STR_MAXLEN] = { '\0' };
299 knot_rrtype_to_string(type, type_buf, sizeof(type_buf));
300 lua_pushboolean(L, true);
301 lua_setfield(L, -2, type_buf);
302 /* Set name typemap */
303 lua_setfield(L, -2, name);
304 }
305
306 /** Query cached records. TODO: fix caveats in ./README.rst documentation? */
307 static int cache_get(lua_State *L)
308 {
309 //struct kr_cache *cache = cache_assert_open(L); // to be fixed soon
310
311 /* Check parameters */
312 int n = lua_gettop(L);
313 if (n < 1 || !lua_isstring(L, 1))
314 lua_error_p(L, "expected 'cache.get(string key)'");
315
316 /* Retrieve set of keys */
317 const char *prefix = lua_tostring(L, 1);
318 knot_db_val_t keyval[100][2];
319 int ret = cache_prefixed(cache, prefix, false/*FIXME*/, keyval, 100);
320 lua_error_maybe(L, ret);
321 /* Format output */
322 lua_newtable(L);
323 for (int i = 0; i < ret; ++i) {
324 cache_dump(L, keyval[i]);
325 }
326 return 1;
327 }
328 #endif
cache_get(lua_State * L)329 static int cache_get(lua_State *L)
330 {
331 lua_error_maybe(L, ENOSYS);
332 return kr_error(ENOSYS); /* doesn't happen */
333 }
334
335 /** Set time interval for cleaning rtt cache.
336 * Servers with score >= KR_NS_TIMEOUT will be cleaned after
337 * this interval ended up, so that they will be able to participate
338 * in NS elections again. */
cache_ns_tout(lua_State * L)339 static int cache_ns_tout(lua_State *L)
340 {
341 struct kr_context *ctx = &the_worker->engine->resolver;
342
343 /* Check parameters */
344 int n = lua_gettop(L);
345 if (n < 1) {
346 lua_pushinteger(L, ctx->cache_rtt_tout_retry_interval);
347 return 1;
348 }
349
350 if (!lua_isnumber(L, 1))
351 lua_error_p(L, "expected 'cache.ns_tout(interval in ms)'");
352
353 lua_Integer interval_lua = lua_tointeger(L, 1);
354 if (!(interval_lua > 0 && interval_lua < UINT_MAX)) {
355 lua_error_p(L, "invalid interval specified, it must be in range > 0, < "
356 STR(UINT_MAX));
357 }
358
359 ctx->cache_rtt_tout_retry_interval = interval_lua;
360 lua_pushinteger(L, ctx->cache_rtt_tout_retry_interval);
361 return 1;
362 }
363
364 /** Zone import completion callback.
365 * Deallocates zone import context. */
cache_zone_import_cb(int state,void * param)366 static void cache_zone_import_cb(int state, void *param)
367 {
368 (void)state;
369 struct worker_ctx *worker = param;
370 if (kr_fails_assert(worker && worker->z_import)) return;
371 zi_free(worker->z_import);
372 worker->z_import = NULL;
373 }
374
375 /** Import zone from file. */
cache_zone_import(lua_State * L)376 static int cache_zone_import(lua_State *L)
377 {
378 int ret = -1;
379 char msg[128];
380
381 struct worker_ctx *worker = the_worker;
382 if (!worker) {
383 strncpy(msg, "internal error, empty worker pointer", sizeof(msg));
384 goto finish;
385 }
386
387 if (worker->z_import && zi_import_started(worker->z_import)) {
388 strncpy(msg, "import already started", sizeof(msg));
389 goto finish;
390 }
391
392 (void)cache_assert_open(L); /* just check it in advance */
393
394 /* Check parameters */
395 int n = lua_gettop(L);
396 if (n < 1 || !lua_isstring(L, 1)) {
397 strncpy(msg, "expected 'cache.zone_import(path to zone file)'", sizeof(msg));
398 goto finish;
399 }
400
401 /* Parse zone file */
402 const char *zone_file = lua_tostring(L, 1);
403
404 const char *default_origin = NULL; /* TODO */
405 uint16_t default_rclass = 1;
406 uint32_t default_ttl = 0;
407
408 if (worker->z_import == NULL) {
409 worker->z_import = zi_allocate(worker, cache_zone_import_cb, worker);
410 if (worker->z_import == NULL) {
411 strncpy(msg, "can't allocate zone import context", sizeof(msg));
412 goto finish;
413 }
414 }
415
416 ret = zi_zone_import(worker->z_import, zone_file, default_origin,
417 default_rclass, default_ttl);
418
419 lua_newtable(L);
420 if (ret == 0) {
421 strncpy(msg, "zone file successfully parsed, import started", sizeof(msg));
422 } else if (ret == 1) {
423 strncpy(msg, "TA not found", sizeof(msg));
424 } else {
425 strncpy(msg, "error parsing zone file", sizeof(msg));
426 }
427
428 finish:
429 msg[sizeof(msg) - 1] = 0;
430 lua_newtable(L);
431 lua_pushstring(L, msg);
432 lua_setfield(L, -2, "msg");
433 lua_pushnumber(L, ret);
434 lua_setfield(L, -2, "code");
435
436 return 1;
437 }
438
kr_bindings_cache(lua_State * L)439 int kr_bindings_cache(lua_State *L)
440 {
441 static const luaL_Reg lib[] = {
442 { "backends", cache_backends },
443 { "count", cache_count },
444 { "stats", cache_stats },
445 { "checkpoint", cache_checkpoint },
446 { "open", cache_open },
447 { "close", cache_close },
448 { "clear_everything", cache_clear_everything },
449 { "get", cache_get },
450 { "max_ttl", cache_max_ttl },
451 { "min_ttl", cache_min_ttl },
452 { "ns_tout", cache_ns_tout },
453 { "zone_import", cache_zone_import },
454 { NULL, NULL }
455 };
456
457 luaL_register(L, "cache", lib);
458 return 1;
459 }
460
461