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.
  • [get | view] (2020-08-12 06:11:32, 20.7 KB) [[attachment:iterate.c]]
 All files | Selected Files: delete move to page copy to page

You are not allowed to attach a file to this page.