Attachment 'resolve.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 <ctype.h>
  18 #include <stdio.h>
  19 #include <fcntl.h>
  20 #include <libknot/rrtype/rdname.h>
  21 #include <libknot/descriptor.h>
  22 #include <ucw/mempool.h>
  23 #include "lib/resolve.h"
  24 #include "lib/layer.h"
  25 #include "lib/rplan.h"
  26 #include "lib/layer/iterate.h"
  27 #include "lib/dnssec/ta.h"
  28 
  29 #define DEBUG_MSG(qry, fmt...) QRDEBUG((qry), "resl",  fmt)
  30 
  31 /** @internal Macro for iterating module layers. */
  32 #define ITERATE_LAYERS(req, qry, func, ...) \
  33     (req)->current_query = (qry); \
  34 	for (unsigned i = 0; i < (req)->ctx->modules->len; ++i) { \
  35 		struct kr_module *mod = (req)->ctx->modules->at[i]; \
  36 		if (mod->layer) { \
  37 			struct knot_layer layer = {.state = (req)->state, .api = mod->layer(mod), .data = (req)}; \
  38 			if (layer.api && layer.api->func) { \
  39 				(req)->state = layer.api->func(&layer, ##__VA_ARGS__); \
  40 				if ((req)->state == KNOT_STATE_YIELD) \
  41 					break; \
  42 			} \
  43 		} \
  44 	} /* Invalidate current query. */ \
  45 	(req)->current_query = NULL
  46 
  47 /* Randomize QNAME letter case.
  48  * This adds 32 bits of randomness at maximum, but that's more than an average domain name length.
  49  * https://tools.ietf.org/html/draft-vixie-dnsext-dns0x20-00
  50  */
  51 static void randomized_qname_case(knot_dname_t *qname, uint32_t secret)
  52 {
  53 	unsigned k = 0;
  54 	while (*qname != '\0') {
  55 		for (unsigned i = *qname; i--;) {
  56 			int chr = qname[i + 1];
  57 			if (isalpha(chr)) {
  58 				if (secret & (1 << k)) {
  59 					qname[i + 1] ^= 0x20;
  60 				}
  61 				k = (k + 1) % (sizeof(secret) * CHAR_BIT);
  62 			}
  63 		}
  64 		qname = (uint8_t *)knot_wire_next_label(qname, NULL);
  65 	}
  66 }
  67 
  68 /** Invalidate current NS/addr pair. */
  69 static int invalidate_ns(struct kr_rplan *rplan, struct kr_query *qry)
  70 {
  71 	if (qry->ns.addr[0].ip.sa_family != AF_UNSPEC) {
  72 		uint8_t *addr = kr_nsrep_inaddr(qry->ns.addr[0]);
  73 		size_t addr_len = kr_nsrep_inaddr_len(qry->ns.addr[0]);
  74 		knot_rdata_t rdata[knot_rdata_array_size(addr_len)];
  75 		knot_rdata_init(rdata, addr_len, addr, 0);
  76 		return kr_zonecut_del(&qry->zone_cut, qry->ns.name, rdata);
  77 	} else {
  78 		return kr_zonecut_del(&qry->zone_cut, qry->ns.name, NULL);
  79 	}
  80 }
  81 
  82 static int ns_fetch_cut(struct kr_query *qry, struct kr_request *req, bool secured)
  83 {
  84 	int ret = 0;
  85 
  86 	/* Find closest zone cut from cache */
  87 	struct kr_cache_txn txn;
  88 	if (kr_cache_txn_begin(&req->ctx->cache, &txn, NAMEDB_RDONLY) == 0) {
  89 		/* If at/subdomain of parent zone cut, start from its encloser.
  90 		 * This is for case when we get to a dead end (and need glue from parent), or DS refetch. */
  91 		struct kr_query *parent = qry->parent;
  92 		if (parent && qry->sname[0] != '\0' && knot_dname_in(parent->zone_cut.name, qry->sname)) {
  93 			const knot_dname_t *encloser = knot_wire_next_label(parent->zone_cut.name, NULL);
  94 			ret = kr_zonecut_find_cached(req->ctx, &qry->zone_cut, encloser, &txn, qry->timestamp.tv_sec, secured);
  95 		} else {
  96 			ret = kr_zonecut_find_cached(req->ctx, &qry->zone_cut, qry->sname, &txn, qry->timestamp.tv_sec, secured);
  97 		}
  98 		kr_cache_txn_abort(&txn);
  99 	} else {
 100 		ret = kr_error(ENOENT);
 101 	}
 102 	return ret;
 103 }
 104 
 105 static int ns_resolve_addr(struct kr_query *qry, struct kr_request *param)
 106 {
 107 	struct kr_rplan *rplan = &param->rplan;
 108 	struct kr_context *ctx = param->ctx;
 109 
 110 
 111 	/* Start NS queries from root, to avoid certain cases
 112 	 * where a NS drops out of cache and the rest is unavailable,
 113 	 * this would lead to dependency loop in current zone cut.
 114 	 * Prefer IPv6 and continue with IPv4 if not available.
 115 	 */
 116 	uint16_t next_type = 0;
 117 	if (!(qry->flags & QUERY_AWAIT_IPV6)) {
 118 		next_type = KNOT_RRTYPE_AAAA;
 119 		qry->flags |= QUERY_AWAIT_IPV6;
 120 	} else if (!(qry->flags & QUERY_AWAIT_IPV4)) {
 121 		next_type = KNOT_RRTYPE_A;
 122 		qry->flags |= QUERY_AWAIT_IPV4;
 123 		/* Hmm, no useable IPv6 then. */
 124 		qry->ns.reputation |= KR_NS_NOIP6;
 125 		kr_nsrep_update_rep(&qry->ns, qry->ns.reputation, ctx->cache_rep);
 126 	}
 127 	/* Bail out if the query is already pending or dependency loop. */
 128 	if (!next_type || kr_rplan_satisfies(qry->parent, qry->ns.name, KNOT_CLASS_IN, next_type)) {
 129 		/* Fall back to SBELT if root server query fails. */
 130 		if (!next_type && qry->zone_cut.name[0] == '\0') {
 131 			DEBUG_MSG(qry, "=> fallback to root hints\n");
 132 			kr_zonecut_set_sbelt(ctx, &qry->zone_cut);
 133 			qry->flags |= QUERY_NO_THROTTLE; /* Pick even bad SBELT servers */
 134 			return kr_error(EAGAIN);
 135 		}
 136 		/* No IPv4 nor IPv6, flag server as unuseable. */
 137 		DEBUG_MSG(qry, "=> unresolvable NS address, bailing out\n");
 138 		qry->ns.reputation |= KR_NS_NOIP4 | KR_NS_NOIP6;
 139 		kr_nsrep_update_rep(&qry->ns, qry->ns.reputation, ctx->cache_rep);
 140 		invalidate_ns(rplan, qry);
 141 		return kr_error(EHOSTUNREACH);
 142 	}
 143 	/* Push new query to the resolution plan */
 144 	struct kr_query *next = kr_rplan_push(rplan, qry, qry->ns.name, KNOT_CLASS_IN, next_type);
 145 	if (!next) {
 146 		return kr_error(ENOMEM);
 147 	}
 148 	/* At the root level with no NS addresses, add SBELT subrequest. */
 149 	int ret = 0;
 150 	if (qry->zone_cut.name[0] == '\0') {
 151 		ret = kr_zonecut_set_sbelt(ctx, &next->zone_cut);
 152 		if (ret == 0) { /* Copy TA and key since it's the same cut to avoid lookup. */
 153 			kr_zonecut_copy_trust(&next->zone_cut, &qry->zone_cut);
 154 			kr_zonecut_set_sbelt(ctx, &qry->zone_cut); /* Add SBELT to parent in case query fails. */
 155 			qry->flags |= QUERY_NO_THROTTLE; /* Pick even bad SBELT servers */
 156 		}
 157 	} else {
 158 		next->flags |= QUERY_AWAIT_CUT;
 159 	}
 160 	return ret;
 161 }
 162 
 163 static int edns_put(knot_pkt_t *pkt)
 164 {
 165 	if (!pkt->opt_rr) {
 166 		return kr_ok();
 167 	}
 168 	/* Reclaim reserved size. */
 169 	int ret = knot_pkt_reclaim(pkt, knot_edns_wire_size(pkt->opt_rr));
 170 	if (ret != 0) {
 171 		return ret;
 172 	}
 173 	/* Write to packet. */
 174 	assert(pkt->current == KNOT_ADDITIONAL);
 175 	return knot_pkt_put(pkt, KNOT_COMPR_HINT_NONE, pkt->opt_rr, KNOT_PF_FREE);
 176 }
 177 
 178 static int edns_create(knot_pkt_t *pkt, knot_pkt_t *template, struct kr_request *req)
 179 {
 180 	pkt->opt_rr = knot_rrset_copy(req->ctx->opt_rr, &pkt->mm);
 181 	return knot_pkt_reserve(pkt, knot_edns_wire_size(pkt->opt_rr));
 182 }
 183 
 184 static int answer_prepare(knot_pkt_t *answer, knot_pkt_t *query, struct kr_request *req)
 185 {
 186 	if (knot_pkt_init_response(answer, query) != 0) {
 187 		return kr_error(ENOMEM); /* Failed to initialize answer */
 188 	}
 189 	/* Handle EDNS in the query */
 190 	if (knot_pkt_has_edns(query)) {
 191 		int ret = edns_create(answer, query, req);
 192 		if (ret != 0){
 193 			return ret;
 194 		}
 195 		/* Set DO bit if set (DNSSEC requested). */
 196 		if (knot_pkt_has_dnssec(query)) {
 197 			knot_edns_set_do(answer->opt_rr);
 198 		}
 199 	}
 200 	return kr_ok();
 201 }
 202 
 203 static int answer_finalize(struct kr_request *request, int state)
 204 {
 205 	/* Write EDNS information */
 206 	knot_pkt_t *answer = request->answer;
 207 	knot_pkt_begin(answer, KNOT_ADDITIONAL);
 208 	if (answer->opt_rr) {
 209 		int ret = edns_put(answer);
 210 		if (ret != 0) {
 211 			return ret;
 212 		}
 213 	}
 214 	/* Set AD=1 if succeeded and requested secured answer. */
 215 	struct kr_rplan *rplan = &request->rplan;
 216 	if (state == KNOT_STATE_DONE && !EMPTY_LIST(rplan->resolved)) {
 217 		struct kr_query *last = TAIL(rplan->resolved);
 218 		/* Do not set AD for RRSIG query, as we can't validate it. */
 219 		if ((last->flags & QUERY_DNSSEC_WANT) && knot_pkt_has_dnssec(answer) &&
 220 			knot_pkt_qtype(answer) != KNOT_RRTYPE_RRSIG) {
 221 			knot_wire_set_ad(answer->wire);
 222 		}
 223 	}
 224 	return kr_ok();
 225 }
 226 
 227 static int query_finalize(struct kr_request *request, struct kr_query *qry, knot_pkt_t *pkt)
 228 {
 229 	/* Randomize query case (if not in safemode) */
 230 	qry->secret = (qry->flags & QUERY_SAFEMODE) ? 0 : kr_rand_uint(UINT32_MAX);
 231 	knot_dname_t *qname_raw = (knot_dname_t *)knot_pkt_qname(pkt);
 232 	randomized_qname_case(qname_raw, qry->secret);
 233 
 234 	int ret = 0;
 235 	knot_pkt_begin(pkt, KNOT_ADDITIONAL);
 236 	if (!(qry->flags & QUERY_SAFEMODE)) {
 237 		ret = edns_create(pkt, request->answer, request);
 238 		if (ret == 0) { /* Enable DNSSEC for query. */
 239 			if (qry->flags & QUERY_DNSSEC_WANT) {
 240 				knot_edns_set_do(pkt->opt_rr);
 241 				knot_wire_set_cd(pkt->wire);
 242 			}
 243 			ret = edns_put(pkt);
 244 		}
 245 	}
 246 	return ret;
 247 }
 248 
 249 int kr_resolve_begin(struct kr_request *request, struct kr_context *ctx, knot_pkt_t *answer)
 250 {
 251 	/* Initialize request */
 252 	request->ctx = ctx;
 253 	request->answer = answer;
 254 	request->options = ctx->options;
 255 	request->state = KNOT_STATE_CONSUME;
 256 	request->current_query = NULL;
 257 
 258 	/* Expect first query */
 259 	kr_rplan_init(&request->rplan, request, &request->pool);
 260 	return KNOT_STATE_CONSUME;
 261 }
 262 
 263 static int resolve_query(struct kr_request *request, const knot_pkt_t *packet)
 264 {
 265 	struct kr_rplan *rplan = &request->rplan;
 266 	const knot_dname_t *qname = knot_pkt_qname(packet);
 267 	uint16_t qclass = knot_pkt_qclass(packet);
 268 	uint16_t qtype = knot_pkt_qtype(packet);
 269 	struct kr_query *qry = kr_rplan_push(rplan, NULL, qname, qclass, qtype);
 270 	if (!qry) {
 271 		return KNOT_STATE_FAIL;
 272 	}
 273 
 274 	/* Deferred zone cut lookup for this query. */
 275 	qry->flags |= QUERY_AWAIT_CUT;
 276 	/* Want DNSSEC if it's posible to secure this name (e.g. is covered by any TA) */
 277 	map_t *negative_anchors = &request->ctx->negative_anchors;
 278 	map_t *trust_anchors = &request->ctx->trust_anchors;
 279 	if (knot_pkt_has_dnssec(packet) &&
 280 	    kr_ta_covers(trust_anchors, qname) && !kr_ta_covers(negative_anchors, qname)) {
 281 		qry->flags |= QUERY_DNSSEC_WANT;
 282 	}
 283 
 284 	/* Initialize answer packet */
 285 	knot_pkt_t *answer = request->answer;
 286 	knot_wire_set_qr(answer->wire);
 287 	knot_wire_clear_aa(answer->wire);
 288 	knot_wire_set_ra(answer->wire);
 289 	knot_wire_set_rcode(answer->wire, KNOT_RCODE_NOERROR);
 290 
 291 	/* Expect answer, pop if satisfied immediately */
 292 	ITERATE_LAYERS(request, qry, begin, request);
 293 	if (request->state == KNOT_STATE_DONE) {
 294 		kr_rplan_pop(rplan, qry);
 295 	}
 296 	return request->state;
 297 }
 298 
 299 int kr_resolve_consume(struct kr_request *request, const struct sockaddr *src, knot_pkt_t *packet)
 300 {
 301 	struct kr_rplan *rplan = &request->rplan;
 302 	struct kr_context *ctx = request->ctx;
 303 
 304 	/* Empty resolution plan, push packet as the new query */
 305 	if (packet && kr_rplan_empty(rplan)) {
 306 		if (answer_prepare(request->answer, packet, request) != 0) {
 307 			return KNOT_STATE_FAIL;
 308 		}
 309 		return resolve_query(request, packet);
 310 	}
 311 
 312 	/* Different processing for network error */
 313 	struct kr_query *qry = TAIL(rplan->pending);
 314 	bool tried_tcp = (qry->flags & QUERY_TCP);
 315 	if (!packet || packet->size == 0) {
 316 		/* Network error, retry over TCP. */
 317 		if (!tried_tcp) {
 318 			DEBUG_MSG(qry, "=> NS unreachable, retrying over TCP\n");
 319 			qry->flags |= QUERY_TCP;
 320 			return KNOT_STATE_PRODUCE;
 321 		}
 322 		request->state = KNOT_STATE_FAIL;
 323 	} else {
 324 		/* Packet cleared, derandomize QNAME. */
 325 		knot_dname_t *qname_raw = (knot_dname_t *)knot_pkt_qname(packet);
 326 		if (qname_raw && qry->secret != 0) {
 327 			randomized_qname_case(qname_raw, qry->secret);
 328 		}
 329 		request->state = KNOT_STATE_CONSUME;
 330 		ITERATE_LAYERS(request, qry, consume, packet);
 331 	}
 332 
 333 	/* Resolution failed, invalidate current NS. */
 334 	if (request->state == KNOT_STATE_FAIL) {
 335 		kr_nsrep_update_rtt(&qry->ns, src, KR_NS_TIMEOUT, ctx->cache_rtt);
 336 		invalidate_ns(rplan, qry);
 337 		qry->flags &= ~QUERY_RESOLVED;
 338 	/* Track RTT for iterative answers */
 339 	} else if (!(qry->flags & QUERY_CACHED)) {
 340 		struct timeval now;
 341 		gettimeofday(&now, NULL);
 342 		kr_nsrep_update_rtt(&qry->ns, src, time_diff(&qry->timestamp, &now), ctx->cache_rtt);
 343 		/* Sucessful answer, lift any address resolution requests. */
 344 		qry->flags &= ~(QUERY_AWAIT_IPV6|QUERY_AWAIT_IPV4);
 345 	}
 346 
 347 	/* Pop query if resolved. */
 348 	if (qry->flags & QUERY_RESOLVED) {
 349 		kr_rplan_pop(rplan, qry);
 350 	} else if (!tried_tcp && (qry->flags & QUERY_TCP)) {
 351 		return KNOT_STATE_PRODUCE; /* Requery over TCP */
 352 	} else { /* Clear query flags for next attempt */
 353 		qry->flags &= ~(QUERY_CACHED|QUERY_TCP);
 354 	}
 355 
 356 	ITERATE_LAYERS(request, qry, reset);
 357 
 358 	/* Do not finish with bogus answer. */
 359 	if (qry->flags & QUERY_DNSSEC_BOGUS)  {
 360 		return KNOT_STATE_FAIL;
 361 	}
 362 
 363 	return kr_rplan_empty(&request->rplan) ? KNOT_STATE_DONE : KNOT_STATE_PRODUCE;
 364 }
 365 
 366 /** @internal Spawn subrequest in current zone cut (no minimization or lookup). */
 367 static struct kr_query *zone_cut_subreq(struct kr_rplan *rplan, struct kr_query *parent,
 368                            const knot_dname_t *qname, uint16_t qtype)
 369 {
 370 	struct kr_query *next = kr_rplan_push(rplan, parent, qname, parent->sclass, qtype);
 371 	if (!next) {
 372 		return NULL;
 373 	}
 374 	kr_zonecut_set(&next->zone_cut, parent->zone_cut.name);
 375 	if (kr_zonecut_copy(&next->zone_cut, &parent->zone_cut) != 0 ||
 376 	    kr_zonecut_copy_trust(&next->zone_cut, &parent->zone_cut) != 0) {
 377 		return NULL;
 378 	}
 379 	next->flags |= QUERY_NO_MINIMIZE;
 380 	if (parent->flags & QUERY_DNSSEC_WANT) {
 381 		next->flags |= QUERY_DNSSEC_WANT;
 382 	}
 383 	return next;
 384 }
 385 
 386 /** @internal Check current zone cut status and credibility, spawn subrequests if needed. */
 387 static int zone_cut_check(struct kr_request *request, struct kr_query *qry, knot_pkt_t *packet)
 388 {
 389 	struct kr_rplan *rplan = &request->rplan;
 390 	map_t *trust_anchors = &request->ctx->trust_anchors;
 391 	map_t *negative_anchors = &request->ctx->negative_anchors;
 392 
 393 	/* The query wasn't resolved from cache,
 394 	 * now it's the time to look up closest zone cut from cache. */
 395 	if (qry->flags & QUERY_AWAIT_CUT) {
 396 		/* Want DNSSEC if it's posible to secure this name (e.g. is covered by any TA) */
 397 		if (!kr_ta_covers(negative_anchors, qry->zone_cut.name) &&
 398 		    kr_ta_covers(trust_anchors, qry->zone_cut.name)) {
 399 			qry->flags |= QUERY_DNSSEC_WANT;
 400 		} else {
 401 			qry->flags &= ~QUERY_DNSSEC_WANT;
 402 		}
 403 		int ret = ns_fetch_cut(qry, request, (qry->flags & QUERY_DNSSEC_WANT));
 404 		if (ret != 0) {
 405 			/* No cached cut found, start from SBELT and issue priming query. */
 406 			if (ret == kr_error(ENOENT)) {
 407 				ret = kr_zonecut_set_sbelt(request->ctx, &qry->zone_cut);
 408 				if (ret != 0) {
 409 					return KNOT_STATE_FAIL;
 410 				}
 411 				if (qry->sname[0] != '\0') {
 412 					DEBUG_MSG(qry, "=> root priming query\n");
 413 					zone_cut_subreq(rplan, qry, qry->zone_cut.name, KNOT_RRTYPE_NS);
 414 				} else {
 415 					DEBUG_MSG(qry, "=> using root hints\n");
 416 				}
 417 				qry->flags &= ~QUERY_AWAIT_CUT;
 418 				return KNOT_STATE_DONE;
 419 			} else {
 420 				return KNOT_STATE_FAIL;
 421 			}
 422 		}
 423 		/* Update minimized QNAME if zone cut changed */
 424 		if (qry->zone_cut.name[0] != '\0' && !(qry->flags & QUERY_NO_MINIMIZE)) {
 425 			if (kr_make_query(qry, packet) != 0) {
 426 				return KNOT_STATE_FAIL;
 427 			}
 428 		}
 429 		qry->flags &= ~QUERY_AWAIT_CUT;
 430 	}
 431 	/* Disable DNSSEC if it enters NTA. */
 432 	if (kr_ta_get(negative_anchors, qry->zone_cut.name)){
 433 		DEBUG_MSG(qry, ">< negative TA, going insecure\n");
 434 		qry->flags &= ~QUERY_DNSSEC_WANT;
 435 	}
 436 	/* Enable DNSSEC if enters a new island of trust. */
 437 	bool want_secured = (qry->flags & QUERY_DNSSEC_WANT);
 438 	if (!want_secured && kr_ta_get(trust_anchors, qry->zone_cut.name)) {
 439 		qry->flags |= QUERY_DNSSEC_WANT;
 440 		want_secured = true;
 441 		WITH_DEBUG {
 442 		char qname_str[KNOT_DNAME_MAXLEN];
 443 		knot_dname_to_str(qname_str, qry->zone_cut.name, sizeof(qname_str));
 444 		DEBUG_MSG(qry, ">< TA: '%s'\n", qname_str);
 445 		}
 446 	}
 447 	if (want_secured && !qry->zone_cut.trust_anchor) {
 448 		knot_rrset_t *ta_rr = kr_ta_get(trust_anchors, qry->zone_cut.name);
 449 		qry->zone_cut.trust_anchor = knot_rrset_copy(ta_rr, qry->zone_cut.pool);
 450 	}
 451 	/* Try to fetch missing DS (from above the cut). */
 452 	const bool has_ta = (qry->zone_cut.trust_anchor != NULL);
 453 	const knot_dname_t *ta_name = (has_ta ? qry->zone_cut.trust_anchor->owner : NULL);
 454 	const bool refetch_ta = !has_ta || !knot_dname_is_equal(qry->zone_cut.name, ta_name);
 455 	if (want_secured && refetch_ta) {
 456 		/* @todo we could fetch the information from the parent cut, but we don't remember that now */
 457 		struct kr_query *next = kr_rplan_push(rplan, qry, qry->zone_cut.name, qry->sclass, KNOT_RRTYPE_DS);
 458 		if (!next) {
 459 			return KNOT_STATE_FAIL;
 460 		}
 461 		next->flags |= QUERY_AWAIT_CUT|QUERY_DNSSEC_WANT;
 462 		return KNOT_STATE_DONE;
 463 	}
 464 	/* Try to fetch missing DNSKEY (either missing or above current cut).
 465 	 * Do not fetch if this is a DNSKEY subrequest to avoid circular dependency. */
 466 	const bool is_dnskey_subreq = kr_rplan_satisfies(qry, ta_name, KNOT_CLASS_IN, KNOT_RRTYPE_DNSKEY);
 467 	const bool refetch_key = has_ta && (!qry->zone_cut.key || !knot_dname_is_equal(ta_name, qry->zone_cut.key->owner));
 468 	if (want_secured && refetch_key && !is_dnskey_subreq) {
 469 		struct kr_query *next = zone_cut_subreq(rplan, qry, ta_name, KNOT_RRTYPE_DNSKEY);
 470 		if (!next) {
 471 			return KNOT_STATE_FAIL;
 472 		}
 473 		return KNOT_STATE_DONE;
 474 	}
 475 
 476 	return KNOT_STATE_PRODUCE;	
 477 }
 478 
 479 int kr_resolve_produce(struct kr_request *request, struct sockaddr **dst, int *type, knot_pkt_t *packet)
 480 {
 481 	struct kr_rplan *rplan = &request->rplan;
 482 	unsigned ns_election_iter = 0;
 483 
 484 	/* No query left for resolution */
 485 	if (kr_rplan_empty(rplan)) {
 486 		return KNOT_STATE_FAIL;
 487 	}
 488 
 489 	/* Resolve current query and produce dependent or finish */
 490 	struct kr_query *qry = TAIL(rplan->pending);
 491 	request->state = KNOT_STATE_PRODUCE;
 492 	ITERATE_LAYERS(request, qry, produce, packet);
 493 	if (request->state != KNOT_STATE_FAIL && knot_wire_get_qr(packet->wire)) {
 494 		/* Produced an answer, consume it. */
 495 		qry->secret = 0;
 496 		request->state = KNOT_STATE_CONSUME;
 497 		ITERATE_LAYERS(request, qry, consume, packet);
 498 	}
 499 	switch(request->state) {
 500 	case KNOT_STATE_FAIL: return request->state;
 501 	case KNOT_STATE_CONSUME: break;
 502 	case KNOT_STATE_DONE:
 503 	default: /* Current query is done */
 504 		if (qry->flags & QUERY_RESOLVED) {
 505 			kr_rplan_pop(rplan, qry);
 506 		}
 507 		ITERATE_LAYERS(request, qry, reset);
 508 		return kr_rplan_empty(rplan) ? KNOT_STATE_DONE : KNOT_STATE_PRODUCE;
 509 	}
 510 
 511 	/* This query has RD=0 or is ANY, stop here. */
 512 	if (qry->stype == KNOT_RRTYPE_ANY || !knot_wire_get_rd(request->answer->wire)) {
 513 		DEBUG_MSG(qry, "=> qtype is ANY or RD=0, bail out\n");
 514 		return KNOT_STATE_FAIL;
 515 	}
 516 
 517 	/* Update zone cut, spawn new subrequests. */
 518 	int state = zone_cut_check(request, qry, packet);
 519 	switch(state) {
 520 	case KNOT_STATE_FAIL: return KNOT_STATE_FAIL;
 521 	case KNOT_STATE_DONE: return KNOT_STATE_PRODUCE;
 522 	default: break;
 523 	}
 524 
 525 ns_election:
 526 
 527 	/* If the query has already selected a NS and is waiting for IPv4/IPv6 record,
 528 	 * elect best address only, otherwise elect a completely new NS.
 529 	 */
 530 	if(++ns_election_iter >= KR_ITER_LIMIT) {
 531 		DEBUG_MSG(qry, "=> couldn't converge NS selection, bail out\n");
 532 		return KNOT_STATE_FAIL;
 533 	}
 534 	if (qry->flags & (QUERY_AWAIT_IPV4|QUERY_AWAIT_IPV6)) {
 535 		kr_nsrep_elect_addr(qry, request->ctx);
 536 	} else if (!qry->ns.name || !(qry->flags & QUERY_TCP)) { /* Keep address when TCP retransmit. */
 537 		/* Root DNSKEY must be fetched from the hints to avoid chicken and egg problem. */
 538 		if (qry->sname[0] == '\0' && qry->stype == KNOT_RRTYPE_DNSKEY) {
 539 			DEBUG_MSG(qry, "=> priming root DNSKEY\n");
 540 			kr_zonecut_set_sbelt(request->ctx, &qry->zone_cut);
 541 			qry->flags |= QUERY_NO_THROTTLE; /* Pick even bad SBELT servers */
 542 		}
 543 		kr_nsrep_elect(qry, request->ctx);
 544 		if (qry->ns.score > KR_NS_MAX_SCORE) {
 545 			DEBUG_MSG(qry, "=> no valid NS left\n");
 546 			ITERATE_LAYERS(request, qry, reset);
 547 			kr_rplan_pop(rplan, qry);
 548 			return KNOT_STATE_PRODUCE;
 549 		}
 550 	}
 551 
 552 	/* Resolve address records */
 553 	if (qry->ns.addr[0].ip.sa_family == AF_UNSPEC) {
 554 		int ret = ns_resolve_addr(qry, request);
 555 		if (ret != 0) {
 556 			qry->flags &= ~(QUERY_AWAIT_IPV6|QUERY_AWAIT_IPV4|QUERY_TCP);
 557 			goto ns_election; /* Must try different NS */
 558 		}
 559 		ITERATE_LAYERS(request, qry, reset);
 560 		return KNOT_STATE_PRODUCE;
 561 	}
 562 
 563 	/* Prepare additional query */
 564 	int ret = query_finalize(request, qry, packet);
 565 	if (ret != 0) {
 566 		return KNOT_STATE_FAIL;
 567 	}
 568 
 569 	WITH_DEBUG {
 570 	char qname_str[KNOT_DNAME_MAXLEN], zonecut_str[KNOT_DNAME_MAXLEN], ns_str[SOCKADDR_STRLEN], type_str[16];
 571 	knot_dname_to_str(qname_str, knot_pkt_qname(packet), sizeof(qname_str));
 572 	knot_dname_to_str(zonecut_str, qry->zone_cut.name, sizeof(zonecut_str));
 573 	knot_rrtype_to_string(knot_pkt_qtype(packet), type_str, sizeof(type_str));
 574 	for (size_t i = 0; i < KR_NSREP_MAXADDR; ++i) {
 575 		struct sockaddr *addr = &qry->ns.addr[i].ip;
 576 		if (addr->sa_family == AF_UNSPEC) {
 577 			break;
 578 		}
 579 		inet_ntop(addr->sa_family, kr_nsrep_inaddr(qry->ns.addr[i]), ns_str, sizeof(ns_str));
 580 		DEBUG_MSG(qry, "%s: '%s' score: %u zone cut: '%s' m12n: '%s' type: '%s'\n",
 581 		          i == 0 ? "=> querying" : "   optional",
 582 		          ns_str, qry->ns.score, zonecut_str, qname_str, type_str);
 583 	}
 584 	}
 585 
 586 	gettimeofday(&qry->timestamp, NULL);
 587 	*dst = &qry->ns.addr[0].ip;
 588 	*type = (qry->flags & QUERY_TCP) ? SOCK_STREAM : SOCK_DGRAM;
 589 	return request->state;
 590 }
 591 
 592 int kr_resolve_finish(struct kr_request *request, int state)
 593 {
 594 #ifndef NDEBUG
 595 	struct kr_rplan *rplan = &request->rplan;
 596 #endif
 597 	/* Finalize answer */
 598 	if (answer_finalize(request, state) != 0) {
 599 		state = KNOT_STATE_FAIL;
 600 	}
 601 	/* Error during procesing, internal failure */
 602 	if (state != KNOT_STATE_DONE) {
 603 		knot_pkt_t *answer = request->answer;
 604 		if (knot_wire_get_rcode(answer->wire) == KNOT_RCODE_NOERROR) {
 605 			knot_wire_set_rcode(answer->wire, KNOT_RCODE_SERVFAIL);
 606 		}
 607 	}
 608 
 609 	ITERATE_LAYERS(request, NULL, finish);
 610 	DEBUG_MSG(NULL, "finished: %d, queries: %zu, mempool: %zu B\n",
 611 	          state, list_size(&rplan->resolved), (size_t) mp_total_size(request->pool.ctx));
 612 	return KNOT_STATE_DONE;
 613 }
 614 
 615 struct kr_rplan *kr_resolve_plan(struct kr_request *request)
 616 {
 617 	if (request) {
 618 		return &request->rplan;
 619 	}
 620 	return NULL;
 621 }

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:30, 21.0 KB) [[attachment:resolve.c]]
 All files | Selected Files: delete move to page copy to page

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