1 /*  Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
2 
3     This program is free software: you can redistribute it and/or modify
4     it under the terms of the GNU General Public License as published by
5     the Free Software Foundation, either version 3 of the License, or
6     (at your option) any later version.
7 
8     This program is distributed in the hope that it will be useful,
9     but WITHOUT ANY WARRANTY; without even the implied warranty of
10     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11     GNU General Public License for more details.
12 
13     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 "libknot/libknot.h"
18 #include "knot/dnssec/rrset-sign.h"
19 #include "knot/nameserver/internet.h"
20 #include "knot/nameserver/nsec_proofs.h"
21 #include "knot/nameserver/query_module.h"
22 #include "knot/zone/serial.h"
23 #include "contrib/mempattern.h"
24 
25 /*! \brief Check if given node was already visited. */
wildcard_has_visited(knotd_qdata_t * qdata,const zone_node_t * node)26 static int wildcard_has_visited(knotd_qdata_t *qdata, const zone_node_t *node)
27 {
28 	struct wildcard_hit *item;
29 	WALK_LIST(item, qdata->extra->wildcards) {
30 		if (item->node == node) {
31 			return true;
32 		}
33 	}
34 	return false;
35 }
36 
37 /*! \brief Mark given node as visited. */
wildcard_visit(knotd_qdata_t * qdata,const zone_node_t * node,const zone_node_t * prev,const knot_dname_t * sname)38 static int wildcard_visit(knotd_qdata_t *qdata, const zone_node_t *node,
39                           const zone_node_t *prev, const knot_dname_t *sname)
40 {
41 	assert(qdata);
42 	assert(node);
43 
44 	if (node->flags & NODE_FLAGS_NONAUTH) {
45 		return KNOT_EOK;
46 	}
47 
48 	knot_mm_t *mm = qdata->mm;
49 	struct wildcard_hit *item = mm_alloc(mm, sizeof(struct wildcard_hit));
50 	item->node = node;
51 	item->prev = prev;
52 	item->sname = sname;
53 	add_tail(&qdata->extra->wildcards, (node_t *)item);
54 	return KNOT_EOK;
55 }
56 
57 /*! \brief Synthetizes a CNAME RR from a DNAME. */
dname_cname_synth(const knot_rrset_t * dname_rr,const knot_dname_t * qname,knot_rrset_t * cname_rrset,knot_mm_t * mm)58 static int dname_cname_synth(const knot_rrset_t *dname_rr,
59                              const knot_dname_t *qname,
60                              knot_rrset_t *cname_rrset,
61                              knot_mm_t *mm)
62 {
63 	if (cname_rrset == NULL) {
64 		return KNOT_EINVAL;
65 	}
66 	knot_dname_t *owner_copy = knot_dname_copy(qname, mm);
67 	if (owner_copy == NULL) {
68 		return KNOT_ENOMEM;
69 	}
70 	knot_rrset_init(cname_rrset, owner_copy, KNOT_RRTYPE_CNAME, dname_rr->rclass,
71 	                dname_rr->ttl);
72 
73 	/* Replace last labels of qname with DNAME. */
74 	const knot_dname_t *dname_wire = dname_rr->owner;
75 	const knot_dname_t *dname_tgt = knot_dname_target(dname_rr->rrs.rdata);
76 	size_t labels = knot_dname_labels(dname_wire, NULL);
77 	knot_dname_t *cname = knot_dname_replace_suffix(qname, labels, dname_tgt, mm);
78 	if (cname == NULL) {
79 		knot_dname_free(owner_copy, mm);
80 		return KNOT_ENOMEM;
81 	}
82 
83 	/* Store DNAME into RDATA. */
84 	size_t cname_size = knot_dname_size(cname);
85 	uint8_t cname_rdata[cname_size];
86 	memcpy(cname_rdata, cname, cname_size);
87 	knot_dname_free(cname, mm);
88 
89 	int ret = knot_rrset_add_rdata(cname_rrset, cname_rdata, cname_size, mm);
90 	if (ret != KNOT_EOK) {
91 		knot_dname_free(owner_copy, mm);
92 		return ret;
93 	}
94 
95 	return KNOT_EOK;
96 }
97 
98 /*!
99  * \brief Checks if the name created by replacing the owner of \a dname_rrset
100  *        in the \a qname by the DNAME's target would be longer than allowed.
101  */
dname_cname_cannot_synth(const knot_rrset_t * rrset,const knot_dname_t * qname)102 static bool dname_cname_cannot_synth(const knot_rrset_t *rrset, const knot_dname_t *qname)
103 {
104 	if (knot_dname_labels(qname, NULL) - knot_dname_labels(rrset->owner, NULL) +
105 	    knot_dname_labels(knot_dname_target(rrset->rrs.rdata), NULL) > KNOT_DNAME_MAXLABELS) {
106 		return true;
107 	} else if (knot_dname_size(qname) - knot_dname_size(rrset->owner) +
108 	           knot_dname_size(knot_dname_target(rrset->rrs.rdata)) > KNOT_DNAME_MAXLEN) {
109 		return true;
110 	} else {
111 		return false;
112 	}
113 }
114 
115 /*! \brief DNSSEC both requested & available. */
have_dnssec(knotd_qdata_t * qdata)116 static bool have_dnssec(knotd_qdata_t *qdata)
117 {
118 	return knot_pkt_has_dnssec(qdata->query) &&
119 	       qdata->extra->contents->dnssec;
120 }
121 
122 /*! \brief This is a wildcard-covered or any other terminal node for QNAME.
123  *         e.g. positive answer.
124  */
put_answer(knot_pkt_t * pkt,uint16_t type,knotd_qdata_t * qdata)125 static int put_answer(knot_pkt_t *pkt, uint16_t type, knotd_qdata_t *qdata)
126 {
127 	/* Wildcard expansion or exact match, either way RRSet owner is
128 	 * is QNAME. We can fake name synthesis by setting compression hint to
129 	 * QNAME position. Just need to check if we're answering QNAME and not
130 	 * a CNAME target.
131 	 */
132 	uint16_t compr_hint = KNOT_COMPR_HINT_NONE;
133 	if (pkt->rrset_count == 0) { /* Guaranteed first answer. */
134 		compr_hint = KNOT_COMPR_HINT_QNAME;
135 	}
136 
137 	unsigned put_rr_flags = (qdata->params->flags & KNOTD_QUERY_FLAG_LIMIT_SIZE) ?
138 	                        KNOT_PF_NULL : KNOT_PF_NOTRUNC;
139 	put_rr_flags |= KNOT_PF_ORIGTTL;
140 
141 	knot_rrset_t rrsigs = node_rrset(qdata->extra->node, KNOT_RRTYPE_RRSIG);
142 	knot_rrset_t rrset;
143 	switch (type) {
144 	case KNOT_RRTYPE_ANY: /* Put one RRSet, not all. */
145 		rrset = node_rrset_at(qdata->extra->node, 0);
146 		break;
147 	case KNOT_RRTYPE_RRSIG: /* Put some RRSIGs, not all. */
148 		if (!knot_rrset_empty(&rrsigs)) {
149 			knot_rrset_init(&rrset, rrsigs.owner, rrsigs.type, rrsigs.rclass, rrsigs.ttl);
150 			int ret = knot_synth_rrsig(KNOT_RRTYPE_ANY, &rrsigs.rrs, &rrset.rrs, qdata->mm);
151 			if (ret != KNOT_EOK) {
152 				return ret;
153 			}
154 		} else {
155 			knot_rrset_init_empty(&rrset);
156 		}
157 		break;
158 	default: /* Single RRSet of given type. */
159 		rrset = node_rrset(qdata->extra->node, type);
160 		break;
161 	}
162 
163 	if (knot_rrset_empty(&rrset)) {
164 		return KNOT_EOK;
165 	}
166 
167 	return process_query_put_rr(pkt, qdata, &rrset, &rrsigs, compr_hint, put_rr_flags);
168 }
169 
170 /*! \brief Puts optional SOA RRSet to the Authority section of the response. */
put_authority_soa(knot_pkt_t * pkt,knotd_qdata_t * qdata,const zone_contents_t * zone)171 static int put_authority_soa(knot_pkt_t *pkt, knotd_qdata_t *qdata,
172                              const zone_contents_t *zone)
173 {
174 	knot_rrset_t soa = node_rrset(zone->apex, KNOT_RRTYPE_SOA);
175 	knot_rrset_t rrsigs = node_rrset(zone->apex, KNOT_RRTYPE_RRSIG);
176 	return process_query_put_rr(pkt, qdata, &soa, &rrsigs,
177 	                            KNOT_COMPR_HINT_NONE,
178 	                            KNOT_PF_NOTRUNC | KNOT_PF_SOAMINTTL);
179 }
180 
181 /*! \brief Put the delegation NS RRSet to the Authority section. */
put_delegation(knot_pkt_t * pkt,knotd_qdata_t * qdata)182 static int put_delegation(knot_pkt_t *pkt, knotd_qdata_t *qdata)
183 {
184 	/* Find closest delegation point. */
185 	while (!(qdata->extra->node->flags & NODE_FLAGS_DELEG)) {
186 		qdata->extra->node = node_parent(qdata->extra->node);
187 	}
188 
189 	/* Insert NS record. */
190 	knot_rrset_t rrset = node_rrset(qdata->extra->node, KNOT_RRTYPE_NS);
191 	knot_rrset_t rrsigs = node_rrset(qdata->extra->node, KNOT_RRTYPE_RRSIG);
192 	return process_query_put_rr(pkt, qdata, &rrset, &rrsigs,
193 	                            KNOT_COMPR_HINT_NONE, 0);
194 }
195 
196 /*! \brief Put additional records for given RR. */
put_additional(knot_pkt_t * pkt,const knot_rrset_t * rr,knotd_qdata_t * qdata,knot_rrinfo_t * info,int state)197 static int put_additional(knot_pkt_t *pkt, const knot_rrset_t *rr,
198                           knotd_qdata_t *qdata, knot_rrinfo_t *info, int state)
199 {
200 	if (rr->additional == NULL) {
201 		return KNOT_EOK;
202 	}
203 
204 	/* Valid types for ADDITIONALS insertion. */
205 	/* \note Not resolving CNAMEs as MX/NS name must not be an alias. (RFC2181/10.3) */
206 	static const uint16_t ar_type_list[] = { KNOT_RRTYPE_A, KNOT_RRTYPE_AAAA };
207 	static const int ar_type_count = 2;
208 
209 	int ret = KNOT_EOK;
210 
211 	additional_t *additional = (additional_t *)rr->additional;
212 
213 	/* Iterate over the additionals. */
214 	for (uint16_t i = 0; i < additional->count; i++) {
215 		glue_t *glue = &additional->glues[i];
216 		uint32_t flags = KNOT_PF_NULL;
217 
218 		/* Optional glue doesn't cause truncation. (RFC 1034/4.3.2 step 3b). */
219 		if (state != KNOTD_IN_STATE_DELEG || glue->optional) {
220 			flags |= KNOT_PF_NOTRUNC;
221 		}
222 
223 		uint16_t hint = knot_compr_hint(info, KNOT_COMPR_HINT_RDATA +
224 		                                glue->ns_pos);
225 		const zone_node_t *gluenode = glue_node(glue, qdata->extra->node);
226 		knot_rrset_t rrsigs = node_rrset(gluenode, KNOT_RRTYPE_RRSIG);
227 		for (int k = 0; k < ar_type_count; ++k) {
228 			knot_rrset_t rrset = node_rrset(gluenode, ar_type_list[k]);
229 			if (knot_rrset_empty(&rrset)) {
230 				continue;
231 			}
232 			ret = process_query_put_rr(pkt, qdata, &rrset, &rrsigs,
233 			                           hint, flags);
234 			if (ret != KNOT_EOK) {
235 				break;
236 			}
237 		}
238 	}
239 
240 	return ret;
241 }
242 
follow_cname(knot_pkt_t * pkt,uint16_t rrtype,knotd_qdata_t * qdata)243 static int follow_cname(knot_pkt_t *pkt, uint16_t rrtype, knotd_qdata_t *qdata)
244 {
245 	/* CNAME chain processing limit. */
246 	if (++qdata->extra->cname_chain > CNAME_CHAIN_MAX) {
247 		qdata->extra->node = NULL;
248 		return KNOTD_IN_STATE_HIT;
249 	}
250 
251 	const zone_node_t *cname_node = qdata->extra->node;
252 	knot_rrset_t cname_rr = node_rrset(qdata->extra->node, rrtype);
253 	knot_rrset_t rrsigs = node_rrset(qdata->extra->node, KNOT_RRTYPE_RRSIG);
254 
255 	assert(!knot_rrset_empty(&cname_rr));
256 
257 	/* Check whether RR is already in the packet. */
258 	uint16_t flags = KNOT_PF_CHECKDUP;
259 
260 	/* Now, try to put CNAME to answer. */
261 	uint16_t rr_count_before = pkt->rrset_count;
262 	int ret = process_query_put_rr(pkt, qdata, &cname_rr, &rrsigs, 0, flags);
263 	switch (ret) {
264 	case KNOT_EOK:    break;
265 	case KNOT_ESPACE: return KNOTD_IN_STATE_TRUNC;
266 	default:          return KNOTD_IN_STATE_ERROR;
267 	}
268 
269 	/* Synthesize CNAME if followed DNAME. */
270 	if (rrtype == KNOT_RRTYPE_DNAME) {
271 		if (dname_cname_cannot_synth(&cname_rr, qdata->name)) {
272 			qdata->rcode = KNOT_RCODE_YXDOMAIN;
273 		} else {
274 			knot_rrset_t dname_rr = cname_rr;
275 			ret = dname_cname_synth(&dname_rr, qdata->name,
276 			                        &cname_rr, &pkt->mm);
277 			if (ret != KNOT_EOK) {
278 				qdata->rcode = KNOT_RCODE_SERVFAIL;
279 				return KNOTD_IN_STATE_ERROR;
280 			}
281 			ret = process_query_put_rr(pkt, qdata, &cname_rr, NULL, 0, KNOT_PF_FREE);
282 			switch (ret) {
283 			case KNOT_EOK:    break;
284 			case KNOT_ESPACE: return KNOTD_IN_STATE_TRUNC;
285 			default:          return KNOTD_IN_STATE_ERROR;
286 			}
287 			if (knot_pkt_qtype(pkt) == KNOT_RRTYPE_CNAME) {
288 				/* Synthesized CNAME is a perfect answer to query. */
289 				return KNOTD_IN_STATE_HIT;
290 			}
291 		}
292 	}
293 
294 	/* Check if RR count increased. */
295 	if (pkt->rrset_count <= rr_count_before) {
296 		qdata->extra->node = NULL; /* Act as if the name leads to nowhere. */
297 		return KNOTD_IN_STATE_HIT;
298 	}
299 
300 	/* If node is a wildcard, follow only if we didn't visit the same node
301 	 * earlier, as that would mean a CNAME loop. */
302 	if (knot_dname_is_wildcard(cname_node->owner)) {
303 
304 		/* Check if is not in wildcard nodes (loop). */
305 		if (wildcard_has_visited(qdata, cname_node)) {
306 			qdata->extra->node = NULL; /* Act as if the name leads to nowhere. */
307 
308 			if (wildcard_visit(qdata, cname_node, qdata->extra->previous, qdata->name) != KNOT_EOK) { // in case of loop, re-add this cname_node because it might have different qdata->name
309 				return KNOTD_IN_STATE_ERROR;
310 			}
311 			return KNOTD_IN_STATE_HIT;
312 		}
313 
314 		/* Put to wildcard node list. */
315 		if (wildcard_visit(qdata, cname_node, qdata->extra->previous, qdata->name) != KNOT_EOK) {
316 			return KNOTD_IN_STATE_ERROR;
317 		}
318 	}
319 
320 	/* Now follow the next CNAME TARGET. */
321 	qdata->name = knot_cname_name(cname_rr.rrs.rdata);
322 
323 	return KNOTD_IN_STATE_FOLLOW;
324 }
325 
name_found(knot_pkt_t * pkt,knotd_qdata_t * qdata)326 static int name_found(knot_pkt_t *pkt, knotd_qdata_t *qdata)
327 {
328 	uint16_t qtype = knot_pkt_qtype(pkt);
329 
330 	/* DS query at DP is answered normally, but everything else at/below DP
331 	 * triggers referral response. */
332 	if (((qdata->extra->node->flags & NODE_FLAGS_DELEG) && qtype != KNOT_RRTYPE_DS) ||
333 	    (qdata->extra->node->flags & NODE_FLAGS_NONAUTH)) {
334 		return KNOTD_IN_STATE_DELEG;
335 	}
336 
337 	if (node_rrtype_exists(qdata->extra->node, KNOT_RRTYPE_CNAME)
338 	    && qtype != KNOT_RRTYPE_CNAME
339 	    && qtype != KNOT_RRTYPE_RRSIG
340 	    && qtype != KNOT_RRTYPE_NSEC
341 	    && qtype != KNOT_RRTYPE_ANY) {
342 		return follow_cname(pkt, KNOT_RRTYPE_CNAME, qdata);
343 	}
344 
345 	uint16_t old_rrcount = pkt->rrset_count;
346 	int ret = put_answer(pkt, qtype, qdata);
347 	if (ret != KNOT_EOK) {
348 		if (ret == KNOT_ESPACE && (qdata->params->flags & KNOTD_QUERY_FLAG_LIMIT_SIZE)) {
349 			return KNOTD_IN_STATE_TRUNC;
350 		} else {
351 			return KNOTD_IN_STATE_ERROR;
352 		}
353 	}
354 
355 	/* Check for NODATA (=0 RRs added). */
356 	if (old_rrcount == pkt->rrset_count) {
357 		return KNOTD_IN_STATE_NODATA;
358 	} else {
359 		return KNOTD_IN_STATE_HIT;
360 	}
361 }
362 
name_not_found(knot_pkt_t * pkt,knotd_qdata_t * qdata)363 static int name_not_found(knot_pkt_t *pkt, knotd_qdata_t *qdata)
364 {
365 	/* Name is covered by wildcard. */
366 	if (qdata->extra->encloser->flags & NODE_FLAGS_WILDCARD_CHILD) {
367 		/* Find wildcard child in the zone. */
368 		const zone_node_t *wildcard_node =
369 			zone_contents_find_wildcard_child(
370 				qdata->extra->contents, qdata->extra->encloser);
371 
372 		qdata->extra->node = wildcard_node;
373 		assert(qdata->extra->node != NULL);
374 
375 		/* Follow expanded wildcard. */
376 		int next_state = name_found(pkt, qdata);
377 
378 		/* Put to wildcard node list. */
379 		if (wildcard_has_visited(qdata, wildcard_node)) {
380 			return next_state;
381 		}
382 		if (wildcard_visit(qdata, wildcard_node, qdata->extra->previous, qdata->name) != KNOT_EOK) {
383 			next_state = KNOTD_IN_STATE_ERROR;
384 		}
385 
386 		return next_state;
387 	}
388 
389 	/* Name is under DNAME, use it for substitution. */
390 	bool encloser_auth = !(qdata->extra->encloser->flags & (NODE_FLAGS_NONAUTH | NODE_FLAGS_DELEG));
391 	knot_rrset_t dname_rrset = node_rrset(qdata->extra->encloser, KNOT_RRTYPE_DNAME);
392 	if (encloser_auth && !knot_rrset_empty(&dname_rrset)) {
393 		qdata->extra->node = qdata->extra->encloser; /* Follow encloser as new node. */
394 		return follow_cname(pkt, KNOT_RRTYPE_DNAME, qdata);
395 	}
396 
397 	/* Look up an authoritative encloser or its parent. */
398 	const zone_node_t *node = qdata->extra->encloser;
399 	while (node->rrset_count == 0 || node->flags & NODE_FLAGS_NONAUTH) {
400 		node = node_parent(node);
401 		assert(node);
402 	}
403 
404 	/* Name is below delegation. */
405 	if ((node->flags & NODE_FLAGS_DELEG)) {
406 		qdata->extra->node = node;
407 		return KNOTD_IN_STATE_DELEG;
408 	}
409 
410 	return KNOTD_IN_STATE_MISS;
411 }
412 
solve_name(int state,knot_pkt_t * pkt,knotd_qdata_t * qdata)413 static int solve_name(int state, knot_pkt_t *pkt, knotd_qdata_t *qdata)
414 {
415 	int ret = zone_contents_find_dname(qdata->extra->contents, qdata->name,
416 	                                   &qdata->extra->node, &qdata->extra->encloser,
417 	                                   &qdata->extra->previous);
418 
419 	switch (ret) {
420 	case ZONE_NAME_FOUND:
421 		return name_found(pkt, qdata);
422 	case ZONE_NAME_NOT_FOUND:
423 		return name_not_found(pkt, qdata);
424 	case KNOT_EOUTOFZONE:
425 		assert(state == KNOTD_IN_STATE_FOLLOW); /* CNAME/DNAME chain only. */
426 		return KNOTD_IN_STATE_HIT;
427 	default:
428 		return KNOTD_IN_STATE_ERROR;
429 	}
430 }
431 
solve_answer(int state,knot_pkt_t * pkt,knotd_qdata_t * qdata,void * ctx)432 static int solve_answer(int state, knot_pkt_t *pkt, knotd_qdata_t *qdata, void *ctx)
433 {
434 	int old_state = state;
435 
436 	/* Do not solve if already solved, e.g. in a module. */
437 	if (state == KNOTD_IN_STATE_HIT) {
438 		return state;
439 	}
440 
441 	/* Get answer to QNAME. */
442 	state = solve_name(state, pkt, qdata);
443 
444 	/* Promote NODATA from a module if nothing found in zone. */
445 	if (state == KNOTD_IN_STATE_MISS && old_state == KNOTD_IN_STATE_NODATA) {
446 		state = old_state;
447 	}
448 
449 	/* Is authoritative answer unless referral.
450 	 * Must check before we chase the CNAME chain. */
451 	if (state != KNOTD_IN_STATE_DELEG) {
452 		knot_wire_set_aa(pkt->wire);
453 	}
454 
455 	/* Additional resolving for CNAME/DNAME chain. */
456 	while (state == KNOTD_IN_STATE_FOLLOW) {
457 		state = solve_name(state, pkt, qdata);
458 	}
459 
460 	return state;
461 }
462 
solve_answer_dnssec(int state,knot_pkt_t * pkt,knotd_qdata_t * qdata,void * ctx)463 static int solve_answer_dnssec(int state, knot_pkt_t *pkt, knotd_qdata_t *qdata, void *ctx)
464 {
465 	/* RFC4035, section 3.1 RRSIGs for RRs in ANSWER are mandatory. */
466 	int ret = nsec_append_rrsigs(pkt, qdata, false);
467 	switch (ret) {
468 	case KNOT_ESPACE: return KNOTD_IN_STATE_TRUNC;
469 	case KNOT_EOK:    return state;
470 	default:          return KNOTD_IN_STATE_ERROR;
471 	}
472 }
473 
solve_authority(int state,knot_pkt_t * pkt,knotd_qdata_t * qdata,void * ctx)474 static int solve_authority(int state, knot_pkt_t *pkt, knotd_qdata_t *qdata, void *ctx)
475 {
476 	int ret = KNOT_ERROR;
477 	const zone_contents_t *zone_contents = qdata->extra->contents;
478 
479 	switch (state) {
480 	case KNOTD_IN_STATE_HIT:    /* Positive response. */
481 		ret = KNOT_EOK;
482 		break;
483 	case KNOTD_IN_STATE_MISS:   /* MISS, set NXDOMAIN RCODE. */
484 		qdata->rcode = KNOT_RCODE_NXDOMAIN;
485 		ret = put_authority_soa(pkt, qdata, zone_contents);
486 		break;
487 	case KNOTD_IN_STATE_NODATA: /* NODATA append AUTHORITY SOA. */
488 		ret = put_authority_soa(pkt, qdata, zone_contents);
489 		break;
490 	case KNOTD_IN_STATE_DELEG:  /* Referral response. */
491 		ret = put_delegation(pkt, qdata);
492 		break;
493 	case KNOTD_IN_STATE_TRUNC:  /* Truncated ANSWER. */
494 		ret = KNOT_ESPACE;
495 		break;
496 	case KNOTD_IN_STATE_ERROR:  /* Error resolving ANSWER. */
497 		break;
498 	default:
499 		assert(0);
500 		break;
501 	}
502 
503 	/* Evaluate final state. */
504 	switch (ret) {
505 	case KNOT_EOK:    return state; /* Keep current state. */
506 	case KNOT_ESPACE: return KNOTD_IN_STATE_TRUNC; /* Truncated. */
507 	default:          return KNOTD_IN_STATE_ERROR; /* Error. */
508 	}
509 }
510 
solve_authority_dnssec(int state,knot_pkt_t * pkt,knotd_qdata_t * qdata,void * ctx)511 static int solve_authority_dnssec(int state, knot_pkt_t *pkt, knotd_qdata_t *qdata, void *ctx)
512 {
513 	int ret = KNOT_ERROR;
514 
515 	/* Authenticated denial of existence. */
516 	switch (state) {
517 	case KNOTD_IN_STATE_HIT:    ret = KNOT_EOK; break;
518 	case KNOTD_IN_STATE_MISS:   ret = nsec_prove_nxdomain(pkt, qdata); break;
519 	case KNOTD_IN_STATE_NODATA: ret = nsec_prove_nodata(pkt, qdata); break;
520 	case KNOTD_IN_STATE_DELEG:  ret = nsec_prove_dp_security(pkt, qdata); break;
521 	case KNOTD_IN_STATE_TRUNC:  ret = KNOT_ESPACE; break;
522 	case KNOTD_IN_STATE_ERROR:  ret = KNOT_ERROR; break;
523 	default:
524 		assert(0);
525 		break;
526 	}
527 
528 	/* RFC4035 3.1.3 Prove visited wildcards.
529 	 * Wildcard expansion applies for Name Error, Wildcard Answer and
530 	 * No Data proofs if at one point the search expanded a wildcard node. */
531 	if (ret == KNOT_EOK) {
532 		ret = nsec_prove_wildcards(pkt, qdata);
533 	}
534 
535 	/* RFC4035, section 3.1 RRSIGs for RRs in AUTHORITY are mandatory. */
536 	if (ret == KNOT_EOK) {
537 		ret = nsec_append_rrsigs(pkt, qdata, false);
538 	}
539 
540 	/* Evaluate final state. */
541 	switch (ret) {
542 	case KNOT_EOK:    return state; /* Keep current state. */
543 	case KNOT_ESPACE: return KNOTD_IN_STATE_TRUNC; /* Truncated. */
544 	default:          return KNOTD_IN_STATE_ERROR; /* Error. */
545 	}
546 }
547 
solve_additional(int state,knot_pkt_t * pkt,knotd_qdata_t * qdata,void * ctx)548 static int solve_additional(int state, knot_pkt_t *pkt, knotd_qdata_t *qdata,
549                             void *ctx)
550 {
551 	int ret = KNOT_EOK;
552 
553 	/* Scan all RRs in ANSWER/AUTHORITY. */
554 	for (uint16_t i = 0; i < pkt->rrset_count; ++i) {
555 		knot_rrset_t *rr = &pkt->rr[i];
556 		knot_rrinfo_t *info = &pkt->rr_info[i];
557 
558 		/* Skip types for which it doesn't apply. */
559 		if (!knot_rrtype_additional_needed(rr->type)) {
560 			continue;
561 		}
562 
563 		/* Put additional records for given type. */
564 		ret = put_additional(pkt, rr, qdata, info, state);
565 		if (ret != KNOT_EOK) {
566 			break;
567 		}
568 	}
569 
570 	/* Evaluate final state. */
571 	switch (ret) {
572 	case KNOT_EOK:    return state; /* Keep current state. */
573 	case KNOT_ESPACE: return KNOTD_IN_STATE_TRUNC; /* Truncated. */
574 	default:          return KNOTD_IN_STATE_ERROR; /* Error. */
575 	}
576 }
577 
solve_additional_dnssec(int state,knot_pkt_t * pkt,knotd_qdata_t * qdata,void * ctx)578 static int solve_additional_dnssec(int state, knot_pkt_t *pkt, knotd_qdata_t *qdata, void *ctx)
579 {
580 	/* RFC4035, section 3.1 RRSIGs for RRs in ADDITIONAL are optional. */
581 	int ret = nsec_append_rrsigs(pkt, qdata, true);
582 	switch (ret) {
583 	case KNOT_ESPACE: return KNOTD_IN_STATE_TRUNC;
584 	case KNOT_EOK:    return state;
585 	default:          return KNOTD_IN_STATE_ERROR;
586 	}
587 }
588 
589 /*! \brief Helper for internet_query repetitive code. */
590 #define SOLVE_STEP(solver, state, context) \
591 	state = (solver)(state, pkt, qdata, context); \
592 	if (state == KNOTD_IN_STATE_TRUNC) { \
593 		return KNOT_STATE_DONE; \
594 	} else if (state == KNOTD_IN_STATE_ERROR) { \
595 		return KNOT_STATE_FAIL; \
596 	}
597 
answer_query(knot_pkt_t * pkt,knotd_qdata_t * qdata)598 static int answer_query(knot_pkt_t *pkt, knotd_qdata_t *qdata)
599 {
600 	int state = KNOTD_IN_STATE_BEGIN;
601 	struct query_plan *plan = qdata->extra->zone->query_plan;
602 	struct query_step *step;
603 
604 	bool with_dnssec = have_dnssec(qdata);
605 
606 	/* Resolve PREANSWER. */
607 	if (plan != NULL) {
608 		WALK_LIST(step, plan->stage[KNOTD_STAGE_PREANSWER]) {
609 			SOLVE_STEP(step->process, state, step->ctx);
610 		}
611 	}
612 
613 	/* Resolve ANSWER. */
614 	knot_pkt_begin(pkt, KNOT_ANSWER);
615 	SOLVE_STEP(solve_answer, state, NULL);
616 	if (with_dnssec) {
617 		SOLVE_STEP(solve_answer_dnssec, state, NULL);
618 	}
619 	if (plan != NULL) {
620 		WALK_LIST(step, plan->stage[KNOTD_STAGE_ANSWER]) {
621 			SOLVE_STEP(step->process, state, step->ctx);
622 		}
623 	}
624 
625 	/* Resolve AUTHORITY. */
626 	knot_pkt_begin(pkt, KNOT_AUTHORITY);
627 	SOLVE_STEP(solve_authority, state, NULL);
628 	if (with_dnssec) {
629 		SOLVE_STEP(solve_authority_dnssec, state, NULL);
630 	}
631 	if (plan != NULL) {
632 		WALK_LIST(step, plan->stage[KNOTD_STAGE_AUTHORITY]) {
633 			SOLVE_STEP(step->process, state, step->ctx);
634 		}
635 	}
636 
637 	/* Resolve ADDITIONAL. */
638 	knot_pkt_begin(pkt, KNOT_ADDITIONAL);
639 	SOLVE_STEP(solve_additional, state, NULL);
640 	if (with_dnssec) {
641 		SOLVE_STEP(solve_additional_dnssec, state, NULL);
642 	}
643 	if (plan != NULL) {
644 		WALK_LIST(step, plan->stage[KNOTD_STAGE_ADDITIONAL]) {
645 			SOLVE_STEP(step->process, state, step->ctx);
646 		}
647 	}
648 
649 	/* Write resulting RCODE. */
650 	knot_wire_set_rcode(pkt->wire, qdata->rcode);
651 
652 	return KNOT_STATE_DONE;
653 }
654 
internet_process_query(knot_pkt_t * pkt,knotd_qdata_t * qdata)655 int internet_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata)
656 {
657 	if (pkt == NULL || qdata == NULL) {
658 		return KNOT_STATE_FAIL;
659 	}
660 
661 	/* Check valid zone, transaction security (optional) and contents. */
662 	NS_NEED_ZONE(qdata, KNOT_RCODE_REFUSED);
663 
664 	/* No applicable ACL, refuse transaction security. */
665 	if (knot_pkt_has_tsig(qdata->query)) {
666 		/* We have been challenged... */
667 		NS_NEED_AUTH(qdata, ACL_ACTION_NONE);
668 
669 		/* Reserve space for TSIG. */
670 		int ret = knot_pkt_reserve(pkt, knot_tsig_wire_size(&qdata->sign.tsig_key));
671 		if (ret != KNOT_EOK) {
672 			return KNOT_STATE_FAIL;
673 		}
674 	}
675 
676 	NS_NEED_ZONE_CONTENTS(qdata); /* Expired */
677 
678 	/* Get answer to QNAME. */
679 	qdata->name = knot_pkt_qname(qdata->query);
680 
681 	return answer_query(pkt, qdata);
682 }
683