inet_ntop(choice->sa_family, kr_inaddr(choice), addr_str, sizeof(addr_str));
QRDEBUG(qry, "wrkr", "=> server: '%s' flagged as 'bad'\n", addr_str);
}
- kr_nsrep_update_rtt(&qry->ns, choice, KR_NS_TIMEOUT, worker->engine->resolver.cache_rtt);
+ kr_nsrep_update_rtt(&qry->ns, choice, KR_NS_TIMEOUT,
+ worker->engine->resolver.cache_rtt, KR_NS_UPDATE);
}
}
/* Release timer handle */
#define KR_ITER_LIMIT 50 /* Built-in iterator limit */
#define KR_CNAME_CHAIN_LIMIT 40 /* Built-in maximum CNAME chain length */
#define KR_TIMEOUT_LIMIT 4 /* Maximum number of retries after timeout. */
+#define KR_QUERY_FAIL_LIMIT 40 /* Limit to the number of SERVFAIL\REFUSED
+ * responses per query
+ */
+#define KR_QUERY_NSRETRY_LIMIT 3 /* Maximum number of retries for single ns */
/*
* Defines.
#define kr_asan_poison(addr, size)
#define kr_asan_unpoison(addr, size)
#endif
-/* @endcond */
\ No newline at end of file
+/* @endcond */
switch(knot_wire_get_rcode(pkt->wire)) {
case KNOT_RCODE_NOERROR:
case KNOT_RCODE_NXDOMAIN:
- case KNOT_RCODE_REFUSED:
break; /* OK */
+ case KNOT_RCODE_REFUSED:
+ case KNOT_RCODE_SERVFAIL: {
+ DEBUG_MSG("<= rcode: %s\n", rcode ? rcode->name : "??");
+ query->fails += 1;
+ query->ns.fails += 1;
+ if (query->fails >= KR_QUERY_FAIL_LIMIT ||
+ query->ns.fails >= KR_QUERY_NSRETRY_LIMIT) {
+ return resolve_error(pkt, req);
+ } else {
+ query->flags |= QUERY_SERVFAIL;
+ return KNOT_STATE_CONSUME;
+ }
+ }
case KNOT_RCODE_FORMERR:
case KNOT_RCODE_NOTIMPL:
DEBUG_MSG("<= rcode: %s\n", rcode ? rcode->name : "??");
qry->ns.name = (const uint8_t *)"";
qry->ns.score = KR_NS_UNKNOWN;
qry->ns.reputation = 0;
+ qry->ns.fails = 0;
update_nsrep(&qry->ns, 0, addr, addr_len);
update_nsrep(&qry->ns, 1, NULL, 0);
return kr_ok();
(ns)->ctx = (ctx_); \
(ns)->addr[0].ip.sa_family = AF_UNSPEC; \
(ns)->reputation = 0; \
+ (ns)->fails = 0; \
(ns)->score = KR_NS_MAX_SCORE + 1; \
} while (0)
#undef ELECT_INIT
-int kr_nsrep_update_rtt(struct kr_nsrep *ns, const struct sockaddr *addr, unsigned score, kr_nsrep_lru_t *cache)
+int kr_nsrep_update_rtt(struct kr_nsrep *ns, const struct sockaddr *addr,
+ unsigned score, kr_nsrep_lru_t *cache, int umode)
{
if (!ns || !cache || ns->addr[0].ip.sa_family == AF_UNSPEC) {
return kr_error(EINVAL);
if (score <= KR_NS_GLUED) {
score = KR_NS_GLUED + 1;
}
- /* Set initial value or smooth over last two measurements */
- if (*cur != 0) {
+
+ if ((*cur != 0) && (umode == KR_NS_UPDATE)) {
+ /* In KR_NS_UPDATE mode, calculate smooth over last two measurements */
*cur = (*cur + score) / 2;
} else {
- /* First measurement, reset */
+ /* First measurement or KR_NS_RESET mode, reset */
*cur = score;
}
+
return kr_ok();
}
}
*cur = reputation;
return kr_ok();
-}
\ No newline at end of file
+}
KR_NS_NOEDNS = 1 << 2 /**< NS has no EDNS support */
};
+/**
+ * NS RTT update modes.
+ */
+enum kr_ns_update_mode {
+ KR_NS_UPDATE = 0, /**< Update as smooth over last two measurements */
+ KR_NS_RESET /**< Set to given value */
+};
+
/**
* NS reputation/QoS tracking.
*/
{
unsigned score; /**< NS score */
unsigned reputation; /**< NS reputation */
+ unsigned fails; /**< NS fail counter */
const knot_dname_t *name; /**< NS name */
struct kr_context *ctx; /**< Resolution context */
union {
/**
* Update NS address RTT information.
*
- * @brief Reputation is smoothed over last N measurements.
+ * @brief In KR_NS_UPDATE mode reputation is smoothed over last N measurements.
*
* @param ns updated NS representation
* @param addr chosen address (NULL for first)
* @param score new score (i.e. RTT), see enum kr_ns_score
* @param cache LRU cache
+ * @param umode update mode (KR_NS_UPDATE or KR_NS_RESET)
* @return 0 on success, error code on failure
*/
KR_EXPORT
-int kr_nsrep_update_rtt(struct kr_nsrep *ns, const struct sockaddr *addr, unsigned score, kr_nsrep_lru_t *cache);
+int kr_nsrep_update_rtt(struct kr_nsrep *ns, const struct sockaddr *addr,
+ unsigned score, kr_nsrep_lru_t *cache, int umode);
/**
* Update NSSET reputation information.
if (!(qry->flags & QUERY_SAFEMODE)) {
struct timeval now;
gettimeofday(&now, NULL);
- kr_nsrep_update_rtt(&qry->ns, src, time_diff(&qry->timestamp, &now), ctx->cache_rtt);
+ kr_nsrep_update_rtt(&qry->ns, src, time_diff(&qry->timestamp, &now), ctx->cache_rtt, KR_NS_UPDATE);
WITH_DEBUG {
char addr_str[INET6_ADDRSTRLEN];
inet_ntop(src->sa_family, kr_inaddr(src), addr_str, sizeof(addr_str));
DEBUG_MSG(qry, "<= server: '%s' rtt: %ld ms\n", addr_str, time_diff(&qry->timestamp, &now));
}
}
- qry->flags &= ~(QUERY_AWAIT_IPV6|QUERY_AWAIT_IPV4);
+ if (!(qry->flags & QUERY_SERVFAIL)) {
+ qry->flags &= ~(QUERY_AWAIT_IPV6|QUERY_AWAIT_IPV4);
+ }
/* Do not penalize validation timeouts. */
} else if (!(qry->flags & QUERY_DNSSEC_BOGUS)) {
- kr_nsrep_update_rtt(&qry->ns, src, KR_NS_TIMEOUT, ctx->cache_rtt);
+ kr_nsrep_update_rtt(&qry->ns, src, KR_NS_TIMEOUT, ctx->cache_rtt, KR_NS_RESET);
WITH_DEBUG {
char addr_str[INET6_ADDRSTRLEN];
inet_ntop(src->sa_family, kr_inaddr(src), addr_str, sizeof(addr_str));
ns_election:
- /* If the query has already selected a NS and is waiting for IPv4/IPv6 record,
+ /* If the query has got REFUSED & SERVFAIL, retry with current src up to KR_QUERY_NSRETRY_LIMIT.
+ * If the query has already selected a NS and is waiting for IPv4/IPv6 record,
* elect best address only, otherwise elect a completely new NS.
*/
if(++ns_election_iter >= KR_ITER_LIMIT) {
DEBUG_MSG(qry, "=> couldn't converge NS selection, bail out\n");
return KNOT_STATE_FAIL;
}
- if (qry->flags & (QUERY_AWAIT_IPV4|QUERY_AWAIT_IPV6)) {
+
+ if (qry->flags & QUERY_SERVFAIL) {
+ qry->flags &= ~QUERY_SERVFAIL;
+ } else if (qry->flags & (QUERY_AWAIT_IPV4|QUERY_AWAIT_IPV6)) {
kr_nsrep_elect_addr(qry, request->ctx);
} else if (!qry->ns.name || !(qry->flags & (QUERY_TCP|QUERY_STUB))) { /* Keep NS when requerying/stub. */
/* Root DNSKEY must be fetched from the hints to avoid chicken and egg problem. */
X(ALWAYS_CUT, 1 << 18) /**< Always recover zone cut (even if cached). */ \
X(DNSSEC_WEXPAND, 1 << 19) /**< Query response has wildcard expansion. */ \
X(PERMISSIVE, 1 << 20) /**< Permissive resolver mode. */ \
- X(STRICT, 1 << 21) /**< Strict resolver mode. */
+ X(STRICT, 1 << 21) /**< Strict resolver mode. */ \
+ X(SERVFAIL, 1 << 22) /**< Query response is SERVFAIL or REFUSED. */
/** Query flags */
enum kr_query_flag {
uint16_t id;
uint32_t flags;
uint32_t secret;
+ uint16_t fails;
struct timeval timestamp;
struct kr_zonecut zone_cut;
struct kr_nsrep ns;
-Subproject commit 900b169f958dcebf2fb7bba69538e6d316c06b7c
+Subproject commit 85cbf82244059113ff3149da04f99089dddc1c5b