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