1 /*  Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
2  *  SPDX-License-Identifier: GPL-3.0-or-later
3  */
4 
5 /* Module is intended to import resource records from file into resolver's cache.
6  * File supposed to be a standard DNS zone file
7  * which contains text representations of resource records.
8  * For now only root zone import is supported.
9  *
10  * Import process consists of two stages.
11  * 1) Zone file parsing.
12  * 2) Import of parsed entries into the cache.
13  *
14  * These stages are implemented as two separate functions
15  * (zi_zone_import and zi_zone_process) which runs sequentially with the
16  * pause between them. This is done because resolver is a single-threaded
17  * application, so it can't process user's requests during the whole import
18  * process. Separation into two stages allows to reduce the
19  * continuous time interval when resolver can't serve user requests.
20  * Since root zone isn't large it is imported as single
21  * chunk. If it would be considered as necessary, import stage can be
22  * split into shorter stages.
23  *
24  * zi_zone_import() uses libzscanner to parse zone file.
25  * Parsed records are stored to internal storage from where they are imported to
26  * cache during the second stage.
27  *
28  * zi_zone_process() imports parsed resource records to cache.
29  * It imports rrset by creating request that will never be sent to upstream.
30  * After request creation resolver creates pseudo-answer which must contain
31  * all necessary data for validation. Then resolver process answer as if he had
32  * been received from network.
33  */
34 
35 #include <inttypes.h> /* PRIu64 */
36 #include <limits.h>
37 #include <stdlib.h>
38 #include <uv.h>
39 #include <ucw/mempool.h>
40 #include <libknot/rrset.h>
41 #include <libzscanner/scanner.h>
42 
43 #include "lib/utils.h"
44 #include "lib/dnssec/ta.h"
45 #include "daemon/worker.h"
46 #include "daemon/zimport.h"
47 #include "lib/generic/map.h"
48 #include "lib/generic/array.h"
49 
50 #define VERBOSE_MSG(qry, ...) QRVERBOSE(qry, ZIMPORT, __VA_ARGS__)
51 
52 /* Pause between parse and import stages, milliseconds.
53  * See comment in zi_zone_import() */
54 #define ZONE_IMPORT_PAUSE 100
55 
56 typedef array_t(knot_rrset_t *) qr_rrsetlist_t;
57 
58 struct zone_import_ctx {
59 	struct worker_ctx *worker;
60 	bool started;
61 	knot_dname_t *origin;
62 	knot_rrset_t *ta;
63 	knot_rrset_t *key;
64 	uint64_t start_timestamp;
65 	size_t rrset_idx;
66 	uv_timer_t timer;
67 	map_t rrset_indexed;
68 	qr_rrsetlist_t rrset_sorted;
69 	knot_mm_t pool;
70 	zi_callback cb;
71 	void *cb_param;
72 };
73 
74 typedef struct zone_import_ctx zone_import_ctx_t;
75 
76 static int RRSET_IS_ALREADY_IMPORTED = 1;
77 
78 /** @internal Allocate zone import context.
79  * @return pointer to zone import context or NULL. */
zi_ctx_alloc()80 static zone_import_ctx_t *zi_ctx_alloc()
81 {
82 	return calloc(1, sizeof(zone_import_ctx_t));
83 }
84 
85 /** @internal Free zone import context. */
zi_ctx_free(zone_import_ctx_t * z_import)86 static void zi_ctx_free(zone_import_ctx_t *z_import)
87 {
88 	if (z_import != NULL) {
89 		free(z_import);
90 	}
91 }
92 
93 /** @internal Reset all fields in the zone import context to their default values.
94  * Flushes memory pool, but doesn't reallocate memory pool buffer.
95  * Doesn't affect timer handle, pointers to callback and callback parameter.
96  * @return 0 if success; -1 if failed. */
zi_reset(struct zone_import_ctx * z_import,size_t rrset_sorted_list_size)97 static int zi_reset(struct zone_import_ctx *z_import, size_t rrset_sorted_list_size)
98 {
99 	mp_flush(z_import->pool.ctx);
100 
101 	z_import->started = false;
102 	z_import->start_timestamp = 0;
103 	z_import->rrset_idx = 0;
104 	z_import->pool.alloc = (knot_mm_alloc_t) mp_alloc;
105 	z_import->rrset_indexed = map_make(&z_import->pool);
106 
107 	array_init(z_import->rrset_sorted);
108 
109 	int ret = 0;
110 	if (rrset_sorted_list_size) {
111 		ret = array_reserve_mm(z_import->rrset_sorted, rrset_sorted_list_size,
112 				       kr_memreserve, &z_import->pool);
113 	}
114 
115 	return ret;
116 }
117 
118 /** @internal Close callback for timer handle.
119  * @note Actually frees zone import context. */
on_timer_close(uv_handle_t * handle)120 static void on_timer_close(uv_handle_t *handle)
121 {
122 	zone_import_ctx_t *z_import = (zone_import_ctx_t *)handle->data;
123 	if (z_import != NULL) {
124 		zi_ctx_free(z_import);
125 	}
126 }
127 
zi_allocate(struct worker_ctx * worker,zi_callback cb,void * param)128 zone_import_ctx_t *zi_allocate(struct worker_ctx *worker,
129 			       zi_callback cb, void *param)
130 {
131 	if (worker->loop == NULL) {
132 		return NULL;
133 	}
134 	zone_import_ctx_t *z_import = zi_ctx_alloc();
135 	if (!z_import) {
136 		return NULL;
137 	}
138 	void *mp = mp_new (8192);
139 	if (!mp) {
140 		zi_ctx_free(z_import);
141 		return NULL;
142 	}
143 	z_import->pool.ctx = mp;
144 	z_import->worker = worker;
145 	int ret = zi_reset(z_import, 0);
146 	if (ret < 0) {
147 		mp_delete(mp);
148 		zi_ctx_free(z_import);
149 		return NULL;
150 	}
151 	uv_timer_init(z_import->worker->loop, &z_import->timer);
152 	z_import->timer.data = z_import;
153 	z_import->cb = cb;
154 	z_import->cb_param = param;
155 	return z_import;
156 }
157 
zi_free(zone_import_ctx_t * z_import)158 void zi_free(zone_import_ctx_t *z_import)
159 {
160 	z_import->started = false;
161 	z_import->start_timestamp = 0;
162 	z_import->rrset_idx = 0;
163 	mp_delete(z_import->pool.ctx);
164 	z_import->pool.ctx = NULL;
165 	z_import->pool.alloc = NULL;
166 	z_import->worker = NULL;
167 	z_import->cb = NULL;
168 	z_import->cb_param = NULL;
169 	uv_close((uv_handle_t *)&z_import->timer, on_timer_close);
170 }
171 
172 /** @internal Mark rrset that has been already imported
173  *  to avoid repeated import. */
zi_rrset_mark_as_imported(knot_rrset_t * rr)174 static inline void zi_rrset_mark_as_imported(knot_rrset_t *rr)
175 {
176 	rr->additional = (void *)&RRSET_IS_ALREADY_IMPORTED;
177 }
178 
179 /** @internal Check if rrset is marked as "already imported".
180  * @return true if marked, false if isn't */
zi_rrset_is_marked_as_imported(knot_rrset_t * rr)181 static inline bool zi_rrset_is_marked_as_imported(knot_rrset_t *rr)
182 {
183 	return (rr->additional == &RRSET_IS_ALREADY_IMPORTED);
184 }
185 
186 /** @internal Try to find rrset with given requisites amongst parsed rrsets
187  * and put it to given packet. If there is RRSIG which covers that rrset, it
188  * will be added as well. If rrset found and successfully put, it marked as
189  * "already imported" to avoid repeated import. The same is true for RRSIG.
190  * @return -1 if failed
191  *          0 if required record been actually put into the packet
192  *          1 if required record could not be found */
zi_rrset_find_put(struct zone_import_ctx * z_import,knot_pkt_t * pkt,const knot_dname_t * owner,uint16_t class,uint16_t type,uint16_t additional)193 static int zi_rrset_find_put(struct zone_import_ctx *z_import,
194 			     knot_pkt_t *pkt, const knot_dname_t *owner,
195 			     uint16_t class, uint16_t type, uint16_t additional)
196 {
197 	if (type != KNOT_RRTYPE_RRSIG) {
198 		/* If required rrset isn't rrsig, these must be the same values */
199 		additional = type;
200 	}
201 
202 	char key[KR_RRKEY_LEN];
203 	int err = kr_rrkey(key, class, owner, type, additional);
204 	if (err <= 0) {
205 		return -1;
206 	}
207 	knot_rrset_t *rr = map_get(&z_import->rrset_indexed, key);
208 	if (!rr) {
209 		return 1;
210 	}
211 	err = knot_pkt_put(pkt, 0, rr, 0);
212 	if (err != KNOT_EOK) {
213 		return -1;
214 	}
215 	zi_rrset_mark_as_imported(rr);
216 
217 	if (type != KNOT_RRTYPE_RRSIG) {
218 		/* Try to find corresponding rrsig */
219 		err = zi_rrset_find_put(z_import, pkt, owner,
220 					class, KNOT_RRTYPE_RRSIG, type);
221 		if (err < 0) {
222 			return err;
223 		}
224 	}
225 
226 	return 0;
227 }
228 
229 /** @internal Try to put given rrset to the given packet.
230  * If there is RRSIG which covers that rrset, it will be added as well.
231  * If rrset successfully put in the packet, it marked as
232  * "already imported" to avoid repeated import.
233  * The same is true for RRSIG.
234  * @return -1 if failed
235  *          0 if required record been actually put into the packet */
zi_rrset_put(struct zone_import_ctx * z_import,knot_pkt_t * pkt,knot_rrset_t * rr)236 static int zi_rrset_put(struct zone_import_ctx *z_import, knot_pkt_t *pkt,
237 			knot_rrset_t *rr)
238 {
239 	if (kr_fails_assert(rr && rr->type != KNOT_RRTYPE_RRSIG))
240 		return -1;
241 	int err = knot_pkt_put(pkt, 0, rr, 0);
242 	if (err != KNOT_EOK) {
243 		return -1;
244 	}
245 	zi_rrset_mark_as_imported(rr);
246 	/* Try to find corresponding RRSIG */
247 	err = zi_rrset_find_put(z_import, pkt, rr->owner, rr->rclass,
248 				KNOT_RRTYPE_RRSIG, rr->type);
249 	return (err < 0) ? err : 0;
250 }
251 
252 /** @internal Try to put DS & NSEC* for rset->owner to given packet.
253  * @return -1 if failed;
254  *          0 if no errors occurred (it doesn't mean
255  *            that records were actually added). */
zi_put_delegation(zone_import_ctx_t * z_import,knot_pkt_t * pkt,knot_rrset_t * rr)256 static int zi_put_delegation(zone_import_ctx_t *z_import, knot_pkt_t *pkt,
257 			     knot_rrset_t *rr)
258 {
259 	int err = zi_rrset_find_put(z_import, pkt, rr->owner,
260 				    rr->rclass, KNOT_RRTYPE_DS, 0);
261 	if (err == 1) {
262 		/* DS not found, maybe there are NSEC* */
263 		err = zi_rrset_find_put(z_import, pkt, rr->owner,
264 					rr->rclass, KNOT_RRTYPE_NSEC, 0);
265 		if (err >= 0) {
266 			err = zi_rrset_find_put(z_import, pkt, rr->owner,
267 						rr->rclass, KNOT_RRTYPE_NSEC3, 0);
268 		}
269 	}
270 	return err < 0 ? err : 0;
271 }
272 
273 /** @internal Try to put A & AAAA records for rset->owner to given packet.
274  * @return -1 if failed;
275  *          0 if no errors occurred (it doesn't mean
276  *            that records were actually added). */
zi_put_glue(zone_import_ctx_t * z_import,knot_pkt_t * pkt,knot_rrset_t * rr)277 static int zi_put_glue(zone_import_ctx_t *z_import, knot_pkt_t *pkt,
278 			     knot_rrset_t *rr)
279 {
280 	int err = 0;
281 	knot_rdata_t *rdata_i = rr->rrs.rdata;
282 	for (uint16_t i = 0; i < rr->rrs.count;
283 			++i, rdata_i = knot_rdataset_next(rdata_i)) {
284 		const knot_dname_t *ns_name = knot_ns_name(rdata_i);
285 		err = zi_rrset_find_put(z_import, pkt, ns_name,
286 					rr->rclass, KNOT_RRTYPE_A, 0);
287 		if (err < 0) {
288 			break;
289 		}
290 
291 		err = zi_rrset_find_put(z_import, pkt, ns_name,
292 					rr->rclass, KNOT_RRTYPE_AAAA, 0);
293 		if (err < 0) {
294 			break;
295 		}
296 	}
297 	return err < 0 ? err : 0;
298 }
299 
300 /** @internal Create query. */
zi_query_create(zone_import_ctx_t * z_import,knot_rrset_t * rr)301 static knot_pkt_t *zi_query_create(zone_import_ctx_t *z_import, knot_rrset_t *rr)
302 {
303 	knot_mm_t *pool = &z_import->pool;
304 
305 	uint32_t msgid = kr_rand_bytes(2);
306 
307 	knot_pkt_t *query = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, pool);
308 	if (!query) {
309 		return NULL;
310 	}
311 
312 	knot_pkt_put_question(query, rr->owner, rr->rclass, rr->type);
313 	knot_pkt_begin(query, KNOT_ANSWER);
314 	knot_wire_set_rd(query->wire);
315 	knot_wire_set_id(query->wire, msgid);
316 	int err = knot_pkt_parse(query, 0);
317 	if (err != KNOT_EOK) {
318 		knot_pkt_free(query);
319 		return NULL;
320 	}
321 
322 	return query;
323 }
324 
325 /** @internal Import given rrset to cache.
326  * @return -1 if failed; 0 if success */
zi_rrset_import(zone_import_ctx_t * z_import,knot_rrset_t * rr)327 static int zi_rrset_import(zone_import_ctx_t *z_import, knot_rrset_t *rr)
328 {
329 	/* Create "pseudo query" which asks for given rrset. */
330 	knot_pkt_t *query = zi_query_create(z_import, rr);
331 	if (!query) {
332 		return -1;
333 	}
334 
335 	knot_mm_t *pool = &z_import->pool;
336 	uint8_t *dname = rr->owner;
337 	uint16_t rrtype = rr->type;
338 	uint16_t rrclass = rr->rclass;
339 
340 	/* Create "pseudo answer". */
341 	knot_pkt_t *answer = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, pool);
342 	if (!answer) {
343 		knot_pkt_free(query);
344 		return -1;
345 	}
346 	knot_pkt_put_question(answer, dname, rrclass, rrtype);
347 	knot_pkt_begin(answer, KNOT_ANSWER);
348 
349 	struct kr_qflags options = { 0 };
350 	options.DNSSEC_WANT = true;
351 	options.NO_MINIMIZE = true;
352 
353 	/* This call creates internal structures which necessary for
354 	 * resolving - qr_task & request_ctx. */
355 	struct qr_task *task = worker_resolve_start(query, options);
356 	if (!task) {
357 		knot_pkt_free(query);
358 		knot_pkt_free(answer);
359 		return -1;
360 	}
361 
362 	/* Push query to the request resolve plan.
363 	 * Actually query will never been sent to upstream. */
364 	struct kr_request *request = worker_task_request(task);
365 	struct kr_rplan *rplan = &request->rplan;
366 	struct kr_query *qry = kr_rplan_push(rplan, NULL, dname, rrclass, rrtype);
367 	int state = KR_STATE_FAIL;
368 	bool origin_is_owner = knot_dname_is_equal(rr->owner, z_import->origin);
369 	bool is_referral = (rrtype == KNOT_RRTYPE_NS && !origin_is_owner);
370 	uint32_t msgid = knot_wire_get_id(query->wire);
371 
372 	qry->id = msgid;
373 
374 	/* Prepare zonecut. It must have all the necessary requisites for
375 	 * successful validation - matched zone name & keys & trust-anchors. */
376 	kr_zonecut_init(&qry->zone_cut, z_import->origin, pool);
377 	qry->zone_cut.key = z_import->key;
378 	qry->zone_cut.trust_anchor = z_import->ta;
379 
380 	if (knot_pkt_init_response(answer, query) != 0) {
381 		goto cleanup;
382 	}
383 
384 	/* Since "pseudo" query asks for NS for subzone,
385 	 * "pseudo" answer must simulate referral. */
386 	if (is_referral) {
387 		knot_pkt_begin(answer, KNOT_AUTHORITY);
388 	}
389 
390 	/* Put target rrset to ANSWER\AUTHORITY as well as corresponding RRSIG */
391 	int err = zi_rrset_put(z_import, answer, rr);
392 	if (err != 0) {
393 		goto cleanup;
394 	}
395 
396 	if (!is_referral) {
397 		knot_wire_set_aa(answer->wire);
398 	} else {
399 		/* Type is KNOT_RRTYPE_NS and owner is not equal to origin.
400 		 * It will be "referral" answer and must contain delegation. */
401 		err = zi_put_delegation(z_import, answer, rr);
402 		if (err < 0) {
403 			goto cleanup;
404 		}
405 	}
406 
407 	knot_pkt_begin(answer, KNOT_ADDITIONAL);
408 
409 	if (rrtype == KNOT_RRTYPE_NS) {
410 		/* Try to find glue addresses. */
411 		err = zi_put_glue(z_import, answer, rr);
412 		if (err < 0) {
413 			goto cleanup;
414 		}
415 	}
416 
417 	knot_wire_set_id(answer->wire, msgid);
418 	answer->parsed = answer->size;
419 	err = knot_pkt_parse(answer, 0);
420 	if (err != KNOT_EOK) {
421 		goto cleanup;
422 	}
423 
424 	/* Importing doesn't imply communication with upstream at all.
425 	 * "answer" contains pseudo-answer from upstream and must be successfully
426 	 * validated in CONSUME stage. If not, something gone wrong. */
427 	state = kr_resolve_consume(request, NULL, answer);
428 
429 cleanup:
430 
431 	knot_pkt_free(query);
432 	knot_pkt_free(answer);
433 	worker_task_finalize(task, state);
434 	return state == (is_referral ? KR_STATE_PRODUCE : KR_STATE_DONE) ? 0 : -1;
435 }
436 
437 /** @internal Create element in qr_rrsetlist_t rrset_list for
438  * given node of map_t rrset_sorted.  */
zi_mapwalk_preprocess(const char * k,void * v,void * baton)439 static int zi_mapwalk_preprocess(const char *k, void *v, void *baton)
440 {
441 	zone_import_ctx_t *z_import = (zone_import_ctx_t *)baton;
442 
443 	int ret = array_push_mm(z_import->rrset_sorted, v, kr_memreserve, &z_import->pool);
444 
445 	return (ret < 0);
446 }
447 
448 /** @internal Iterate over parsed rrsets and try to import each of them. */
zi_zone_process(uv_timer_t * handle)449 static void zi_zone_process(uv_timer_t* handle)
450 {
451 	zone_import_ctx_t *z_import = (zone_import_ctx_t *)handle->data;
452 
453 	size_t failed = 0;
454 	size_t ns_imported = 0;
455 	size_t other_imported = 0;
456 
457 	if (kr_fails_assert(z_import->worker)) {
458 		failed = 1;
459 		goto finish;
460 	}
461 
462 	/* At the moment import of root zone only is supported.
463 	 * Check the name of the parsed zone.
464 	 * TODO - implement importing of arbitrary zone. */
465 	KR_DNAME_GET_STR(zone_name_str, z_import->origin);
466 
467 	if (strcmp(".", zone_name_str) != 0) {
468 		kr_log_error(ZIMPORT, "unexpected zone name `%s` (root zone expected), fail\n",
469 			     zone_name_str);
470 		failed = 1;
471 		goto finish;
472 	}
473 
474 	if (z_import->rrset_sorted.len <= 0) {
475 		kr_log_error(ZIMPORT, "zone `%s` is empty\n", zone_name_str);
476 		goto finish;
477 	}
478 
479 	/* TA have been found, zone is secured.
480 	 * DNSKEY must be somewhere amongst the imported records. Find it.
481 	 * TODO - For those zones that provenly do not have TA this step must be skipped. */
482 	char key[KR_RRKEY_LEN];
483 	int err = kr_rrkey(key, KNOT_CLASS_IN, z_import->origin,
484 			   KNOT_RRTYPE_DNSKEY, KNOT_RRTYPE_DNSKEY);
485 	if (err <= 0) {
486 		failed = 1;
487 		goto finish;
488 	}
489 
490 	knot_rrset_t *rr_key = map_get(&z_import->rrset_indexed, key);
491 	if (!rr_key) {
492 		/* DNSKEY MUST be here. If not found - fail. */
493 		kr_log_error(ZIMPORT, "DNSKEY not found for `%s`, fail\n", zone_name_str);
494 		failed = 1;
495 		goto finish;
496 	}
497 	z_import->key = rr_key;
498 
499 	map_t *trust_anchors = &z_import->worker->engine->resolver.trust_anchors;
500 	knot_rrset_t *rr_ta = kr_ta_get(trust_anchors, z_import->origin);
501 	if (!rr_ta) {
502 		kr_log_error(ZIMPORT, "error: TA for zone `%s` vanished, fail", zone_name_str);
503 		failed = 1;
504 		goto finish;
505 	}
506 	z_import->ta = rr_ta;
507 
508 	VERBOSE_MSG(NULL, "started: zone: '%s'\n", zone_name_str);
509 
510 	z_import->start_timestamp = kr_now();
511 
512 	/* Import DNSKEY at first step. If any validation problems will appear,
513 	 * cancel import of whole zone. */
514 	KR_DNAME_GET_STR(kname_str, rr_key->owner);
515 	KR_RRTYPE_GET_STR(ktype_str, rr_key->type);
516 
517 	VERBOSE_MSG(NULL, "importing: name: '%s' type: '%s'\n",
518 		    kname_str, ktype_str);
519 
520 	int res = zi_rrset_import(z_import, rr_key);
521 	if (res != 0) {
522 		kr_log_error(ZIMPORT, "import failed: qname: '%s' type: '%s'\n",
523 			    kname_str, ktype_str);
524 		failed = 1;
525 		goto finish;
526 	}
527 
528 	/* Import all NS records */
529 	for (size_t i = 0; i < z_import->rrset_sorted.len; ++i) {
530 		knot_rrset_t *rr = z_import->rrset_sorted.at[i];
531 
532 		if (rr->type != KNOT_RRTYPE_NS) {
533 			continue;
534 		}
535 
536 		KR_DNAME_GET_STR(name_str, rr->owner);
537 		KR_RRTYPE_GET_STR(type_str, rr->type);
538 		VERBOSE_MSG(NULL, "importing: name: '%s' type: '%s'\n",
539 			    name_str, type_str);
540 		int ret = zi_rrset_import(z_import, rr);
541 		if (ret == 0) {
542 			++ns_imported;
543 		} else {
544 			VERBOSE_MSG(NULL, "import failed: name: '%s' type: '%s'\n",
545 				    name_str, type_str);
546 			++failed;
547 		}
548 		z_import->rrset_sorted.at[i] = NULL;
549 	}
550 
551 	/* NS records have been imported as well as relative DS, NSEC* and glue.
552 	 * Now import what's left. */
553 	for (size_t i = 0; i < z_import->rrset_sorted.len; ++i) {
554 
555 		knot_rrset_t *rr = z_import->rrset_sorted.at[i];
556 		if (rr == NULL) {
557 			continue;
558 		}
559 
560 		if (zi_rrset_is_marked_as_imported(rr)) {
561 			continue;
562 		}
563 
564 		if (rr->type == KNOT_RRTYPE_DNSKEY || rr->type == KNOT_RRTYPE_RRSIG) {
565 			continue;
566 		}
567 
568 		KR_DNAME_GET_STR(name_str, rr->owner);
569 		KR_RRTYPE_GET_STR(type_str, rr->type);
570 		VERBOSE_MSG(NULL, "importing: name: '%s' type: '%s'\n",
571 			    name_str, type_str);
572 		res = zi_rrset_import(z_import, rr);
573 		if (res == 0) {
574 			++other_imported;
575 		} else {
576 			VERBOSE_MSG(NULL, "import failed: name: '%s' type: '%s'\n",
577 				    name_str, type_str);
578 			++failed;
579 		}
580 	}
581 
582 	uint64_t elapsed = kr_now() - z_import->start_timestamp;
583 	elapsed = elapsed > UINT_MAX ? UINT_MAX : elapsed;
584 
585 	VERBOSE_MSG(NULL, "finished in %"PRIu64" ms; zone: `%s`; ns: %zd"
586 		    "; other: %zd; failed: %zd\n",
587 		    elapsed, zone_name_str, ns_imported, other_imported, failed);
588 
589 finish:
590 
591 	uv_timer_stop(&z_import->timer);
592 	z_import->started = false;
593 
594 	int import_state = 0;
595 
596 	if (failed != 0) {
597 		if (ns_imported == 0 && other_imported == 0) {
598 			import_state = -1;
599 			kr_log_error(ZIMPORT, "import failed; zone `%s` \n", zone_name_str);
600 		} else {
601 			import_state = 1;
602 		}
603 	} else {
604 		import_state = 0;
605 	}
606 
607 	if (z_import->cb != NULL) {
608 		z_import->cb(import_state, z_import->cb_param);
609 	}
610 }
611 
612 /** @internal Store rrset that has been imported to zone import context memory pool.
613  * @return -1 if failed; 0 if success. */
zi_record_store(zs_scanner_t * s)614 static int zi_record_store(zs_scanner_t *s)
615 {
616 	if (s->r_data_length > UINT16_MAX) {
617 		/* Due to knot_rrset_add_rdata(..., const uint16_t size, ...); */
618 		kr_log_error(ZSCANNER, "line %"PRIu64": rdata is too long\n",
619 				s->line_counter);
620 		return -1;
621 	}
622 
623 	if (knot_dname_size(s->r_owner) != strlen((const char *)(s->r_owner)) + 1) {
624 		kr_log_error(ZSCANNER, "line %"PRIu64
625 				": owner name contains zero byte, skip\n",
626 				s->line_counter);
627 		return 0;
628 	}
629 
630 	zone_import_ctx_t *z_import = (zone_import_ctx_t *)s->process.data;
631 
632 	knot_rrset_t *new_rr = knot_rrset_new(s->r_owner, s->r_type, s->r_class,
633 					      s->r_ttl, &z_import->pool);
634 	if (!new_rr) {
635 		kr_log_error(ZSCANNER, "line %"PRIu64": error creating rrset\n",
636 				s->line_counter);
637 		return -1;
638 	}
639 	int res = knot_rrset_add_rdata(new_rr, s->r_data, s->r_data_length,
640 				       &z_import->pool);
641 	if (res != KNOT_EOK) {
642 		kr_log_error(ZSCANNER, "line %"PRIu64": error adding rdata to rrset\n",
643 				s->line_counter);
644 		return -1;
645 	}
646 
647 	/* Records in zone file may not be grouped by name and RR type.
648 	 * Use map to create search key and
649 	 * avoid ineffective searches across all the imported records. */
650 	char key[KR_RRKEY_LEN];
651 	uint16_t additional_key_field = kr_rrset_type_maysig(new_rr);
652 
653 	res = kr_rrkey(key, new_rr->rclass, new_rr->owner, new_rr->type,
654 		       additional_key_field);
655 	if (res <= 0) {
656 		kr_log_error(ZSCANNER, "line %"PRIu64": error constructing rrkey\n",
657 				s->line_counter);
658 		return -1;
659 	}
660 
661 	knot_rrset_t *saved_rr = map_get(&z_import->rrset_indexed, key);
662 	if (saved_rr) {
663 		res = knot_rdataset_merge(&saved_rr->rrs, &new_rr->rrs,
664 					  &z_import->pool);
665 	} else {
666 		res = map_set(&z_import->rrset_indexed, key, new_rr);
667 	}
668 	if (res != 0) {
669 		kr_log_error(ZSCANNER, "line %"PRIu64": error saving parsed rrset\n",
670 				s->line_counter);
671 		return -1;
672 	}
673 
674 	return 0;
675 }
676 
677 /** @internal zscanner callback. */
zi_state_parsing(zs_scanner_t * s)678 static int zi_state_parsing(zs_scanner_t *s)
679 {
680 	bool empty = true;
681 	while (zs_parse_record(s) == 0) {
682 		switch (s->state) {
683 		case ZS_STATE_DATA:
684 			if (zi_record_store(s) != 0) {
685 				return -1;
686 			}
687 			zone_import_ctx_t *z_import = (zone_import_ctx_t *) s->process.data;
688 			empty = false;
689 			if (s->r_type == 6) {
690 				z_import->origin = knot_dname_copy(s->r_owner,
691                                                                  &z_import->pool);
692 			}
693 			break;
694 		case ZS_STATE_ERROR:
695 			kr_log_error(ZSCANNER, "line: %"PRIu64
696 				     ": parse error; code: %i ('%s')\n",
697 				     s->line_counter, s->error.code,
698 				     zs_strerror(s->error.code));
699 			return -1;
700 		case ZS_STATE_INCLUDE:
701 			kr_log_error(ZSCANNER, "line: %"PRIu64
702 				     ": INCLUDE is not supported\n",
703 				     s->line_counter);
704 			return -1;
705 		case ZS_STATE_EOF:
706 		case ZS_STATE_STOP:
707 			if (empty) {
708 				kr_log_error(ZIMPORT, "empty zone file\n");
709 				return -1;
710 			}
711 			if (!((zone_import_ctx_t *) s->process.data)->origin) {
712 				kr_log_error(ZIMPORT, "zone file doesn't contain SOA record\n");
713 				return -1;
714 			}
715 			return (s->error.counter == 0) ? 0 : -1;
716 		default:
717 			kr_log_error(ZSCANNER, "line: %"PRIu64
718 				     ": unexpected parse state: %i\n",
719 				     s->line_counter, s->state);
720 			return -1;
721 		}
722 	}
723 
724 	return -1;
725 }
726 
zi_zone_import(struct zone_import_ctx * z_import,const char * zone_file,const char * origin,uint16_t rclass,uint32_t ttl)727 int zi_zone_import(struct zone_import_ctx *z_import,
728 		   const char *zone_file, const char *origin,
729 		   uint16_t rclass, uint32_t ttl)
730 {
731 	if (kr_fails_assert(z_import && z_import->worker && zone_file))
732 		return -1;
733 
734 	zs_scanner_t *s = malloc(sizeof(zs_scanner_t));
735 	if (s == NULL) {
736 		kr_log_error(ZSCANNER, "error creating instance of zone scanner (malloc() fails)\n");
737 		return -1;
738 	}
739 
740 	/* zs_init(), zs_set_input_file(), zs_set_processing() returns -1 in case of error,
741 	 * so don't print error code as it meaningless. */
742 	int res = zs_init(s, origin, rclass, ttl);
743 	if (res != 0) {
744 		kr_log_error(ZSCANNER, "error initializing zone scanner instance, error: %i (%s)\n",
745 			     s->error.code, zs_strerror(s->error.code));
746 		free(s);
747 		return -1;
748 	}
749 
750 	res = zs_set_input_file(s, zone_file);
751 	if (res != 0) {
752 		kr_log_error(ZSCANNER, "error opening zone file `%s`, error: %i (%s)\n",
753 			     zone_file, s->error.code, zs_strerror(s->error.code));
754 		zs_deinit(s);
755 		free(s);
756 		return -1;
757 	}
758 
759 	/* Don't set processing and error callbacks as we don't use automatic parsing.
760 	 * Parsing as well error processing will be performed in zi_state_parsing().
761 	 * Store pointer to zone import context for further use. */
762 	if (zs_set_processing(s, NULL, NULL, (void *)z_import) != 0) {
763 		kr_log_error(ZSCANNER, "zs_set_processing() failed for zone file `%s`, "
764 				"error: %i (%s)\n",
765 				zone_file, s->error.code, zs_strerror(s->error.code));
766 		zs_deinit(s);
767 		free(s);
768 		return -1;
769 	}
770 
771 	uint64_t elapsed = 0;
772 	int ret = zi_reset(z_import, 4096);
773 	if (ret == 0) {
774 		z_import->started = true;
775 		z_import->start_timestamp = kr_now();
776 		VERBOSE_MSG(NULL, "[zscanner] started; zone file `%s`\n",
777 			    zone_file);
778 		ret = zi_state_parsing(s);
779 		if (ret == 0) {
780 			/* Try to find TA for worker->z_import.origin. */
781 			map_t *trust_anchors = &z_import->worker->engine->resolver.trust_anchors;
782 			knot_rrset_t *rr = kr_ta_get(trust_anchors, z_import->origin);
783 			if (!rr) {
784 				/* For now - fail.
785 				 * TODO - query DS and continue after answer had been obtained. */
786 				KR_DNAME_GET_STR(zone_name_str, z_import->origin);
787 				kr_log_error(ZIMPORT, "no TA found for `%s`, fail\n", zone_name_str);
788 				ret = 1;
789 			}
790 			elapsed = kr_now() - z_import->start_timestamp;
791 			elapsed = elapsed > UINT_MAX ? UINT_MAX : elapsed;
792 		}
793 	}
794 	zs_deinit(s);
795 	free(s);
796 
797 	if (ret != 0) {
798 		kr_log_error(ZSCANNER, "error parsing zone file `%s`\n", zone_file);
799 		z_import->started = false;
800 		return ret;
801 	}
802 
803 	VERBOSE_MSG(NULL, "[zscanner] finished in %"PRIu64" ms; zone file `%s`\n",
804 			    elapsed, zone_file);
805 	map_walk(&z_import->rrset_indexed, zi_mapwalk_preprocess, z_import);
806 
807 	/* Zone have been parsed already, so start the import. */
808 	uv_timer_start(&z_import->timer, zi_zone_process,
809 		       ZONE_IMPORT_PAUSE, ZONE_IMPORT_PAUSE);
810 
811 	return 0;
812 }
813 
zi_import_started(struct zone_import_ctx * z_import)814 bool zi_import_started(struct zone_import_ctx *z_import)
815 {
816 	return z_import ? z_import->started : false;
817 }
818