bindings.c 43.6 KB
Newer Older
1
/*  Copyright (C) 2015-2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
2 3 4 5 6 7 8 9 10 11 12 13

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
14
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
15 16
 */

17
#include <assert.h>
18
#include <stdint.h>
19
#include <uv.h>
20
#include <contrib/cleanup.h>
21
#include <libknot/descriptor.h>
22

23 24
#include "lib/cache/api.h"
#include "lib/cache/cdb_api.h"
25
#include "lib/utils.h"
26
#include "daemon/bindings.h"
27
#include "daemon/worker.h"
28
#include "daemon/tls.h"
29
#include "daemon/zimport.h"
30

31 32 33
#define xstr(s) str(s)
#define str(s) #s

34 35 36
/** @internal Annotate for static checkers. */
KR_NORETURN int lua_error (lua_State *L);

37 38 39 40 41 42 43
/** @internal Prefix error with file:line */
static int format_error(lua_State* L, const char *err)
{
	lua_Debug d;
	lua_getstack(L, 1, &d);
	/* error message prefix */
	lua_getinfo(L, "Sln", &d);
44 45 46 47 48 49 50 51 52
	if (strncmp(d.short_src, "[", 1) != 0) {
		lua_pushstring(L, d.short_src);
		lua_pushstring(L, ":");
		lua_pushnumber(L, d.currentline);
		lua_pushstring(L, ": error: ");
		lua_concat(L, 4);
	} else {
		lua_pushstring(L, "error: ");
	}
53 54
	/* error message */
	lua_pushstring(L, err);
55
	lua_concat(L,  2);
56 57 58
	return 1;
}

59 60 61 62 63 64 65
static inline struct worker_ctx *wrk_luaget(lua_State *L) {
	lua_getglobal(L, "__worker");
	struct worker_ctx *worker = lua_touserdata(L, -1);
	lua_pop(L, 1);
	return worker;
}

66 67 68 69
/** List loaded modules */
static int mod_list(lua_State *L)
{
	struct engine *engine = engine_luaget(L);
70
	lua_newtable(L);
71
	for (unsigned i = 0; i < engine->modules.len; ++i) {
72
		struct kr_module *module = engine->modules.at[i];
73
		lua_pushstring(L, module->name);
74
		lua_rawseti(L, -2, i + 1);
75
	}
76
	return 1;
77 78 79 80 81 82 83 84
}

/** Load module. */
static int mod_load(lua_State *L)
{
	/* Check parameters */
	int n = lua_gettop(L);
	if (n != 1 || !lua_isstring(L, 1)) {
85
		format_error(L, "expected 'load(string name)'");
86 87
		lua_error(L);
	}
88
	/* Parse precedence declaration */
89
	char *declaration = strdup(lua_tostring(L, 1));
90 91 92 93 94 95
	if (!declaration) {
		return kr_error(ENOMEM);
	}
	const char *name = strtok(declaration, " ");
	const char *precedence = strtok(NULL, " ");
	const char *ref = strtok(NULL, " ");
96 97
	/* Load engine module */
	struct engine *engine = engine_luaget(L);
98
	int ret = engine_register(engine, name, precedence, ref);
99
	free(declaration);
100
	if (ret != 0) {
101 102 103 104 105
		if (ret == kr_error(EIDRM)) {
			format_error(L, "referenced module not found");
		} else {
			format_error(L, kr_strerror(ret));
		}
106 107
		lua_error(L);
	}
108 109 110

	lua_pushboolean(L, 1);
	return 1;
111 112 113 114 115
}

/** Unload module. */
static int mod_unload(lua_State *L)
{
116 117 118
	/* Check parameters */
	int n = lua_gettop(L);
	if (n != 1 || !lua_isstring(L, 1)) {
Marek Vavruša's avatar
Marek Vavruša committed
119
		format_error(L, "expected 'unload(string name)'");
120 121 122 123 124 125
		lua_error(L);
	}
	/* Unload engine module */
	struct engine *engine = engine_luaget(L);
	int ret = engine_unregister(engine, lua_tostring(L, 1));
	if (ret != 0) {
126
		format_error(L, kr_strerror(ret));
127 128
		lua_error(L);
	}
129 130 131

	lua_pushboolean(L, 1);
	return 1;
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
}

int lib_modules(lua_State *L)
{
	static const luaL_Reg lib[] = {
		{ "list",   mod_list },
		{ "load",   mod_load },
		{ "unload", mod_unload },
		{ NULL, NULL }
	};

	register_lib(L, "modules", lib);
	return 1;
}

147 148 149 150 151 152 153 154 155 156 157 158 159 160
/** Append 'addr = {port = int, udp = bool, tcp = bool}' */
static int net_list_add(const char *key, void *val, void *ext)
{
	lua_State *L = (lua_State *)ext;
	endpoint_array_t *ep_array = val;
	lua_newtable(L);
	for (size_t i = ep_array->len; i--;) {
		struct endpoint *ep = ep_array->at[i];
		lua_pushinteger(L, ep->port);
		lua_setfield(L, -2, "port");
		lua_pushboolean(L, ep->flags & NET_UDP);
		lua_setfield(L, -2, "udp");
		lua_pushboolean(L, ep->flags & NET_TCP);
		lua_setfield(L, -2, "tcp");
161 162
		lua_pushboolean(L, ep->flags & NET_TLS);
		lua_setfield(L, -2, "tls");
163 164 165 166 167
	}
	lua_setfield(L, -2, key);
	return kr_ok();
}

168 169 170
/** List active endpoints. */
static int net_list(lua_State *L)
{
171 172 173 174
	struct engine *engine = engine_luaget(L);
	lua_newtable(L);
	map_walk(&engine->net.endpoints, net_list_add, L);
	return 1;
175 176
}

177 178
/** Listen on an address list represented by the top of lua stack. */
static int net_listen_addrs(lua_State *L, int port, int flags)
179
{
180 181 182 183 184
	/* Case: table with 'addr' field; only follow that field directly. */
	lua_getfield(L, -1, "addr");
	if (!lua_isnil(L, -1)) {
		lua_replace(L, -2);
	} else {
185 186 187
		lua_pop(L, 1);
	}

188 189 190 191 192
	/* Case: string, representing a single address. */
	const char *str = lua_tostring(L, -1);
	if (str != NULL) {
		struct engine *engine = engine_luaget(L);
		int ret = network_listen(&engine->net, str, port, flags);
193
		if (ret != 0) {
194
			kr_log_info("[system] bind to '%s@%d' %s\n",
195
					str, port, kr_strerror(ret));
196
		}
197
		return ret == 0;
198 199
	}

200 201 202 203 204 205 206 207 208 209 210 211
	/* Last case: table where all entries are added recursively. */
	if (!lua_istable(L, -1)) {
		format_error(L, "bad type for address");
		lua_error(L);
		return 0;
	}
	lua_pushnil(L);
	while (lua_next(L, -2)) {
		if (net_listen_addrs(L, port, flags) == 0)
			return 0;
		lua_pop(L, 1);
	}
212 213 214
	return 1;
}

215 216 217 218 219 220 221 222 223 224 225
static bool table_get_flag(lua_State *L, int index, const char *key, bool def)
{
	bool result = def;
	lua_getfield(L, index, key);
	if (lua_isboolean(L, -1)) {
		result = lua_toboolean(L, -1);
	}
	lua_pop(L, 1);
	return result;
}

226 227 228
/** Listen on endpoint. */
static int net_listen(lua_State *L)
{
229 230
	/* Check parameters */
	int n = lua_gettop(L);
231 232
	if (n < 1 || n > 3) {
		format_error(L, "expected one to three arguments; usage:\n"
233
			     "net.listen(addressses, [port = " xstr(KR_DNS_PORT) ", flags = {tls = (port == " xstr(KR_DNS_TLS_PORT) ")}])\n");
234 235 236
		lua_error(L);
	}

237 238 239 240
	int port = KR_DNS_PORT;
	if (n > 1 && lua_isnumber(L, 2)) {
		port = lua_tointeger(L, 2);
	}
241

242 243 244 245 246
	bool tls = (port == KR_DNS_TLS_PORT);
	if (n > 2 && lua_istable(L, 3)) {
		tls = table_get_flag(L, 3, "tls", tls);
	}
	int flags = tls ? (NET_TCP|NET_TLS) : (NET_TCP|NET_UDP);
247 248 249 250 251 252
	
	/* Now focus on the first argument. */
	lua_pop(L, n - 1);
	int res = net_listen_addrs(L, port, flags);
	lua_pushboolean(L, res);
	return res;
253 254 255 256
}

/** Close endpoint. */
static int net_close(lua_State *L)
257
{
258 259 260
	/* Check parameters */
	int n = lua_gettop(L);
	if (n < 2) {
Marek Vavruša's avatar
Marek Vavruša committed
261
		format_error(L, "expected 'close(string addr, number port)'");
262 263 264 265 266 267 268 269
		lua_error(L);
	}

	/* Open resolution context cache */
	struct engine *engine = engine_luaget(L);
	int ret = network_close(&engine->net, lua_tostring(L, 1), lua_tointeger(L, 2));
	lua_pushboolean(L, ret == 0);
	return 1;
270 271
}

272
/** List available interfaces. */
273 274 275 276
static int net_interfaces(lua_State *L)
{
	/* Retrieve interface list */
	int count = 0;
277
	char buf[INET6_ADDRSTRLEN]; /* https://tools.ietf.org/html/rfc4291 */
278 279 280
	uv_interface_address_t *info = NULL;
	uv_interface_addresses(&info, &count);
	lua_newtable(L);
281
	for (int i = 0; i < count; ++i) {
282
		uv_interface_address_t iface = info[i];
283 284 285 286 287
		lua_getfield(L, -1, iface.name);
		if (lua_isnil(L, -1)) {
			lua_pop(L, 1);
			lua_newtable(L);
		}
288 289

		/* Address */
290 291 292 293 294 295
		lua_getfield(L, -1, "addr");
		if (lua_isnil(L, -1)) {
			lua_pop(L, 1);
			lua_newtable(L);
		}
		if (iface.address.address4.sin_family == AF_INET) {
296
			uv_ip4_name(&iface.address.address4, buf, sizeof(buf));
297
		} else if (iface.address.address4.sin_family == AF_INET6) {
298
			uv_ip6_name(&iface.address.address6, buf, sizeof(buf));
299 300
		} else {
			buf[0] = '\0';
301 302
		}
		lua_pushstring(L, buf);
Marek Vavruša's avatar
Marek Vavruša committed
303
		lua_rawseti(L, -2, lua_rawlen(L, -2) + 1);
304 305 306 307 308 309
		lua_setfield(L, -2, "addr");

		/* Hardware address. */
		char *p = buf;
		memset(buf, 0, sizeof(buf));
		for (unsigned k = 0; k < sizeof(iface.phys_addr); ++k) {
310
			sprintf(p, "%s%.2x", k > 0 ? ":" : "", iface.phys_addr[k] & 0xff);
311
			p += 3;
312 313
		}
		lua_pushstring(L, buf);
314
		lua_setfield(L, -2, "mac");
315 316

		/* Push table */
317
		lua_setfield(L, -2, iface.name);
318 319 320 321 322 323
	}
	uv_free_interface_addresses(info, count);

	return 1;
}

324 325 326 327 328 329 330 331 332 333
/** Set UDP maximum payload size. */
static int net_bufsize(lua_State *L)
{
	struct engine *engine = engine_luaget(L);
	knot_rrset_t *opt_rr = engine->resolver.opt_rr;
	if (!lua_isnumber(L, 1)) {
		lua_pushnumber(L, knot_edns_get_payload(opt_rr));
		return 1;
	}
	int bufsize = lua_tointeger(L, 1);
334
	if (bufsize < 512 || bufsize > UINT16_MAX) {
335
		format_error(L, "bufsize must be within <512, " xstr(UINT16_MAX) ">");
336 337 338 339 340 341
		lua_error(L);
	}
	knot_edns_set_payload(opt_rr, (uint16_t) bufsize);
	return 0;
}

342 343 344 345 346 347 348 349 350 351 352 353
/** Set TCP pipelining size. */
static int net_pipeline(lua_State *L)
{
	struct worker_ctx *worker = wrk_luaget(L);
	if (!worker) {
		return 0;
	}
	if (!lua_isnumber(L, 1)) {
		lua_pushnumber(L, worker->tcp_pipeline_max);
		return 1;
	}
	int len = lua_tointeger(L, 1);
354
	if (len < 0 || len > UINT16_MAX) {
355
		format_error(L, "tcp_pipeline must be within <0, " xstr(UINT16_MAX) ">");
356 357 358 359 360 361 362
		lua_error(L);
	}
	worker->tcp_pipeline_max = len;
	lua_pushnumber(L, len);
	return 1;
}

363
static int net_tls(lua_State *L)
364
{
365 366 367 368 369 370
	struct engine *engine = engine_luaget(L);
	if (!engine) {
		return 0;
	}
	struct network *net = &engine->net;
	if (!net) {
371
		return 0;
372 373
	}

374
	/* Only return current credentials. */
375
	if (lua_gettop(L) == 0) {
376 377 378 379 380 381 382 383 384
		/* No credentials configured yet. */
		if (!net->tls_credentials) {
			return 0;
		}
		lua_newtable(L);
		lua_pushstring(L, net->tls_credentials->tls_cert);
		lua_setfield(L, -2, "cert_file");
		lua_pushstring(L, net->tls_credentials->tls_key);
		lua_setfield(L, -2, "key_file");
385
		return 1;
386 387
	}

388 389 390
	if ((lua_gettop(L) != 2) || !lua_isstring(L, 1) || !lua_isstring(L, 2)) {
		lua_pushstring(L, "net.tls takes two parameters: (\"cert_file\", \"key_file\")");
		lua_error(L);
391 392
	}

393
	int r = tls_certificate_set(net, lua_tostring(L, 1), lua_tostring(L, 2));
394
	if (r != 0) {
395
		lua_pushstring(L, kr_strerror(r));
396 397 398 399 400 401 402
		lua_error(L);
	}

	lua_pushboolean(L, true);
	return 1;
}

403 404 405 406 407 408 409 410 411 412
static int print_tls_param(const char *key, void *val, void *data)
{
	if (!val) {
		return 0;
	}

	struct tls_client_paramlist_entry *entry = (struct tls_client_paramlist_entry *)val;

	lua_State *L = (lua_State *)data;

413
	lua_createtable(L, 0, 3);
414

415
	lua_createtable(L, entry->pins.len, 0);
416 417 418 419 420 421
	for (size_t i = 0; i < entry->pins.len; ++i) {
		lua_pushnumber(L, i + 1);
		lua_pushstring(L, entry->pins.at[i]);
		lua_settable(L, -3);
	}
	lua_setfield(L, -2, "pins");
422 423

	lua_createtable(L, entry->ca_files.len, 0);
424 425 426 427 428
	for (size_t i = 0; i < entry->ca_files.len; ++i) {
		lua_pushnumber(L, i + 1);
		lua_pushstring(L, entry->ca_files.at[i]);
		lua_settable(L, -3);
	}
429 430 431
	lua_setfield(L, -2, "ca_files");

	lua_createtable(L, entry->hostnames.len, 0);
432 433 434 435 436 437
	for (size_t i = 0; i < entry->hostnames.len; ++i) {
		lua_pushnumber(L, i + 1);
		lua_pushstring(L, entry->hostnames.at[i]);
		lua_settable(L, -3);
	}
	lua_setfield(L, -2, "hostnames");
438

439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456
	lua_setfield(L, -2, key);

	return 0;
}

static int print_tls_client_params(lua_State *L)
{
	struct engine *engine = engine_luaget(L);
	if (!engine) {
		return 0;
	}
	struct network *net = &engine->net;
	if (!net) {
		return 0;
	}
	if (net->tls_client_params.root == 0 ) {
		return 0;
	}
457
	lua_newtable(L);
458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479
	map_walk(&net->tls_client_params, print_tls_param, (void *)L);
	return 1;
}


static int net_tls_client(lua_State *L)
{
	struct engine *engine = engine_luaget(L);
	if (!engine) {
		return 0;
	}
	struct network *net = &engine->net;
	if (!net) {
		return 0;
	}

	/* Only return current credentials. */
	if (lua_gettop(L) == 0) {
		return print_tls_client_params(L);
	}

	const char *full_addr = NULL;
480
	bool pin_exists = false;
481
	bool hostname_exists = false;
482 483
	if ((lua_gettop(L) == 1) && lua_isstring(L, 1)) {
		full_addr = lua_tostring(L, 1);
484
	} else if ((lua_gettop(L) == 2) && lua_isstring(L, 1) && lua_istable(L, 2)) {
485
		full_addr = lua_tostring(L, 1);
486 487 488
		pin_exists = true;
	} else if ((lua_gettop(L) == 3) && lua_isstring(L, 1) && lua_istable(L, 2)) {
		full_addr = lua_tostring(L, 1);
489
		hostname_exists = true;
490 491 492 493
	} else if ((lua_gettop(L) == 4) && lua_isstring(L, 1) &&
		    lua_istable(L, 2) && lua_istable(L, 3)) {
		full_addr = lua_tostring(L, 1);
		pin_exists = true;
494
		hostname_exists = true;
495
	} else {
496
		format_error(L, "net.tls_client takes one parameter (\"address\"), two parameters (\"address\",\"pin\"), three parameters (\"address\", \"ca_file\", \"hostname\") or four ones: (\"address\", \"pin\", \"ca_file\", \"hostname\")");
497 498 499 500 501 502 503 504 505 506 507
		lua_error(L);
	}

	char addr[INET6_ADDRSTRLEN];
	uint16_t port = 0;
	if (kr_straddr_split(full_addr, addr, sizeof(addr), &port) != kr_ok()) {
		format_error(L, "invalid IP address");
		lua_error(L);
	}

	if (port == 0) {
508
		port = 853;
509 510
	}

511
	if (!pin_exists && !hostname_exists) {
512
		int r = tls_client_params_set(&net->tls_client_params,
513 514
					      addr, port, NULL,
					      TLS_CLIENT_PARAM_NONE);
515
		if (r != 0) {
516
			lua_pushstring(L, kr_strerror(r));
517 518 519 520 521 522 523 524 525 526 527 528 529 530 531
			lua_error(L);
		}

		lua_pushboolean(L, true);
		return 1;
	}

	if (pin_exists) {
		/* iterate over table with pins
		 * http://www.lua.org/manual/5.1/manual.html#lua_next */
		lua_pushnil(L); /* first key */
		while (lua_next(L, 2)) {  /* pin table is in stack at index 2 */
			/* pin now at index -1, key at index -2*/
			const char *pin = lua_tostring(L, -1);
			int r = tls_client_params_set(&net->tls_client_params,
532 533
						      addr, port, pin,
						      TLS_CLIENT_PARAM_PIN);
534
			if (r != 0) {
535
				lua_pushstring(L, kr_strerror(r));
536 537 538 539 540 541 542 543
				lua_error(L);
			}
			lua_pop(L, 1);
		}
	}

	int ca_table_index = 2;
	int hostname_table_index = 3;
544
	if (hostname_exists) {
545 546 547 548 549 550 551 552 553
		if (pin_exists) {
			ca_table_index = 3;
			hostname_table_index = 4;
		}
	} else {
		lua_pushboolean(L, true);
		return 1;
	}

554 555
	/* iterate over hostnames,
	 * it must be done before iterating over ca filenames */
556
	lua_pushnil(L);
557 558
	while (lua_next(L, hostname_table_index)) {
		const char *hostname = lua_tostring(L, -1);
559
		int r = tls_client_params_set(&net->tls_client_params,
560 561
					      addr, port, hostname,
					      TLS_CLIENT_PARAM_HOSTNAME);
562
		if (r != 0) {
563
			lua_pushstring(L, kr_strerror(r));
564 565 566 567 568 569
			lua_error(L);
		}
		/* removes 'value'; keeps 'key' for next iteration */
		lua_pop(L, 1);
	}

570
	/* iterate over ca filenames */
571
	lua_pushnil(L);
572 573 574
	size_t num_of_ca_files = 0;
	while (lua_next(L, ca_table_index)) {
		const char *ca_file = lua_tostring(L, -1);
575
		int r = tls_client_params_set(&net->tls_client_params,
576 577
					      addr, port, ca_file,
					      TLS_CLIENT_PARAM_CA);
578
		if (r != 0) {
579
			lua_pushstring(L, kr_strerror(r));
580 581
			lua_error(L);
		}
582
		num_of_ca_files += 1;
583 584
		/* removes 'value'; keeps 'key' for next iteration */
		lua_pop(L, 1);
585 586
	}

587 588 589 590 591 592 593 594 595 596 597
	if (num_of_ca_files == 0) {
		/* No ca files were explicitly configured, so use system CA */
		int r = tls_client_params_set(&net->tls_client_params,
					      addr, port, NULL,
					      TLS_CLIENT_PARAM_CA);
		if (r != 0) {
			lua_pushstring(L, kr_strerror(r));
			lua_error(L);
		}
	}

598 599 600 601
	lua_pushboolean(L, true);
	return 1;
}

602 603 604 605 606 607
static int net_tls_padding(lua_State *L)
{
	struct engine *engine = engine_luaget(L);

	/* Only return current padding. */
	if (lua_gettop(L) == 0) {
608 609 610 611 612 613
		if (engine->resolver.tls_padding < 0) {
			lua_pushboolean(L, true);
			return 1;
		} else if (engine->resolver.tls_padding == 0) {
			lua_pushboolean(L, false);
			return 1;
614 615 616 617 618
		}
		lua_pushinteger(L, engine->resolver.tls_padding);
		return 1;
	}

619 620
	if ((lua_gettop(L) != 1)) {
		lua_pushstring(L, "net.tls_padding takes one parameter: (\"padding\")");
621 622
		lua_error(L);
	}
623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638
	if (lua_isboolean(L, 1)) {
		bool x = lua_toboolean(L, 1);
		if (x) {
			engine->resolver.tls_padding = -1;
		} else {
			engine->resolver.tls_padding = 0;
		}
	} else if (lua_isnumber(L, 1)) {
		int padding = lua_tointeger(L, 1);
		if ((padding < 0) || (padding > MAX_TLS_PADDING)) {
			lua_pushstring(L, "net.tls_padding parameter has to be true, false, or a number between <0, " xstr(MAX_TLS_PADDING) ">");
			lua_error(L);
		}
		engine->resolver.tls_padding = padding;
	} else {
		lua_pushstring(L, "net.tls_padding parameter has to be true, false, or a number between <0, " xstr(MAX_TLS_PADDING) ">");
639 640 641 642 643 644
		lua_error(L);
	}
	lua_pushboolean(L, true);
	return 1;
}

645 646
/** Shorter salt can't contain much entropy. */
#define net_tls_sticket_MIN_SECRET_LEN 32
647

648
static int net_tls_sticket_secret_string(lua_State *L)
649
{
650
	struct network *net = &engine_luaget(L)->net;
651

652 653
	size_t secret_len;
	const char *secret;
654

655 656 657 658 659 660 661 662 663 664 665 666 667 668
	if (lua_gettop(L) == 0) {
		/* Zero-length secret, implying random key. */
		secret_len = 0;
		secret = NULL;
	} else {
		if (lua_gettop(L) != 1 || !lua_isstring(L, 1)) {
			lua_pushstring(L,
				"net.tls_sticket_secret takes one parameter: (\"secret string\")");
			lua_error(L);
		}
		secret = lua_tolstring(L, 1, &secret_len);
		if (secret_len < net_tls_sticket_MIN_SECRET_LEN || !secret) {
			lua_pushstring(L, "net.tls_sticket_secret - the secret is shorter than "
						xstr(net_tls_sticket_MIN_SECRET_LEN) " bytes");
669 670 671 672
			lua_error(L);
		}
	}

673 674 675 676 677 678 679 680 681
	tls_session_ticket_ctx_destroy(net->tls_session_ticket_ctx);
	net->tls_session_ticket_ctx =
		tls_session_ticket_ctx_create(net->loop, secret, secret_len);
	if (net->tls_session_ticket_ctx == NULL) {
		lua_pushstring(L,
			"net.tls_sticket_secret_string - can't create session ticket context");
		lua_error(L);
	}

682 683 684 685
	lua_pushboolean(L, true);
	return 1;
}

686
static int net_tls_sticket_secret_file(lua_State *L)
687 688
{
	if (lua_gettop(L) != 1 || !lua_isstring(L, 1)) {
689 690
		lua_pushstring(L,
			"net.tls_sticket_secret_file takes one parameter: (\"file name\")");
691 692 693 694 695
		lua_error(L);
	}

	const char *file_name = lua_tostring(L, 1);
	if (strlen(file_name) == 0) {
696
		lua_pushstring(L, "net.tls_sticket_secret_file - empty file name");
697 698 699 700 701
		lua_error(L);
	}

	FILE *fp = fopen(file_name, "r");
	if (fp == NULL) {
702 703
		lua_pushfstring(L, "net.tls_sticket_secret_file - can't open file '%s': %s",
				file_name, strerror(errno));
704 705 706
		lua_error(L);
	}

707 708 709 710 711 712 713
	char secret_buf[TLS_SESSION_TICKET_SECRET_MAX_LEN];
	const size_t secret_len = fread(secret_buf, 1, sizeof(secret_buf), fp);
	int err = ferror(fp);
	if (err) {
		lua_pushfstring(L,
			"net.tls_sticket_secret_file - error reading from file '%s': %s",
			file_name, strerror(err));
714 715
		lua_error(L);
	}
716 717 718 719 720 721
	if (secret_len < net_tls_sticket_MIN_SECRET_LEN) {
		lua_pushfstring(L,
			"net.tls_sticket_secret_file - file '%s' is shorter than "
				xstr(net_tls_sticket_MIN_SECRET_LEN) " bytes",
			file_name);
		lua_error(L);
722 723
	}
	fclose(fp);
724 725 726 727 728 729 730 731 732 733 734

	struct network *net = &engine_luaget(L)->net;

	tls_session_ticket_ctx_destroy(net->tls_session_ticket_ctx);
	net->tls_session_ticket_ctx =
		tls_session_ticket_ctx_create(net->loop, secret_buf, secret_len);
	if (net->tls_session_ticket_ctx == NULL) {
		lua_pushstring(L,
			"net.tls_sticket_secret_file - can't create session ticket context");
		lua_error(L);
	}
735 736 737 738
	lua_pushboolean(L, true);
	return 1;
}

739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795
static int net_outgoing(lua_State *L, int family)
{
	struct worker_ctx *worker = wrk_luaget(L);
	union inaddr *addr;
	if (family == AF_INET)
		addr = (union inaddr*)&worker->out_addr4;
	else
		addr = (union inaddr*)&worker->out_addr6;

	if (lua_gettop(L) == 0) { /* Return the current value. */
		if (addr->ip.sa_family == AF_UNSPEC) {
			lua_pushnil(L);
			return 1;
		}
		if (addr->ip.sa_family != family) {
			assert(false);
			lua_error(L);
		}
		char addr_buf[INET6_ADDRSTRLEN];
		int err;
		if (family == AF_INET)
			err = uv_ip4_name(&addr->ip4, addr_buf, sizeof(addr_buf));
		else
			err = uv_ip6_name(&addr->ip6, addr_buf, sizeof(addr_buf));
		if (err)
			lua_error(L);
		lua_pushstring(L, addr_buf);
		return 1;
	}

	if ((lua_gettop(L) != 1) || (!lua_isstring(L, 1) && !lua_isnil(L, 1))) {
		format_error(L, "net.outgoing_vX takes one address string parameter or nil");
		lua_error(L);
	}

	if (lua_isnil(L, 1)) {
		addr->ip.sa_family = AF_UNSPEC;
		return 1;
	}

	const char *addr_str = lua_tostring(L, 1);
	int err;
	if (family == AF_INET)
		err = uv_ip4_addr(addr_str, 0, &addr->ip4);
	else
		err = uv_ip6_addr(addr_str, 0, &addr->ip6);
	if (err) {
		format_error(L, "net.outgoing_vX: failed to parse the address");
		lua_error(L);
	}
	lua_pushboolean(L, true);
	return 1;
}

static int net_outgoing_v4(lua_State *L) { return net_outgoing(L, AF_INET); }
static int net_outgoing_v6(lua_State *L) { return net_outgoing(L, AF_INET6); }

796
static int net_update_timeout(lua_State *L, uint64_t *timeout, const char *name)
797 798 799
{
	/* Only return current idle timeout. */
	if (lua_gettop(L) == 0) {
800
		lua_pushnumber(L, *timeout);
801 802 803 804
		return 1;
	}

	if ((lua_gettop(L) != 1)) {
805 806
		lua_pushstring(L, name);
		lua_pushstring(L, " takes one parameter: (\"idle timeout\")");
807 808 809 810 811 812
		lua_error(L);
	}

	if (lua_isnumber(L, 1)) {
		int idle_timeout = lua_tointeger(L, 1);
		if (idle_timeout <= 0) {
813 814
			lua_pushstring(L, name);
			lua_pushstring(L, " parameter has to be positive number");
815 816
			lua_error(L);
		}
817
		*timeout = idle_timeout;
818
	} else {
819 820
		lua_pushstring(L, name);
		lua_pushstring(L, " parameter has to be positive number");
821 822 823 824 825 826
		lua_error(L);
	}
	lua_pushboolean(L, true);
	return 1;
}

827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842
static int net_tcp_in_idle(lua_State *L)
{
	struct engine *engine = engine_luaget(L);
	struct network *net = &engine->net;

	return net_update_timeout(L, &net->tcp.in_idle_timeout, "net.tcp_in_idle");
}

static int net_tls_handshake_timeout(lua_State *L)
{
	struct engine *engine = engine_luaget(L);
	struct network *net = &engine->net;

	return net_update_timeout(L, &net->tcp.tls_handshake_timeout, "net.tls_handshake_timeout");
}

843 844 845
int lib_net(lua_State *L)
{
	static const luaL_Reg lib[] = {
846 847 848 849 850 851
		{ "list",         net_list },
		{ "listen",       net_listen },
		{ "close",        net_close },
		{ "interfaces",   net_interfaces },
		{ "bufsize",      net_bufsize },
		{ "tcp_pipeline", net_pipeline },
852
		{ "tls",          net_tls },
853 854
		{ "tls_server",   net_tls },
		{ "tls_client",   net_tls_client },
855
		{ "tls_padding",  net_tls_padding },
856 857
		{ "tls_sticket_secret", net_tls_sticket_secret_string },
		{ "tls_sticket_secret_file", net_tls_sticket_secret_file },
858 859
		{ "outgoing_v4",  net_outgoing_v4 },
		{ "outgoing_v6",  net_outgoing_v6 },
860
		{ "tcp_in_idle",  net_tcp_in_idle },
861
		{ "tls_handshake_timeout",  net_tls_handshake_timeout },
862 863 864 865 866 867
		{ NULL, NULL }
	};
	register_lib(L, "net", lib);
	return 1;
}

868 869 870 871 872 873 874 875 876 877 878 879 880 881 882


/** @internal return cache, or throw lua error if not open */
struct kr_cache * cache_assert_open(lua_State *L)
{
	struct engine *engine = engine_luaget(L);
	struct kr_cache *cache = &engine->resolver.cache;
	assert(cache);
	if (!cache || !kr_cache_is_open(cache)) {
		format_error(L, "no cache is open yet, use cache.open() or cache.size, etc.");
		lua_error(L);
	}
	return cache;
}

883 884 885 886 887 888
/** Return available cached backends. */
static int cache_backends(lua_State *L)
{
	struct engine *engine = engine_luaget(L);

	lua_newtable(L);
889 890 891 892
	for (unsigned i = 0; i < engine->backends.len; ++i) {
		const struct kr_cdb_api *api = engine->backends.at[i];
		lua_pushboolean(L, api == engine->resolver.cache.api);
		lua_setfield(L, -2, api->name);
893 894 895 896
	}
	return 1;
}

897 898 899
/** Return number of cached records. */
static int cache_count(lua_State *L)
{
900
	struct kr_cache *cache = cache_assert_open(L);
901

902 903
	int count = cache->api->count(cache->db);
	if (count >= 0) {
904 905
		/* First key is a version counter, omit it if nonempty. */
		lua_pushinteger(L, count ? count - 1 : 0);
906 907 908
		return 1;
	}
	return 0;
909 910
}

911 912
/** Return time of last checkpoint, or re-set it if passed `true`. */
static int cache_checkpoint(lua_State *L)
913
{
914
	struct kr_cache *cache = cache_assert_open(L);
915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933

	if (lua_gettop(L) == 0) { /* Return the current value. */
		lua_newtable(L);
		lua_pushnumber(L, cache->checkpoint_monotime);
		lua_setfield(L, -2, "monotime");
		lua_newtable(L);
		lua_pushnumber(L, cache->checkpoint_walltime.tv_sec);
		lua_setfield(L, -2, "sec");
		lua_pushnumber(L, cache->checkpoint_walltime.tv_usec);
		lua_setfield(L, -2, "usec");
		lua_setfield(L, -2, "walltime");
		return 1;
	}

	if (lua_gettop(L) != 1 || !lua_isboolean(L, 1) || !lua_toboolean(L, 1)) {
		format_error(L, "cache.checkpoint() takes no parameters or a true value");
		lua_error(L);
	}
	kr_cache_make_checkpoint(cache);
934 935 936
	return 1;
}

937 938 939
/** Return cache statistics. */
static int cache_stats(lua_State *L)
{
940
	struct kr_cache *cache = cache_assert_open(L);
941 942 943 944 945 946 947 948 949 950 951 952
	lua_newtable(L);
	lua_pushnumber(L, cache->stats.hit);
	lua_setfield(L, -2, "hit");
	lua_pushnumber(L, cache->stats.miss);
	lua_setfield(L, -2, "miss");
	lua_pushnumber(L, cache->stats.insert);
	lua_setfield(L, -2, "insert");
	lua_pushnumber(L, cache->stats.delete);
	lua_setfield(L, -2, "delete");
	return 1;
}

953
static const struct kr_cdb_api *cache_select(struct engine *engine, const char **conf)
954 955
{
	/* Return default backend */
956 957
	if (*conf == NULL || !strstr(*conf, "://")) {
		return engine->backends.at[0];
958 959 960
	}

	/* Find storage backend from config prefix */
961 962 963 964 965
	for (unsigned i = 0; i < engine->backends.len; ++i) {
		const struct kr_cdb_api *api = engine->backends.at[i];
		if (strncmp(*conf, api->name, strlen(api->name)) == 0) {
			*conf += strlen(api->name) + strlen("://");
			return api;
966 967 968 969 970 971
		}
	}

	return NULL;
}

972 973
static int cache_max_ttl(lua_State *L)
{
974
	struct kr_cache *cache = cache_assert_open(L);
975 976 977 978 979 980 981 982 983

	int n = lua_gettop(L);
	if (n > 0) {
		if (!lua_isnumber(L, 1)) {
			format_error(L, "expected 'max_ttl(number ttl)'");
			lua_error(L);
		}
		uint32_t min = cache->ttl_min;
		int64_t ttl = lua_tonumber(L, 1);
984
		if (ttl < 0 || ttl < min || ttl > UINT32_MAX) {
985
			format_error(L, "max_ttl must be larger than minimum TTL, and in range <1, " xstr(UINT32_MAX) ">'");
986 987 988 989 990 991 992 993 994 995 996
			lua_error(L);
		}
		cache->ttl_max = ttl;
	}
	lua_pushinteger(L, cache->ttl_max);
	return 1;
}


static int cache_min_ttl(lua_State *L)
{
997
	struct kr_cache *cache = cache_assert_open(L);
998 999 1000 1001 1002 1003 1004 1005 1006

	int n = lua_gettop(L);
	if (n > 0) {
		if (!lua_isnumber(L, 1)) {
			format_error(L, "expected 'min_ttl(number ttl)'");
			lua_error(L);
		}
		uint32_t max = cache->ttl_max;
		int64_t ttl = lua_tonumber(L, 1);
1007
		if (ttl < 0 || ttl > max || ttl > UINT32_MAX) {
1008
			format_error(L, "min_ttl must be smaller than maximum TTL, and in range <0, " xstr(UINT32_MAX) ">'");
1009 1010 1011 1012 1013 1014 1015 1016
			lua_error(L);
		}
		cache->ttl_min = ttl;
	}
	lua_pushinteger(L, cache->ttl_min);
	return 1;
}

1017 1018 1019 1020 1021
/** Open cache */
static int cache_open(lua_State *L)
{
	/* Check parameters */
	int n = lua_gettop(L);
1022
	if (n < 1 || !lua_isnumber(L, 1)) {
Marek Vavruša's avatar
Marek Vavruša committed
1023
		format_error(L, "expected 'open(number max_size, string config = \"\")'");
1024 1025 1026
		lua_error(L);
	}

1027
	/* Select cache storage backend */
1028
	struct engine *engine = engine_luaget(L);
1029 1030 1031

	lua_Number csize_lua = lua_tonumber(L, 1);
	if (!(csize_lua >= 8192 && csize_lua < SIZE_MAX)) { /* min. is basically arbitrary */
1032
		format_error(L, "invalid cache size specified, it must be in range <8192, " xstr(SIZE_MAX)  ">");
1033 1034 1035 1036
		lua_error(L);
	}
	size_t cache_size = csize_lua;

1037
	const char *conf = n > 1 ? lua_tostring(L, 2) : NULL;
1038
	const char *uri = conf;
1039 1040
	const struct kr_cdb_api *api = cache_select(engine, &conf);
	if (!api) {
1041 1042 1043 1044 1045
		format_error(L, "unsupported cache backend");
		lua_error(L);
	}

	/* Close if already open */
1046
	kr_cache_close(&engine->resolver.cache);
1047

1048
	/* Reopen cache */
1049 1050 1051 1052 1053
	struct kr_cdb_opts opts = {
		(conf && strlen(conf)) ? conf : ".",
		cache_size
	};
	int ret = kr_cache_open(&engine->resolver.cache, api, &opts, engine->pool);
1054
	if (ret != 0) {
1055 1056 1057 1058 1059 1060
		char cwd[PATH_MAX];
		if(getcwd(cwd, sizeof(cwd)) == NULL) {
			const char errprefix[] = "<invalid working directory>";
			strncpy(cwd, errprefix, sizeof(cwd));
		}
		return luaL_error(L, "can't open cache path '%s'; working directory '%s'", opts.path, cwd);
1061 1062
	}

1063 1064
	/* Store current configuration */
	lua_getglobal(L, "cache");
1065
	lua_pushstring(L, "current_size");
1066 1067
	lua_pushnumber(L, cache_size);
	lua_rawset(L, -3);
1068
	lua_pushstring(L, "current_storage");
1069 1070
	lua_pushstring(L, uri);
	lua_rawset(L, -3);
1071
	lua_pop(L, 1);
1072

1073 1074 1075 1076 1077 1078 1079
	lua_pushboolean(L, 1);
	return 1;
}

static int cache_close(lua_State *L)
{
	struct engine *engine = engine_luaget(L);
1080 1081 1082 1083 1084 1085
	struct kr_cache *cache = &engine->resolver.cache;
	if (!kr_cache_is_open(cache)) {
		return 0;
	}

	kr_cache_close(cache);
1086 1087 1088 1089 1090
	lua_getglobal(L, "cache");
	lua_pushstring(L, "current_size");
	lua_pushnumber(L, 0);
	lua_rawset(L, -3);
	lua_pop(L, 1);
1091 1092
	lua_pushboolean(L, 1);
	return 1;
1093 1094
}

1095
#if 0
1096
/** @internal Prefix walk. */
1097 1098
static int cache_prefixed(struct kr_cache *cache, const char *prefix, bool exact_name,
			  knot_db_val_t keyval[][2], int maxcount)
1099 1100 1101
{
	/* Convert to domain name */
	uint8_t buf[KNOT_DNAME_MAXLEN];
1102
	if (!knot_dname_from_str(buf, prefix, sizeof(buf))) {
1103 1104 1105
		return kr_error(EINVAL);
	}
	/* Start prefix search */
1106
	return kr_cache_match(cache, buf, exact_name, keyval, maxcount);
1107
}
1108
#endif
1109 1110 1111 1112

/** Prune expired/invalid records. */
static int cache_prune(lua_State *L)
{
1113
	struct kr_cache *cache = cache_assert_open(L);
1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134
	/* Check parameters */
	int prune_max = UINT16_MAX;
	int n = lua_gettop(L);
	if (n >= 1 && lua_isnumber(L, 1)) {
		prune_max = lua_tointeger(L, 1);
	}

	/* Check if API supports pruning. */
	int ret = kr_error(ENOSYS);
	if (cache->api->prune) {
		ret = cache->api->prune(cache->db, prune_max);
	}
	/* Commit and format result. */
	if (ret < 0) {
		format_error(L, kr_strerror(ret));
		lua_error(L);
	}
	lua_pushinteger(L, ret);
	return 1;
}

1135 1136
/** Clear everything. */
static int cache_clear_everything(lua_State *L)
1137
{
1138
	struct kr_cache *cache = cache_assert_open(L);
1139

1140
	/* Clear records and packets. */
1141 1142 1143 1144 1145 1146 1147
	int ret = kr_cache_clear(cache);
	if (ret < 0) {
		format_error(L, kr_strerror(ret));
		lua_error(L);
	}

	/* Clear reputation tables */
1148
	struct engine *engine = engine_luaget(L);
1149 1150 1151
	lru_reset(engine->resolver.cache_rtt);
	lru_reset(engine->resolver.cache_rep);
	lru_reset(engine->resolver.cache_cookie);
1152 1153 1154 1155
	lua_pushboolean(L, true);
	return 1;
}

1156
#if 0
1157
/** @internal Dump cache key into table on Lua stack. */
1158
static void cache_dump(lua_State *L, knot_db_val_t keyval[])
1159
{
1160
	knot_dname_t dname[KNOT_DNAME_MAXLEN];
1161
	char name[KNOT_DNAME_TXT_MAXLEN];
1162 1163
	uint16_t type;

1164
	int ret = kr_unpack_cache_key(keyval[0], dname, &type);
1165 1166
	if (ret < 0) {
		return;
1167
	}
1168

1169 1170 1171
	ret = !knot_dname_to_str(name, dname, sizeof(name));
	assert(!ret);
	if (ret) return;
1172

1173
	/* If name typemap doesn't exist yet, create it */
1174
	lua_getfield(L, -1, name);
1175 1176 1177 1178 1179
	if (lua_isnil(L, -1)) {
		lua_pop(L, 1);
		lua_newtable(L);
	}
	/* Append to typemap */
1180
	char type_buf[KR_RRTYPE_STR_MAXLEN] = { '\0' };
1181 1182 1183 1184
	knot_rrtype_to_string(type, type_buf, sizeof(type_buf));
	lua_pushboolean(L, true);
	lua_setfield(L, -2, type_buf);
	/* Set name typemap */
1185
	lua_setfield(L, -2, name);
1186 1187
}

1188
/** Query cached records.  TODO: fix caveats in ./README.rst documentation? */
1189 1190
static int cache_get(lua_State *L)
{
1191
	//struct kr_cache *cache = cache_assert_open(L); // to be fixed soon
1192

1193 1194 1195 1196 1197 1198 1199 1200
	/* Check parameters */
	int n = lua_gettop(L);
	if (n < 1 || !lua_isstring(L, 1)) {
		format_error(L, "expected 'cache.get(string key)'");
		lua_error(L);
	}

	/* Retrieve set of keys */
1201 1202 1203
	const char *prefix = lua_tostring(L, 1);
	knot_db_val_t keyval[100][2];
	int ret = cache_prefixed(cache, prefix, false/*FIXME*/, keyval, 100);
1204 1205 1206 1207 1208 1209 1210
	if (ret < 0) {
		format_error(L, kr_strerror(ret));
		lua_error(L);
	}
	/* Format output */
	lua_newtable(L);
	for (int i = 0; i < ret; ++i) {
1211
		cache_dump(L, keyval[i]);
1212 1213 1214
	}
	return 1;
}
1215 1216 1217 1218 1219 1220 1221 1222
#endif
static int cache_get(lua_State *L)
{
	int ret = kr_error(ENOSYS);
	format_error(L, kr_strerror(ret));
	lua_error(L);
	return ret;
}
1223

1224 1225 1226 1227
/** Set time interval for cleaning rtt cache.
 * Servers with score >= KR_NS_TIMEOUTED will be cleaned after
 * this interval ended up, so that they will be able to participate
 * in NS elections again. */
1228
static int cache_ns_tout(lua_State *L)
1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255
{
	struct engine *engine = engine_luaget(L);
	struct kr_context *ctx = &engine->resolver;

	/* Check parameters */
	int n = lua_gettop(L);
	if (n < 1) {
		lua_pushinteger(L, ctx->cache_rtt_tout_retry_interval);
		return 1;
	}

	if (!lua_isnumber(L, 1)) {
		format_error(L, "expected 'cache.ns_tout(interval in ms)'");
		lua_error(L);
	}

	lua_Number interval_lua = lua_tonumber(L, 1);
	if (!(interval_lua >= 0 && interval_lua < UINT_MAX)) {
		format_error(L, "invalid interval specified, it must be in range > 0, < " xstr(UINT_MAX));
		lua_error(L);
	}

	ctx->cache_rtt_tout_retry_interval = interval_lua;
	lua_pushinteger(L, ctx->cache_rtt_tout_retry_interval);
	return 1;
}

1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270
/** Zone import completion callback.
 * Deallocates zone import context. */
static void cache_zone_import_cb(int state, void *param)
{
	assert (param);
	(void)state;
	struct worker_ctx *worker = (struct worker_ctx *)param;
	assert (worker->z_import);
	zi_free(worker->z_import);
	worker->z_import = NULL;
}

/** Import zone from file. */
static int cache_zone_import(lua_State *L)
{
1271 1272 1273
	int ret = -1;
	char msg[128];

1274 1275
	struct worker_ctx *worker = wrk_luaget(L);
	if (!worker) {
1276 1277
		strncpy(msg, "internal error, empty worker pointer", sizeof(msg));
		goto finish;
1278 1279 1280
	}

	if (worker->z_import && zi_import_started(worker->z_import)) {
1281 1282
		strncpy(msg, "import already started", sizeof(msg));
		goto finish;
1283 1284
	}

1285
	(void)cache_assert_open(L); /* just check it in advance */
1286 1287 1288 1289

	/* Check parameters */
	int n = lua_gettop(L);
	if (n < 1 || !lua_isstring(L, 1)) {
1290 1291
		strncpy(msg, "expected 'cache.zone_import(path to zone file)'", sizeof(msg));
		goto finish;
1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303
	}

	/* Parse zone file */
	const char *zone_file = lua_tostring(L, 1);

	const char *default_origin = NULL; /* TODO */
	uint16_t default_rclass = 1;
	uint32_t default_ttl = 0;

	if (worker->z_import == NULL) {
		worker->z_import = zi_allocate(worker, cache_zone_import_cb, worker);
		if (worker->z_import == NULL) {
1304 1305
			strncpy(msg, "can't allocate zone import context", sizeof(msg));
			goto finish;
1306 1307 1308
		}
	}

1309 1310
	ret = zi_zone_import(worker->z_import, zone_file, default_origin,
			     default_rclass, default_ttl);
1311

1312 1313 1314 1315 1316 1317 1318
	lua_newtable(L);
	if (ret == 0) {
		strncpy(msg, "zone file successfully parsed, import started", sizeof(msg));
	} else if (ret == 1) {
		strncpy(msg, "TA not found", sizeof(msg));
	} else {
		strncpy(msg, "error parsing zone file", sizeof(msg));
1319 1320
	}

1321 1322 1323 1324 1325 1326 1327 1328
finish:
	msg[sizeof(msg) - 1] = 0;
	lua_newtable(L);
	lua_pushstring(L, msg);
	lua_setfield(L, -2, "msg");
	lua_pushnumber(L, ret);
	lua_setfield(L, -2, "code");

1329 1330
	return 1;
}
1331

1332 1333 1334
int lib_cache(lua_State *L)
{
	static const luaL_Reg lib[] = {
1335
		{ "backends", cache_backends },
1336
		{ "count",  cache_count },
1337
		{ "stats",  cache_stats },
1338
		{ "checkpoint", cache_checkpoint },
1339 1340
		{ "open",   cache_open },
		{ "close",  cache_close },
1341
		{ "prune",  cache_prune },
1342 1343
		{ "clear_everything", cache_clear_everything },
		{ "get",     cache_get },
1344 1345
		{ "max_ttl", cache_max_ttl },
		{ "min_ttl", cache_min_ttl },
1346
		{ "ns_tout", cache_ns_tout },
1347
		{ "zone_import", cache_zone_import },
1348 1349 1350 1351
		{ NULL, NULL }
	};

	register_lib(L, "cache", lib);
Marek Vavruša's avatar
Marek Vavruša committed
1352
	return 1;
1353
}
1354 1355 1356 1357 1358 1359 1360 1361 1362 1363

static void event_free(uv_timer_t *timer)
{
	struct worker_ctx *worker = timer->loop->data;
	lua_State *L = worker->engine->L;
	int ref = (intptr_t) timer->data;
	luaL_unref(L, LUA_REGISTRYINDEX, ref);
	free(timer);
}

1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374
static int execute_callback(lua_State *L, int argc)
{
	int ret = engine_pcall(L, argc);
	if (ret != 0) {
		fprintf(stderr, "error: %s\n", lua_tostring(L, -1));
	}
	/* Clear the stack, there may be event a/o enything returned */
	lua_settop(L, 0);
	return ret;
}

1375 1376 1377 1378 1379 1380 1381 1382 1383
static void event_callback(uv_timer_t *timer)
{
	struct worker_ctx *worker = timer->loop->data;
	lua_State *L = worker->engine->L;

	/* Retrieve callback and execute */
	lua_rawgeti(L, LUA_REGISTRYINDEX, (intptr_t) timer->data);
	lua_rawgeti(L, -1, 1);
	lua_pushinteger(L, (intptr_t) timer->data);
1384
	int ret = execute_callback(L, 1);
1385
	/* Free callback if not recurrent or an error */
1386
	if (ret != 0 || (uv_timer_get_repeat(timer) == 0 && uv_is_active((uv_handle_t *)timer) == 0)) {
1387 1388 1389
		if (!uv_is_closing((uv_handle_t *)timer)) {
			uv_close((uv_handle_t *)timer, (uv_close_cb) event_free);
		}
1390 1391 1392
	}
}

1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412
static void event_fdcallback(uv_poll_t* handle, int status, int events)
{
	struct worker_ctx *worker = handle->loop->data;
	lua_State *L = worker->engine->L;

	/* Retrieve callback and execute */
	lua_rawgeti(L, LUA_REGISTRYINDEX, (intptr_t) handle->data);
	lua_rawgeti(L, -1, 1);
	lua_pushinteger(L, (intptr_t) handle->data);
	lua_pushinteger(L, status);
	lua_pushinteger(L, events);
	int ret = execute_callback(L, 3);
	/* Free callback if not recurrent or an error */
	if (ret != 0) {
		if (!uv_is_closing((uv_handle_t *)handle)) {
			uv_close((uv_handle_t *)handle, (uv_close_cb) event_free);
		}
	}
}

1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478
static int event_sched(lua_State *L, unsigned timeout, unsigned repeat)
{
	uv_timer_t *timer = malloc(sizeof(*timer));
	if (!timer) {
		format_error(L, "out of memory");
		lua_error(L);
	}

	/* Start timer with the reference */
	uv_loop_t *loop = uv_default_loop();
	uv_timer_init(loop, timer);
	int ret = uv_timer_start(timer, event_callback, timeout, repeat);
	if (ret != 0) {
		free(timer);
		format_error(L, "couldn't start the event");
		lua_error(L);
	}

	/* Save callback and timer in registry */
	lua_newtable(L);
	lua_pushvalue(L, 2);
	lua_rawseti(L, -2, 1);
	lua_pushlightuserdata(L, timer);
	lua_rawseti(L, -2, 2);
	int ref = luaL_ref(L, LUA_REGISTRYINDEX);

	/* Save reference to the timer */
	timer->data = (void *) (intptr_t)ref;
	lua_pushinteger(L, ref);
	return 1;
}

static int event_after(lua_State *L)
{
	/* Check parameters */
	int n = lua_gettop(L);
	if (n < 2 || !lua_isnumber(L, 1) || !lua_isfunction(L, 2)) {
		format_error(L, "expected 'after(number timeout, function)'");
		lua_error(L);
	}

	return event_sched(L, lua_tonumber(L, 1), 0);
}

static int event_recurrent(lua_State *L)
{
	/* Check parameters */
	int n = lua_gettop(L);
	if (n < 2 || !lua_isnumber(L, 1) || !lua_isfunction(L, 2)) {
		format_error(L, "expected 'recurrent(number interval, function)'");
		lua_error(L);
	}
	return event_sched(L, 0, lua_tonumber(L, 1));
}

static int event_cancel(lua_State *L)
{
	int n = lua_gettop(L);
	if (n < 1 || !lua_isnumber(L, 1)) {
		format_error(L, "expected 'cancel(number event)'");
		lua_error(L);
	}

	/* Fetch event if it exists */
	lua_rawgeti(L, LUA_REGISTRYINDEX, lua_tointeger(L, 1));
	if (!lua_istable(L, -1)) {
1479 1480
		lua_pushboolean(L, false);
		return 1;
1481 1482 1483 1484 1485
	}

	/* Close the timer */
	lua_rawgeti(L, -1, 2);
	uv_handle_t *timer = lua_touserdata(L, -1);
1486 1487 1488
	if (!uv_is_closing(timer)) {
		uv_close(timer, (uv_close_cb) event_free);
	}
1489 1490
	lua_pushboolean(L, true);
	return 1;
1491 1492
}

1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525
static int event_reschedule(lua_State *L)
{
	int n = lua_gettop(L);
	if (n < 2 || !lua_isnumber(L, 1) || !lua_isnumber(L, 2)) {
		format_error(L, "expected 'reschedule(number event, number timeout)'");
		lua_error(L);
	}

	/* Fetch event if it exists */
	lua_rawgeti(L, LUA_REGISTRYINDEX, lua_tointeger(L, 1));
	if (!lua_istable(L, -1)) {
		lua_pushboolean(L, false);
		return 1;
	}

	/* Reschedule the timer */
	lua_rawgeti(L, -1, 2);
	uv_handle_t *timer = lua_touserdata(L, -1);
	if (!uv_is_closing(timer)) {
		if (uv_is_active(timer)) {
			uv_timer_stop((uv_timer_t *)timer);
		}
		int ret = uv_timer_start((uv_timer_t *)timer, event_callback, lua_tointeger(L, 2), 0);
		if (ret != 0) {
			event_cancel(L);
			lua_pushboolean(L, false);
			return 1;
		}
	}
	lua_pushboolean(L, true);
	return 1;
}

1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560