1 /*  Copyright (C) 2014-2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
2  *  SPDX-License-Identifier: GPL-3.0-or-later
3  */
4 
5 /**
6  * @file hints.c
7  * @brief Constructed zone cut from the hosts-like file, see @zonecut.h
8  *
9  * The module provides an override for queried address records.
10  */
11 
12 #include <libknot/packet/pkt.h>
13 #include <libknot/descriptor.h>
14 #include <ccan/json/json.h>
15 #include <ucw/mempool.h>
16 #include <contrib/cleanup.h>
17 #include <lauxlib.h>
18 
19 #include "daemon/engine.h"
20 #include "lib/zonecut.h"
21 #include "lib/module.h"
22 #include "lib/layer.h"
23 
24 #include <inttypes.h>
25 #include <math.h>
26 
27 /* Defaults */
28 #define VERBOSE_MSG(qry, ...) QRVERBOSE(qry, HINT,  __VA_ARGS__)
29 #define ERR_MSG(...) kr_log_error(HINT, "[     ]" __VA_ARGS__)
30 
31 struct hints_data {
32 	struct kr_zonecut hints;
33 	struct kr_zonecut reverse_hints;
34 	bool use_nodata; /**< See hint_use_nodata() description, exposed via lua. */
35 	uint32_t ttl;    /**< TTL used for the hints, exposed via lua. */
36 };
37 static const uint32_t HINTS_TTL_DEFAULT = 5;
38 
39 /** Useful for returning from module properties. */
bool2jsonstr(bool val)40 static char * bool2jsonstr(bool val)
41 {
42 	char *result = NULL;
43 	if (-1 == asprintf(&result, "{ \"result\": %s }", val ? "true" : "false"))
44 		result = NULL;
45 	return result;
46 }
47 
put_answer(knot_pkt_t * pkt,struct kr_query * qry,knot_rrset_t * rr,bool use_nodata)48 static int put_answer(knot_pkt_t *pkt, struct kr_query *qry, knot_rrset_t *rr, bool use_nodata)
49 {
50 	int ret = 0;
51 	if (!knot_rrset_empty(rr) || use_nodata) {
52 		/* Update packet question */
53 		if (!knot_dname_is_equal(knot_pkt_qname(pkt), rr->owner)) {
54 			kr_pkt_recycle(pkt);
55 			knot_pkt_put_question(pkt, qry->sname, qry->sclass, qry->stype);
56 		}
57 		if (!knot_rrset_empty(rr)) {
58 			/* Append to packet */
59 			ret = knot_pkt_put_rotate(pkt, KNOT_COMPR_HINT_QNAME, rr,
60 						  qry->reorder, KNOT_PF_FREE);
61 		} else {
62 			/* Return empty answer if name exists, but type doesn't match */
63 			knot_wire_set_aa(pkt->wire);
64 		}
65 	} else {
66 		ret = kr_error(ENOENT);
67 	}
68 	/* Clear RR if failed */
69 	if (ret != 0) {
70 		knot_rrset_clear(rr, &pkt->mm);
71 	}
72 	return ret;
73 }
74 
satisfy_reverse(struct hints_data * data,knot_pkt_t * pkt,struct kr_query * qry)75 static int satisfy_reverse(/*const*/ struct hints_data *data,
76 			   knot_pkt_t *pkt, struct kr_query *qry)
77 {
78 	/* Find a matching name */
79 	pack_t *addr_set = kr_zonecut_find(&data->reverse_hints, qry->sname);
80 	if (!addr_set || addr_set->len == 0) {
81 		return kr_error(ENOENT);
82 	}
83 	knot_dname_t *qname = knot_dname_copy(qry->sname, &pkt->mm);
84 	knot_rrset_t rr;
85 	knot_rrset_init(&rr, qname, KNOT_RRTYPE_PTR, KNOT_CLASS_IN, data->ttl);
86 
87 	/* Append address records from hints */
88 	uint8_t *addr = pack_last(*addr_set);
89 	if (addr != NULL) {
90 		size_t len = pack_obj_len(addr);
91 		void *addr_val = pack_obj_val(addr);
92 		knot_rrset_add_rdata(&rr, addr_val, len, &pkt->mm);
93 	}
94 
95 	return put_answer(pkt, qry, &rr, data->use_nodata);
96 }
97 
satisfy_forward(struct hints_data * data,knot_pkt_t * pkt,struct kr_query * qry)98 static int satisfy_forward(/*const*/ struct hints_data *data,
99 			   knot_pkt_t *pkt, struct kr_query *qry)
100 {
101 	/* Find a matching name */
102 	pack_t *addr_set = kr_zonecut_find(&data->hints, qry->sname);
103 	if (!addr_set || addr_set->len == 0) {
104 		return kr_error(ENOENT);
105 	}
106 	knot_dname_t *qname = knot_dname_copy(qry->sname, &pkt->mm);
107 	knot_rrset_t rr;
108 	knot_rrset_init(&rr, qname, qry->stype, qry->sclass, data->ttl);
109 
110 	size_t family_len;
111 	switch (rr.type) {
112 	case KNOT_RRTYPE_A:
113 		family_len = sizeof(struct in_addr);
114 		break;
115 	case KNOT_RRTYPE_AAAA:
116 		family_len = sizeof(struct in6_addr);
117 		break;
118 	default:
119 		goto finish;
120 	};
121 
122 	/* Append address records from hints */
123 	uint8_t *addr = pack_head(*addr_set);
124 	while (addr != pack_tail(*addr_set)) {
125 		size_t len = pack_obj_len(addr);
126 		void *addr_val = pack_obj_val(addr);
127 		if (len == family_len) {
128 			knot_rrset_add_rdata(&rr, addr_val, len, &pkt->mm);
129 		}
130 		addr = pack_obj_next(addr);
131 	}
132 finish:
133 	return put_answer(pkt, qry, &rr, data->use_nodata);
134 }
135 
query(kr_layer_t * ctx,knot_pkt_t * pkt)136 static int query(kr_layer_t *ctx, knot_pkt_t *pkt)
137 {
138 	struct kr_query *qry = ctx->req->current_query;
139 	if (!qry || (ctx->state & KR_STATE_FAIL)) {
140 		return ctx->state;
141 	}
142 
143 	struct kr_module *module = ctx->api->data;
144 	struct hints_data *data = module->data;
145 	if (!data) { /* No valid file. */
146 		return ctx->state;
147 	}
148 	/* We can optimize for early return like this: */
149 	if (!data->use_nodata && qry->stype != KNOT_RRTYPE_A
150 	    && qry->stype != KNOT_RRTYPE_AAAA && qry->stype != KNOT_RRTYPE_PTR) {
151 		return ctx->state;
152 	}
153 	/* FIXME: putting directly into packet breaks ordering in case the hint
154 	 * is applied after a CNAME jump. */
155 	if (knot_dname_in_bailiwick(qry->sname, (const uint8_t *)"\4arpa\0") >= 0) {
156 		if (satisfy_reverse(data, pkt, qry) != 0)
157 			return ctx->state;
158 	} else {
159 		if (satisfy_forward(data, pkt, qry) != 0)
160 			return ctx->state;
161 	}
162 
163 	VERBOSE_MSG(qry, "<= answered from hints\n");
164 	qry->flags.DNSSEC_WANT = false; /* Never authenticated */
165 	qry->flags.CACHED = true;
166 	qry->flags.NO_MINIMIZE = true;
167 	pkt->parsed = pkt->size;
168 	knot_wire_set_qr(pkt->wire);
169 	return KR_STATE_DONE;
170 }
171 
parse_addr_str(union inaddr * sa,const char * addr)172 static int parse_addr_str(union inaddr *sa, const char *addr)
173 {
174 	int family = strchr(addr, ':') ? AF_INET6 : AF_INET;
175 	memset(sa, 0, sizeof(*sa));
176 	sa->ip.sa_family = family;
177 	char *addr_bytes = (/*const*/char *)kr_inaddr(&sa->ip);
178 	if (inet_pton(family, addr, addr_bytes) != 1) {
179 		return kr_error(EILSEQ);
180 	}
181 	return 0;
182 }
183 
184 /** @warning _NOT_ thread-safe; returns a pointer to static data! */
raw_addr2reverse(const uint8_t * raw_addr,int family)185 static const knot_dname_t * raw_addr2reverse(const uint8_t *raw_addr, int family)
186 {
187 	#define REV_MAXLEN (4*16 + 16 /* the suffix, terminator, etc. */)
188 	char reverse_addr[REV_MAXLEN];
189 	static knot_dname_t dname[REV_MAXLEN];
190 	#undef REV_MAXLEN
191 
192 	if (family == AF_INET) {
193 		snprintf(reverse_addr, sizeof(reverse_addr),
194 			 "%d.%d.%d.%d.in-addr.arpa.",
195 		         raw_addr[3], raw_addr[2], raw_addr[1], raw_addr[0]);
196 	} else if (family == AF_INET6) {
197 		char *ra_it = reverse_addr;
198 		for (int i = 15; i >= 0; --i) {
199 			ssize_t free_space = reverse_addr + sizeof(reverse_addr) - ra_it;
200 			int written = snprintf(ra_it, free_space, "%x.%x.",
201 						raw_addr[i] & 0x0f, raw_addr[i] >> 4);
202 			if (kr_fails_assert(written < free_space))
203 				return NULL;
204 			ra_it += written;
205 		}
206 		ssize_t free_space = reverse_addr + sizeof(reverse_addr) - ra_it;
207 		if (snprintf(ra_it, free_space, "ip6.arpa.") >= free_space) {
208 			return NULL;
209 		}
210 	} else {
211 		return NULL;
212 	}
213 
214 	if (!knot_dname_from_str(dname, reverse_addr, sizeof(dname))) {
215 		return NULL;
216 	}
217 	return dname;
218 }
219 
addr2reverse(const char * addr)220 static const knot_dname_t * addr2reverse(const char *addr)
221 {
222 	/* Parse address string */
223 	union inaddr ia;
224 	if (parse_addr_str(&ia, addr) != 0) {
225 		return NULL;
226 	}
227 	return raw_addr2reverse((const /*sign*/uint8_t *)kr_inaddr(&ia.ip),
228 				kr_inaddr_family(&ia.ip));
229 }
230 
add_pair(struct kr_zonecut * hints,const char * name,const char * addr)231 static int add_pair(struct kr_zonecut *hints, const char *name, const char *addr)
232 {
233 	/* Build key */
234 	knot_dname_t key[KNOT_DNAME_MAXLEN];
235 	if (!knot_dname_from_str(key, name, sizeof(key))) {
236 		return kr_error(EINVAL);
237 	}
238 	knot_dname_to_lower(key);
239 
240 	union inaddr ia;
241 	if (parse_addr_str(&ia, addr) != 0) {
242 		return kr_error(EINVAL);
243 	}
244 
245 	return kr_zonecut_add(hints, key, kr_inaddr(&ia.ip), kr_inaddr_len(&ia.ip));
246 }
247 
add_reverse_pair(struct kr_zonecut * hints,const char * name,const char * addr)248 static int add_reverse_pair(struct kr_zonecut *hints, const char *name, const char *addr)
249 {
250 	const knot_dname_t *key = addr2reverse(addr);
251 
252 	if (key == NULL) {
253 		return kr_error(EINVAL);
254 	}
255 
256 	knot_dname_t ptr_name[KNOT_DNAME_MAXLEN];
257 	if (!knot_dname_from_str(ptr_name, name, sizeof(ptr_name))) {
258 		return kr_error(EINVAL);
259 	}
260 
261 	return kr_zonecut_add(hints, key, ptr_name, knot_dname_size(ptr_name));
262 }
263 
264 /** For a given name, remove either one address or all of them (if == NULL).
265  *
266  * Also remove the corresponding reverse records.
267  */
del_pair(struct hints_data * data,const char * name,const char * addr)268 static int del_pair(struct hints_data *data, const char *name, const char *addr)
269 {
270 	/* Build key */
271 	knot_dname_t key[KNOT_DNAME_MAXLEN];
272 	if (!knot_dname_from_str(key, name, sizeof(key))) {
273 		return kr_error(EINVAL);
274 	}
275 	int key_len = knot_dname_size(key);
276 
277         if (addr) {
278 		/* Remove the pair. */
279 		union inaddr ia;
280 		if (parse_addr_str(&ia, addr) != 0) {
281 			return kr_error(EINVAL);
282 		}
283 
284 		const knot_dname_t *reverse_key = addr2reverse(addr);
285 		kr_zonecut_del(&data->reverse_hints, reverse_key, key, key_len);
286 		return kr_zonecut_del(&data->hints, key,
287 					kr_inaddr(&ia.ip), kr_inaddr_len(&ia.ip));
288 	}
289 	/* We're removing everything for the name;
290 	 * first find the name's pack */
291 	pack_t *addr_set = kr_zonecut_find(&data->hints, key);
292 	if (!addr_set || addr_set->len == 0) {
293 		return kr_error(ENOENT);
294 	}
295 
296 	/* Remove address records in hints from reverse_hints. */
297 
298 	for (uint8_t *a = pack_head(*addr_set); a != pack_tail(*addr_set);
299 						a = pack_obj_next(a)) {
300 		void *addr_val = pack_obj_val(a);
301 		int family = pack_obj_len(a) == kr_family_len(AF_INET)
302 				? AF_INET : AF_INET6;
303 		const knot_dname_t *reverse_key = raw_addr2reverse(addr_val, family);
304 		if (reverse_key != NULL) {
305 			kr_zonecut_del(&data->reverse_hints, reverse_key, key, key_len);
306 		}
307 	}
308 
309 	/* Remove the whole name. */
310 	return kr_zonecut_del_all(&data->hints, key);
311 }
312 
load_file(struct kr_module * module,const char * path)313 static int load_file(struct kr_module *module, const char *path)
314 {
315 	auto_fclose FILE *fp = fopen(path, "r");
316 	if (fp == NULL) {
317 		ERR_MSG("reading '%s' failed: %s\n", path, strerror(errno));
318 		return kr_error(errno);
319 	} else {
320 		VERBOSE_MSG(NULL, "reading '%s'\n", path);
321 	}
322 
323 	/* Load file to map */
324 	struct hints_data *data = module->data;
325 	size_t line_len = 0;
326 	size_t count = 0;
327 	size_t line_count = 0;
328 	auto_free char *line = NULL;
329 	int ret = kr_ok();
330 
331 	while (getline(&line, &line_len, fp) > 0) {
332 		++line_count;
333 		char *saveptr = NULL;
334 		const char *addr = strtok_r(line, " \t\n", &saveptr);
335 		if (addr == NULL || strchr(addr, '#') || strlen(addr) == 0) {
336 			continue;
337 		}
338 		const char *canonical_name = strtok_r(NULL, " \t\n", &saveptr);
339 		if (canonical_name == NULL) {
340 			ret = -1;
341 			goto error;
342 		}
343 		/* Since the last added PTR records takes preference,
344 		 * we add canonical name as the last one. */
345 		const char *name_tok;
346 		while ((name_tok = strtok_r(NULL, " \t\n", &saveptr)) != NULL) {
347 			ret = add_pair(&data->hints, name_tok, addr);
348 			if (!ret) {
349 				ret = add_reverse_pair(&data->reverse_hints, name_tok, addr);
350 			}
351 			if (ret) {
352 				ret = -1;
353 				goto error;
354 			}
355 			count += 1;
356 		}
357 		ret = add_pair(&data->hints, canonical_name, addr);
358 		if (!ret) {
359 			ret = add_reverse_pair(&data->reverse_hints, canonical_name, addr);
360 		}
361 		if (ret) {
362 			ret = -1;
363 			goto error;
364 		}
365 		count += 1;
366 	}
367 error:
368 	if (ret) {
369 		ret = kr_error(ret);
370 		ERR_MSG("%s:%zu: invalid syntax\n", path, line_count);
371 	}
372 	VERBOSE_MSG(NULL, "loaded %zu hints\n", count);
373 	return ret;
374 }
375 
hint_add_hosts(void * env,struct kr_module * module,const char * args)376 static char* hint_add_hosts(void *env, struct kr_module *module, const char *args)
377 {
378 	if (!args)
379 		args = "/etc/hosts";
380 	int err = load_file(module, args);
381 	return bool2jsonstr(err == kr_ok());
382 }
383 
384 /**
385  * Set name => address hint.
386  *
387  * Input:  { name, address }
388  * Output: { result: bool }
389  *
390  */
hint_set(void * env,struct kr_module * module,const char * args)391 static char* hint_set(void *env, struct kr_module *module, const char *args)
392 {
393 	struct hints_data *data = module->data;
394 	if (!args)
395 		return NULL;
396 	auto_free char *args_copy = strdup(args);
397 	if (!args_copy)
398 		return NULL;
399 
400 	int ret = -1;
401 	char *addr = strchr(args_copy, ' ');
402 	if (addr) {
403 		*addr = '\0';
404 		++addr;
405 		ret = add_reverse_pair(&data->reverse_hints, args_copy, addr);
406 		if (ret) {
407 			del_pair(data, args_copy, addr);
408 		} else {
409 			ret = add_pair(&data->hints, args_copy, addr);
410 		}
411 	}
412 
413 	return bool2jsonstr(ret == 0);
414 }
415 
hint_del(void * env,struct kr_module * module,const char * args)416 static char* hint_del(void *env, struct kr_module *module, const char *args)
417 {
418 	struct hints_data *data = module->data;
419 	if (!args)
420 		return NULL;
421 	auto_free char *args_copy = strdup(args);
422 	if (!args_copy)
423 		return NULL;
424 
425 	int ret = -1;
426 	char *addr = strchr(args_copy, ' ');
427 	if (addr) {
428 		*addr = '\0';
429 		++addr;
430 	}
431 	ret = del_pair(data, args_copy, addr);
432 
433 	return bool2jsonstr(ret == 0);
434 }
435 
436 /** @internal Pack address list into JSON array. */
pack_addrs(pack_t * pack)437 static JsonNode *pack_addrs(pack_t *pack)
438 {
439 	char buf[INET6_ADDRSTRLEN];
440 	JsonNode *root = json_mkarray();
441 	uint8_t *addr = pack_head(*pack);
442 	while (addr != pack_tail(*pack)) {
443 		size_t len = pack_obj_len(addr);
444 		int family = len == sizeof(struct in_addr) ? AF_INET : AF_INET6;
445 		if (!inet_ntop(family, pack_obj_val(addr), buf, sizeof(buf))) {
446 			break;
447 		}
448 		json_append_element(root, json_mkstring(buf));
449 		addr = pack_obj_next(addr);
450 	}
451 	return root;
452 }
453 
454 static char* pack_hints(struct kr_zonecut *hints);
455 /**
456  * Retrieve address hints, either for given name or for all names.
457  *
458  * Input:  name
459  * Output: NULL or "{ address1, address2, ... }"
460  */
hint_get(void * env,struct kr_module * module,const char * args)461 static char* hint_get(void *env, struct kr_module *module, const char *args)
462 {
463 	struct kr_zonecut *hints = &((struct hints_data *) module->data)->hints;
464 	if (kr_fails_assert(hints))
465 		return NULL;
466 
467 	if (!args) {
468 		return pack_hints(hints);
469 	}
470 
471 	knot_dname_t key[KNOT_DNAME_MAXLEN];
472 	pack_t *pack = NULL;
473 	if (knot_dname_from_str(key, args, sizeof(key))) {
474 		pack = kr_zonecut_find(hints, key);
475 	}
476 	if (!pack || pack->len == 0) {
477 		return NULL;
478 	}
479 
480 	char *result = NULL;
481 	JsonNode *root = pack_addrs(pack);
482 	if (root) {
483 		result = json_encode(root);
484 		json_delete(root);
485 	}
486 	return result;
487 }
488 
489 /** @internal Pack all hints into serialized JSON. */
pack_hints(struct kr_zonecut * hints)490 static char* pack_hints(struct kr_zonecut *hints) {
491 	char *result = NULL;
492 	JsonNode *root_node = json_mkobject();
493 	trie_it_t *it;
494 	for (it = trie_it_begin(hints->nsset); !trie_it_finished(it); trie_it_next(it)) {
495 		KR_DNAME_GET_STR(nsname_str, (const knot_dname_t *)trie_it_key(it, NULL));
496 		JsonNode *addr_list = pack_addrs((pack_t *)*trie_it_val(it));
497 		if (!addr_list) goto error;
498 		json_append_member(root_node, nsname_str, addr_list);
499 	}
500 	result = json_encode(root_node);
501 error:
502 	trie_it_free(it);
503 	json_delete(root_node);
504 	return result;
505 }
506 
unpack_hint(struct kr_zonecut * root_hints,JsonNode * table,const char * name)507 static void unpack_hint(struct kr_zonecut *root_hints, JsonNode *table, const char *name)
508 {
509 	JsonNode *node = NULL;
510 	json_foreach(node, table) {
511 		switch(node->tag) {
512 		case JSON_STRING: add_pair(root_hints, name ? name : node->key, node->string_); break;
513 		case JSON_ARRAY: unpack_hint(root_hints, node, name ? name : node->key); break;
514 		default: continue;
515 		}
516 	}
517 }
518 
519 /**
520  * Get/set root hints set.
521  *
522  * Input:  { name: [addr_list], ... }
523  * Output: current list
524  *
525  */
hint_root(void * env,struct kr_module * module,const char * args)526 static char* hint_root(void *env, struct kr_module *module, const char *args)
527 {
528 	struct engine *engine = env;
529 	struct kr_context *ctx = &engine->resolver;
530 	struct kr_zonecut *root_hints = &ctx->root_hints;
531 	/* Replace root hints if parameter is set */
532 	if (args && args[0] != '\0') {
533 		JsonNode *root_node = json_decode(args);
534 		kr_zonecut_set(root_hints, (const uint8_t *)"");
535 		unpack_hint(root_hints, root_node, NULL);
536 		json_delete(root_node);
537 	}
538 	/* Return current root hints */
539 	return pack_hints(root_hints);
540 }
541 
hint_root_file(void * env,struct kr_module * module,const char * args)542 static char* hint_root_file(void *env, struct kr_module *module, const char *args)
543 {
544 	struct engine *engine = env;
545 	struct kr_context *ctx = &engine->resolver;
546 	const char *err_msg = engine_hint_root_file(ctx, args);
547 	if (err_msg) {
548 		luaL_error(engine->L, "error when opening '%s': %s", args, err_msg);
549 	}
550 	return strdup(err_msg ? err_msg : "");
551 }
552 
hint_use_nodata(void * env,struct kr_module * module,const char * args)553 static char* hint_use_nodata(void *env, struct kr_module *module, const char *args)
554 {
555 	struct hints_data *data = module->data;
556 	if (!args) {
557 		return NULL;
558 	}
559 
560 	JsonNode *root_node = json_decode(args);
561 	if (!root_node || root_node->tag != JSON_BOOL) {
562 		json_delete(root_node);
563 		return bool2jsonstr(false);
564 	}
565 
566 	data->use_nodata = root_node->bool_;
567 	json_delete(root_node);
568 	return bool2jsonstr(true);
569 }
570 
hint_ttl(void * env,struct kr_module * module,const char * args)571 static char* hint_ttl(void *env, struct kr_module *module, const char *args)
572 {
573 	struct hints_data *data = module->data;
574 
575 	/* Do no change on nonsense TTL values (incl. suspicious floats). */
576 	JsonNode *root_node = args ? json_decode(args) : NULL;
577 	if (root_node && root_node->tag == JSON_NUMBER) {
578 		double ttl_d = root_node->number_;
579 		uint32_t ttl = (uint32_t)round(ttl_d);
580 		if (ttl_d >= 0 && fabs(ttl_d - ttl) * 64 < 1) {
581 			data->ttl = ttl;
582 		}
583 	}
584 	json_delete(root_node);
585 
586 	/* Always return the current TTL setting.  Plain number is valid JSON. */
587 	char *result = NULL;
588 	if (-1 == asprintf(&result, "%"PRIu32, data->ttl)) {
589 		result = NULL;
590 	}
591 	return result;
592 }
593 
594 /** Basic initialization: get a memory pool, etc. */
595 KR_EXPORT
hints_init(struct kr_module * module)596 int hints_init(struct kr_module *module)
597 {
598 	static kr_layer_api_t layer = {
599 		.produce = &query,
600 	};
601 	/* Store module reference */
602 	layer.data = module;
603 	module->layer = &layer;
604 
605 	static const struct kr_prop props[] = {
606 	    { &hint_set,    "set", "Set {name, address} hint.", },
607 	    { &hint_del,    "del", "Delete one {name, address} hint or all addresses for the name.", },
608 	    { &hint_get,    "get", "Retrieve hint for given name.", },
609 	    { &hint_ttl,    "ttl", "Set/get TTL used for the hints.", },
610 	    { &hint_add_hosts, "add_hosts", "Load a file with hosts-like formatting and add contents into hints.", },
611 	    { &hint_root,   "root", "Replace root hints set (empty value to return current list).", },
612 	    { &hint_root_file, "root_file", "Replace root hints set from a zonefile.", },
613 	    { &hint_use_nodata, "use_nodata", "Synthesise NODATA if name matches, but type doesn't.  True by default.", },
614 	    { NULL, NULL, NULL }
615 	};
616 	module->props = props;
617 
618 	knot_mm_t *pool = mm_ctx_mempool2(MM_DEFAULT_BLKSIZE);
619 	if (!pool) {
620 		return kr_error(ENOMEM);
621 	}
622 	struct hints_data *data = mm_alloc(pool, sizeof(struct hints_data));
623 	if (!data) {
624 		mp_delete(pool->ctx);
625 		return kr_error(ENOMEM);
626 	}
627 	kr_zonecut_init(&data->hints, (const uint8_t *)(""), pool);
628 	kr_zonecut_init(&data->reverse_hints, (const uint8_t *)(""), pool);
629 	data->use_nodata = true;
630 	data->ttl = HINTS_TTL_DEFAULT;
631 	module->data = data;
632 
633 	return kr_ok();
634 }
635 
636 /** Release all resources. */
637 KR_EXPORT
hints_deinit(struct kr_module * module)638 int hints_deinit(struct kr_module *module)
639 {
640 	struct hints_data *data = module->data;
641 	if (data) {
642 		kr_zonecut_deinit(&data->hints);
643 		kr_zonecut_deinit(&data->reverse_hints);
644 		mp_delete(data->hints.pool->ctx);
645 		module->data = NULL;
646 	}
647 	return kr_ok();
648 }
649 
650 /** Drop all hints, and load a hosts file if any was specified.
651  *
652  * It seems slightly strange to drop all, but keep doing that for now.
653  */
654 KR_EXPORT
hints_config(struct kr_module * module,const char * conf)655 int hints_config(struct kr_module *module, const char *conf)
656 {
657 	hints_deinit(module);
658 	int err = hints_init(module);
659 	if (err != kr_ok()) {
660 		return err;
661 	}
662 
663 	if (conf && conf[0]) {
664 		return load_file(module, conf);
665 	}
666 	return kr_ok();
667 }
668 
669 KR_MODULE_EXPORT(hints)
670 
671 #undef VERBOSE_MSG
672