Attachment 'iterate.c'
Download 1 /* Copyright (C) 2014 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 <http://www.gnu.org/licenses/>.
15 */
16
17 #include <sys/time.h>
18 #include <assert.h>
19
20 #include <libknot/descriptor.h>
21 #include <libknot/rrtype/rdname.h>
22 #include <libknot/rrtype/rrsig.h>
23
24 #include "lib/layer/iterate.h"
25 #include "lib/resolve.h"
26 #include "lib/rplan.h"
27 #include "lib/defines.h"
28 #include "lib/nsrep.h"
29 #include "lib/module.h"
30 #include "lib/dnssec/ta.h"
31
32 #define DEBUG_MSG(fmt...) QRDEBUG(req->current_query, "iter", fmt)
33 #define STRICT_MODE 1
34
35 /* Iterator often walks through packet section, this is an abstraction. */
36 typedef int (*rr_callback_t)(const knot_rrset_t *, unsigned, struct kr_request *);
37
38 /** Return minimized QNAME/QTYPE for current zone cut. */
39 static const knot_dname_t *minimized_qname(struct kr_query *query, uint16_t *qtype)
40 {
41 /* Minimization disabled. */
42 const knot_dname_t *qname = query->sname;
43 if (qname[0] == '\0' || query->flags & (QUERY_NO_MINIMIZE|QUERY_STUB)) {
44 return qname;
45 }
46
47 /* Minimize name to contain current zone cut + 1 label. */
48 int cut_labels = knot_dname_labels(query->zone_cut.name, NULL);
49 int qname_labels = knot_dname_labels(qname, NULL);
50 while(qname[0] && qname_labels > cut_labels + 1) {
51 qname = knot_wire_next_label(qname, NULL);
52 qname_labels -= 1;
53 *qtype = KNOT_RRTYPE_NS;
54 }
55 return qname;
56
57 /* Hide QTYPE if minimized. */
58 if (qname != query->sname) {
59 *qtype = KNOT_RRTYPE_NS;
60 }
61
62 return qname;
63 }
64
65 /** Answer is paired to query. */
66 static bool is_paired_to_query(const knot_pkt_t *answer, struct kr_query *query)
67 {
68 uint16_t qtype = query->stype;
69 const knot_dname_t *qname = minimized_qname(query, &qtype);
70
71 return query->id == knot_wire_get_id(answer->wire) &&
72 knot_wire_get_qdcount(answer->wire) > 0 &&
73 (query->sclass == KNOT_CLASS_ANY || query->sclass == knot_pkt_qclass(answer)) &&
74 qtype == knot_pkt_qtype(answer) &&
75 knot_dname_is_equal(qname, knot_pkt_qname(answer));
76 }
77
78 /** Relaxed rule for AA, either AA=1 or SOA matching zone cut is required. */
79 static bool is_authoritative(const knot_pkt_t *answer, struct kr_query *query)
80 {
81 if (knot_wire_get_aa(answer->wire)) {
82 return true;
83 }
84 return false;
85
86 const knot_pktsection_t *ns = knot_pkt_section(answer, KNOT_AUTHORITY);
87 for (unsigned i = 0; i < ns->count; ++i) {
88 const knot_rrset_t *rr = knot_pkt_rr(ns, i);
89 if (rr->type == KNOT_RRTYPE_SOA && knot_dname_in(query->zone_cut.name, rr->owner)) {
90 return true;
91 }
92 }
93
94 #ifndef STRICT_MODE
95 /* Last resort to work around broken auths, if the zone cut is at/parent of the QNAME. */
96 if (knot_dname_is_equal(query->zone_cut.name, knot_pkt_qname(answer))) {
97 return true;
98 }
99 #endif
100 return false;
101 }
102
103 int kr_response_classify(knot_pkt_t *pkt)
104 {
105 const knot_pktsection_t *an = knot_pkt_section(pkt, KNOT_ANSWER);
106 switch (knot_wire_get_rcode(pkt->wire)) {
107 case KNOT_RCODE_NOERROR:
108 return (an->count == 0) ? PKT_NODATA : PKT_NOERROR;
109 case KNOT_RCODE_NXDOMAIN:
110 return PKT_NXDOMAIN;
111 case KNOT_RCODE_REFUSED:
112 return PKT_REFUSED;
113 default:
114 return PKT_ERROR;
115 }
116 }
117
118 /** @internal Filter ANY or loopback addresses. */
119 static bool is_valid_addr(const uint8_t *addr, size_t len)
120 {
121 if (len == sizeof(struct in_addr)) {
122 /* Filter ANY and 127.0.0.0/8 */
123 uint32_t ip_host = ntohl(*(const uint32_t *)(addr));
124 if (ip_host == 0 || (ip_host & 0xff000000) == 0x7f000000) {
125 return false;
126 }
127 } else if (len == sizeof(struct in6_addr)) {
128 struct in6_addr ip6_mask;
129 memset(&ip6_mask, 0, sizeof(ip6_mask));
130 /* All except last byte are zeroed, last byte defines ANY/::1 */
131 if (memcmp(addr, ip6_mask.s6_addr, sizeof(ip6_mask.s6_addr) - 1) == 0) {
132 return (addr[len - 1] > 1);
133 }
134 }
135 return true;
136 }
137
138 static int update_nsaddr(const knot_rrset_t *rr, struct kr_query *query)
139 {
140 if (rr->type == KNOT_RRTYPE_A || rr->type == KNOT_RRTYPE_AAAA) {
141 const knot_rdata_t *rdata = rr->rrs.data;
142 if (!(query->flags & QUERY_ALLOW_LOCAL) &&
143 !is_valid_addr(knot_rdata_data(rdata), knot_rdata_rdlen(rdata))) {
144 return KNOT_STATE_CONSUME; /* Ignore invalid addresses */
145 }
146 int ret = kr_zonecut_add(&query->zone_cut, rr->owner, rdata);
147 if (ret != 0) {
148 return KNOT_STATE_FAIL;
149 }
150 }
151
152 return KNOT_STATE_CONSUME;
153 }
154
155 static int update_parent(const knot_rrset_t *rr, struct kr_query *qry)
156 {
157 return update_nsaddr(rr, qry->parent);
158 }
159
160 static int update_answer(const knot_rrset_t *rr, unsigned hint, knot_pkt_t *answer)
161 {
162 /* Scrub DNSSEC records when not requested. */
163 if (!knot_pkt_has_dnssec(answer)) {
164 if (rr->type != knot_pkt_qtype(answer) && knot_rrtype_is_dnssec(rr->type)) {
165 return KNOT_STATE_DONE; /* Scrub */
166 }
167 }
168 /* Copy record, as it may be accessed after packet processing. */
169 knot_rrset_t *copy = knot_rrset_copy(rr, &answer->mm);
170 /* Write to final answer. */
171 int ret = knot_pkt_put(answer, hint, copy, KNOT_PF_FREE);
172 if (ret != KNOT_EOK) {
173 knot_wire_set_tc(answer->wire);
174 return KNOT_STATE_DONE;
175 }
176
177 return KNOT_STATE_DONE;
178 }
179
180 static void fetch_glue(knot_pkt_t *pkt, const knot_dname_t *ns, struct kr_request *req)
181 {
182 bool used_glue = false;
183 for (knot_section_t i = KNOT_ANSWER; i <= KNOT_ADDITIONAL; ++i) {
184 const knot_pktsection_t *sec = knot_pkt_section(pkt, i);
185 for (unsigned k = 0; k < sec->count; ++k) {
186 const knot_rrset_t *rr = knot_pkt_rr(sec, k);
187 if (knot_dname_is_equal(ns, rr->owner)) {
188 (void) update_nsaddr(rr, req->current_query);
189 used_glue = true;
190 }
191 }
192 }
193 WITH_DEBUG {
194 char name_str[KNOT_DNAME_MAXLEN];
195 knot_dname_to_str(name_str, ns, sizeof(name_str));
196 if (used_glue) {
197 DEBUG_MSG("<= using glue for '%s'\n", name_str);
198 }
199 }
200 }
201
202 /** Attempt to find glue for given nameserver name (best effort). */
203 static int has_glue(knot_pkt_t *pkt, const knot_dname_t *ns)
204 {
205 for (knot_section_t i = KNOT_ANSWER; i <= KNOT_ADDITIONAL; ++i) {
206 const knot_pktsection_t *sec = knot_pkt_section(pkt, i);
207 for (unsigned k = 0; k < sec->count; ++k) {
208 const knot_rrset_t *rr = knot_pkt_rr(sec, k);
209 if (knot_dname_is_equal(ns, rr->owner) &&
210 (rr->type == KNOT_RRTYPE_A || rr->type == KNOT_RRTYPE_AAAA)) {
211 return 1;
212 }
213 }
214 }
215 return 0;
216 }
217
218 static int update_cut(knot_pkt_t *pkt, const knot_rrset_t *rr, struct kr_request *req)
219 {
220 struct kr_query *qry = req->current_query;
221 struct kr_zonecut *cut = &qry->zone_cut;
222 int state = KNOT_STATE_CONSUME;
223
224 /* Authority MUST be at/below the authority of the nameserver, otherwise
225 * possible cache injection attempt. */
226 if (!knot_dname_in(cut->name, rr->owner)) {
227 DEBUG_MSG("<= authority: ns outside bailiwick\n");
228 #ifdef STRICT_MODE
229 return KNOT_STATE_FAIL;
230 #else
231 /* Workaround: ignore out-of-bailiwick NSs for authoritative answers,
232 * but fail for referrals. This is important to detect lame answers. */
233 if (knot_pkt_section(pkt, KNOT_ANSWER)->count == 0) {
234 state = KNOT_STATE_FAIL;
235 }
236 return state;
237 #endif
238 }
239
240 /* Remember current bailiwick for NS processing. */
241 const knot_dname_t *current_cut = cut->name;
242 /* Update zone cut name */
243 DEBUG_MSG("Update zone cut %s\n", cut->name);
244 if (!knot_dname_is_equal(rr->owner, cut->name)) {
245 /* Remember parent cut and descend to new (keep keys and TA). */
246 struct kr_zonecut *parent = mm_alloc(&req->pool, sizeof(*parent));
247 if (parent) {
248 memcpy(parent, cut, sizeof(*parent));
249 kr_zonecut_init(cut, rr->owner, &req->pool);
250 cut->key = parent->key;
251 cut->trust_anchor = parent->trust_anchor;
252 cut->parent = parent;
253 } else {
254 kr_zonecut_set(cut, rr->owner);
255 }
256 state = KNOT_STATE_DONE;
257 }
258
259 /* Fetch glue for each NS */
260 for (unsigned i = 0; i < rr->rrs.rr_count; ++i) {
261 const knot_dname_t *ns_name = knot_ns_name(&rr->rrs, i);
262 int glue_records = has_glue(pkt, ns_name);
263 /* Glue is mandatory for NS below zone */
264 if (!glue_records && knot_dname_in(rr->owner, ns_name)) {
265 DEBUG_MSG("<= authority: missing mandatory glue, rejecting\n");
266 continue;
267 }
268 kr_zonecut_add(cut, ns_name, NULL);
269 DEBUG_MSG("zonecut_add %s NS %s\n", cut->name, ns_name);
270 /* Choose when to use glue records. */
271 if (qry->flags & QUERY_PERMISSIVE) {
272 fetch_glue(pkt, ns_name, req);
273 } else if (qry->flags & QUERY_STRICT) {
274 /* Strict mode uses only mandatory glue. */
275 if (knot_dname_in(cut->name, ns_name))
276 fetch_glue(pkt, ns_name, req);
277 } else {
278 /* Normal mode uses in-bailiwick glue. */
279 if (knot_dname_in(current_cut, ns_name))
280 fetch_glue(pkt, ns_name, req);
281 }
282 }
283
284 return state;
285 }
286
287 static int process_authority(knot_pkt_t *pkt, struct kr_request *req)
288 {
289 int result = KNOT_STATE_CONSUME;
290 struct kr_query *qry = req->current_query;
291 const knot_pktsection_t *ns = knot_pkt_section(pkt, KNOT_AUTHORITY);
292
293 /* Stub resolution doesn't process authority */
294 if (qry->flags & QUERY_STUB) {
295 return KNOT_STATE_CONSUME;
296 }
297
298 #ifdef STRICT_MODE
299 /* AA, terminate resolution chain. */
300 if (knot_wire_get_aa(pkt->wire)) {
301 DEBUG_MSG("AA terminate resolution chain\n");
302 return KNOT_STATE_CONSUME;
303 }
304 #else
305 /* Work around servers sending back CNAME with different delegation and no AA. */
306 const knot_pktsection_t *an = knot_pkt_section(pkt, KNOT_ANSWER);
307 if (an->count > 0 && ns->count > 0) {
308 const knot_rrset_t *rr = knot_pkt_rr(an, 0);
309 if (rr->type == KNOT_RRTYPE_CNAME) {
310 return KNOT_STATE_CONSUME;
311 }
312 }
313 #endif
314 }
315 /* --------------------------------------------------------- */
316 static int process_referral(knot_pkt_t *pkt, struct kr_request *req) {
317 int result = KNOT_STATE_CONSUME;
318 struct kr_query *qry = req->current_query;
319 DEBUG_MSG("process_referral\n");
320 const knot_pktsection_t *ns = knot_pkt_section(pkt, KNOT_AUTHORITY);
321 DEBUG_MSG("process_referral0\n");
322 if (ns->count == 0) return result;
323
324 DEBUG_MSG("process_referral 1\n");
325 const knot_rrset_t *rr0 = knot_pkt_rr(ns, 0);
326 /* bug? useless SOA handling */
327 /* SOA below cut in authority indicates different authority, but same NS set. */
328 if (rr0->type == KNOT_RRTYPE_SOA) {
329 if (knot_dname_is_sub(rr0->owner, qry->zone_cut.name)) {
330 DEBUG_MSG("SOA update cut.name %s\n", rr0->owner);
331 qry->zone_cut.name = knot_dname_copy(rr0->owner, &req->pool);
332 }
333 return result;
334 }
335
336 DEBUG_MSG("process_referral 2\n");
337 if (rr0->type != KNOT_RRTYPE_NS) return result;
338 DEBUG_MSG("process_referral 3\n");
339 for (unsigned i=0; i < ns->count; ++i) {
340 const knot_rrset_t *rr = knot_pkt_rr(ns, i);
341 if (rr->type != KNOT_RRTYPE_NS ||
342 rr->owner != rr0->owner) continue;
343 int state = update_cut(pkt, rr, req);
344 switch(state) {
345 case KNOT_STATE_DONE: result = state; break;
346 case KNOT_STATE_FAIL: return state; break;
347 default: /* continue */ break;
348 }
349 }
350
351 /* CONSUME => Unhelpful referral.
352 * DONE => Zone cut updated. */
353 return result;
354 }
355
356 static void finalize_answer(knot_pkt_t *pkt, struct kr_query *qry, struct kr_request *req)
357 {
358 /* Finalize header */
359 knot_pkt_t *answer = req->answer;
360 knot_wire_set_rcode(answer->wire, knot_wire_get_rcode(pkt->wire));
361
362 /* Fill in bailiwick records in authority */
363 const bool scrub_dnssec = !knot_pkt_has_dnssec(answer);
364 const uint16_t qtype = knot_pkt_qtype(answer);
365 struct kr_zonecut *cut = &qry->zone_cut;
366 int pkt_class = kr_response_classify(pkt);
367 if ((pkt_class & (PKT_NXDOMAIN|PKT_NODATA))) {
368 const knot_pktsection_t *ns = knot_pkt_section(pkt, KNOT_AUTHORITY);
369 for (unsigned i = 0; i < ns->count; ++i) {
370 DEBUG_MSG("<= finalize NXD/NODATA\n");
371 const knot_rrset_t *rr = knot_pkt_rr(ns, i);
372 /* Scrub DNSSEC records when not requested. */
373 if (scrub_dnssec && rr->type != qtype && knot_rrtype_is_dnssec(rr->type)) {
374 continue;
375 }
376 /* Stash the authority records, they will be written to wire on answer finalization. */
377 if (knot_dname_in(cut->name, rr->owner)) {
378 kr_rrarray_add(&req->authority, rr, &answer->mm);
379 DEBUG_MSG("<= finalize rrarray add\n");
380 }
381 }
382 }
383 }
384
385 static int process_answer(knot_pkt_t *pkt, struct kr_request *req)
386 {
387 struct kr_query *query = req->current_query;
388 /* Response for minimized QNAME.
389 * NODATA => may be empty non-terminal, retry (found zone cut)
390 * NOERROR => found zone cut, retry
391 * NXDOMAIN => parent is zone cut, retry as a workaround for bad authoritatives
392 */
393 bool is_final = (query->parent == NULL);
394 int pkt_class = kr_response_classify(pkt);
395 if (!knot_dname_is_equal(knot_pkt_qname(pkt), query->sname) &&
396 (pkt_class & (PKT_NOERROR|PKT_NXDOMAIN|PKT_REFUSED|PKT_NODATA))) {
397 DEBUG_MSG("<= found cut, retrying with non-minimized name\n");
398 query->flags |= QUERY_NO_MINIMIZE;
399 return KNOT_STATE_CONSUME;
400 }
401
402 /* This answer didn't improve resolution chain, therefore must be authoritative (relaxed to negative). */
403 if (!(query->flags & QUERY_STUB) && !is_authoritative(pkt, query)) {
404 if (pkt_class & (PKT_NXDOMAIN|PKT_NODATA)) {
405 DEBUG_MSG("<= lame response: non-auth sent negative response\n");
406 return KNOT_STATE_FAIL;
407 }
408 }
409
410 /* Process answer type */
411 const knot_pktsection_t *an = knot_pkt_section(pkt, KNOT_ANSWER);
412 const knot_dname_t *cname = NULL;
413 const knot_dname_t *pending_cname = query->sname;
414 unsigned cname_chain_len = 0;
415 while (pending_cname) {
416 /* CNAME was found at previous iteration, but records may not follow the correct order.
417 * Try to find records for pending_cname owner from section start. */
418 cname = pending_cname;
419 pending_cname = NULL;
420 for (unsigned i = 0; i < an->count; ++i) {
421 const knot_rrset_t *rr = knot_pkt_rr(an, i);
422 if (!knot_dname_is_equal(rr->owner, cname)) {
423 continue;
424 }
425 /* Process records matching current SNAME */
426 unsigned hint = 0;
427 if(knot_dname_is_equal(cname, knot_pkt_qname(req->answer))) {
428 hint = KNOT_COMPR_HINT_QNAME;
429 }
430 int state = is_final ? update_answer(rr, hint, req->answer) : update_parent(rr, query);
431 if (state == KNOT_STATE_FAIL) {
432 return state;
433 }
434 /* Jump to next CNAME target */
435 if ((query->stype == KNOT_RRTYPE_CNAME) || (rr->type != KNOT_RRTYPE_CNAME)) {
436 continue;
437 }
438 cname_chain_len += 1;
439 pending_cname = knot_cname_name(&rr->rrs);
440 if (!pending_cname) {
441 break;
442 }
443 if (cname_chain_len > an->count || cname_chain_len > KR_CNAME_CHAIN_LIMIT) {
444 DEBUG_MSG("<= too long cname chain\n");
445 return KNOT_STATE_FAIL;
446 }
447 /* If secure, don't use pending_cname immediately.
448 * There are can be RRSIG for "old" cname.
449 */
450 if (query->flags & QUERY_DNSSEC_WANT) {
451 /* Follow chain only within current cut (if secure). */
452 if (pending_cname && !knot_dname_in(query->zone_cut.name, pending_cname)) {
453 pending_cname = NULL;
454 }
455 } else {
456 /* Try to find next cname */
457 cname = pending_cname;
458 }
459 }
460 }
461
462 /* Make sure that this is an authoritative answer (even with AA=0) for other layers */
463 knot_wire_set_aa(pkt->wire);
464 /* Either way it resolves current query. */
465 query->flags |= QUERY_RESOLVED;
466 /* Follow canonical name as next SNAME. */
467 if (!knot_dname_is_equal(cname, query->sname)) {
468 /* Check if target record has been already copied */
469 if (is_final) {
470 const knot_pktsection_t *an = knot_pkt_section(req->answer, KNOT_ANSWER);
471 for (unsigned i = 0; i < an->count; ++i) {
472 const knot_rrset_t *rr = knot_pkt_rr(an, i);
473 if (!knot_dname_is_equal(rr->owner, cname)) {
474 continue;
475 }
476 if ((rr->rclass != query->sclass) ||
477 (rr->type != query->stype)) {
478 continue;
479 }
480 finalize_answer(pkt, query, req);
481 return KNOT_STATE_DONE;
482 }
483 }
484 DEBUG_MSG("<= cname chain, following\n");
485 /* Check if the same query was already resolved */
486 for (int i = 0; i < req->rplan.resolved.len; ++i) {
487 struct kr_query * q = req->rplan.resolved.at[i];
488 if (q->sclass == query->sclass &&
489 q->stype == query->stype &&
490 knot_dname_is_equal(q->sname, cname)) {
491 DEBUG_MSG("<= cname chain loop\n");
492 return KNOT_STATE_FAIL;
493 }
494 }
495 struct kr_query *next = kr_rplan_push(&req->rplan, query->parent, cname, query->sclass, query->stype);
496 if (!next) {
497 return KNOT_STATE_FAIL;
498 }
499 next->flags |= QUERY_AWAIT_CUT;
500 /* Want DNSSEC if it's posible to secure this name (e.g. is covered by any TA) */
501 if (kr_ta_covers(&req->ctx->trust_anchors, cname) &&
502 !kr_ta_covers(&req->ctx->negative_anchors, cname)) {
503 next->flags |= QUERY_DNSSEC_WANT;
504 }
505 } else if (!query->parent) {
506 finalize_answer(pkt, query, req);
507 }
508 return KNOT_STATE_DONE;
509 }
510
511 /** Error handling, RFC1034 5.3.3, 4d. */
512 static int resolve_error(knot_pkt_t *pkt, struct kr_request *req)
513 {
514 return KNOT_STATE_FAIL;
515 }
516
517 /* State-less single resolution iteration step, not needed. */
518 static int reset(knot_layer_t *ctx) { return KNOT_STATE_PRODUCE; }
519
520 /* Set resolution context and parameters. */
521 static int begin(knot_layer_t *ctx, void *module_param)
522 {
523 if (ctx->state & (KNOT_STATE_DONE|KNOT_STATE_FAIL)) {
524 return ctx->state;
525 }
526 return reset(ctx);
527 }
528
529 int kr_make_query(struct kr_query *query, knot_pkt_t *pkt)
530 {
531 /* Minimize QNAME (if possible). */
532 uint16_t qtype = query->stype;
533 const knot_dname_t *qname = minimized_qname(query, &qtype);
534
535 /* Form a query for the authoritative. */
536 knot_pkt_clear(pkt);
537 int ret = knot_pkt_put_question(pkt, qname, query->sclass, qtype);
538 if (ret != KNOT_EOK) {
539 return ret;
540 }
541
542 /* Query built, expect answer. */
543 query->id = kr_rand_uint(UINT16_MAX);
544 knot_wire_set_id(pkt->wire, query->id);
545 pkt->parsed = pkt->size;
546 return kr_ok();
547 }
548
549 static int prepare_query(knot_layer_t *ctx, knot_pkt_t *pkt)
550 {
551 assert(pkt && ctx);
552 struct kr_request *req = ctx->data;
553 struct kr_query *query = req->current_query;
554 if (!query || ctx->state & (KNOT_STATE_DONE|KNOT_STATE_FAIL)) {
555 return ctx->state;
556 }
557
558 /* Make query */
559 int ret = kr_make_query(query, pkt);
560 if (ret != 0) {
561 return KNOT_STATE_FAIL;
562 }
563
564 return KNOT_STATE_CONSUME;
565 }
566
567 static int resolve_badmsg(knot_pkt_t *pkt, struct kr_request *req, struct kr_query *query)
568 {
569 #ifndef STRICT_MODE
570 /* Work around broken auths/load balancers */
571 if (query->flags & QUERY_SAFEMODE) {
572 return resolve_error(pkt, req);
573 } else {
574 query->flags |= QUERY_SAFEMODE;
575 return KNOT_STATE_DONE;
576 }
577 #else
578 return resolve_error(pkt, req);
579 #endif
580 }
581
582 /** Resolve input query or continue resolution with followups.
583 *
584 * This roughly corresponds to RFC1034, 5.3.3 4a-d.
585 */
586 static int resolve(knot_layer_t *ctx, knot_pkt_t *pkt)
587 {
588 assert(pkt && ctx);
589 struct kr_request *req = ctx->data;
590 struct kr_query *query = req->current_query;
591 if (!query || (query->flags & QUERY_RESOLVED)) {
592 return ctx->state;
593 }
594 DEBUG_MSG("start \n");
595 /* Check for packet processing errors first.
596 * Note - we *MUST* check if it has at least a QUESTION,
597 * otherwise it would crash on accessing QNAME. */
598 if (pkt->parsed < pkt->size || pkt->parsed <= KNOT_WIRE_HEADER_SIZE) {
599 DEBUG_MSG("<= malformed response\n");
600 return resolve_badmsg(pkt, req, query);
601 } else if (!is_paired_to_query(pkt, query)) {
602 DEBUG_MSG("<= ignoring mismatching response\n");
603 /* Force TCP, to work around authoritatives messing up question
604 * without yielding to spoofed responses. */
605 query->flags |= QUERY_TCP;
606 return resolve_badmsg(pkt, req, query);
607 } else if (knot_wire_get_tc(pkt->wire)) {
608 DEBUG_MSG("<= truncated response, failover to TCP\n");
609 if (query) {
610 /* Fail if already on TCP. */
611 if (query->flags & QUERY_TCP) {
612 DEBUG_MSG("<= TC=1 with TCP, bailing out\n");
613 return resolve_error(pkt, req);
614 }
615 query->flags |= QUERY_TCP;
616 }
617 return KNOT_STATE_CONSUME;
618 }
619
620 #ifndef NDEBUG
621 const knot_lookup_t *rcode = knot_lookup_by_id(knot_rcode_names, knot_wire_get_rcode(pkt->wire));
622 #endif
623
624 /* Check response code. */
625 DEBUG_MSG("<= rcode: %s\n", rcode ? rcode->name : "??");
626 switch(knot_wire_get_rcode(pkt->wire)) {
627 case KNOT_RCODE_NOERROR:
628 case KNOT_RCODE_NXDOMAIN:
629 case KNOT_RCODE_REFUSED:
630 break; /* OK */
631 case KNOT_RCODE_FORMERR:
632 case KNOT_RCODE_NOTIMPL:
633 return resolve_badmsg(pkt, req, query);
634 default:
635 return resolve_error(pkt, req);
636 }
637
638 /* Resolve authority to see if it's referral or authoritative. */
639 struct kr_query *qry = req->current_query;
640
641 /* Stub resolution doesn't process authority */
642 if (qry->flags & QUERY_STUB) {
643 return KNOT_STATE_CONSUME;
644 }
645
646 if (knot_wire_get_aa(pkt->wire)) {
647 DEBUG_MSG("AA terminate resolution chain\n");
648 /* Not referral, process answer. */
649 return process_answer(pkt, req);
650 }
651 /* Referral */ DEBUG_MSG("<= referral response, follow\n");
652 return process_referral(pkt, req);
653 }
654
655 /** Module implementation. */
656 const knot_layer_api_t *iterate_layer(struct kr_module *module)
657 {
658 static const knot_layer_api_t _layer = {
659 .begin = &begin,
660 .reset = &reset,
661 .consume = &resolve,
662 .produce = &prepare_query
663 };
664 return &_layer;
665 }
666
667 KR_MODULE_EXPORT(iterate)
668
669 #undef DEBUG_MSG
670
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.