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