return KR_STATE_CONSUME;
}
+enum { GLUE_COUNT_THROTTLE = 26 };
+
/** @internal From \a pkt, fetch glue records for name \a ns, and update the cut etc.
*
* \param glue_cnt the number of accepted addresses (to be incremented)
continue;
}
(void) update_nsaddr(rr, req->current_query, glue_cnt);
+ /* If we reach limit on total glue addresses,
+ * we only load the first one per NS name (the one just above). */
+ if (*glue_cnt > GLUE_COUNT_THROTTLE)
+ break;
}
}
}
const knot_dname_t *current_zone_cut = qry->zone_cut.name;
bool ns_record_exists = false;
int glue_cnt = 0;
+ int ns_count = 0;
/* Update zone cut information. */
for (unsigned i = 0; i < ns->count; ++i) {
const knot_rrset_t *rr = knot_pkt_rr(ns, i);
case KR_STATE_FAIL: return state; break;
default: /* continue */ break;
}
+
+ if (++ns_count >= 13) {
+ VERBOSE_MSG("<= authority: many glue NSs, skipping the rest\n");
+ break;
+ }
} else if (rr->type == KNOT_RRTYPE_SOA
&& knot_dname_in_bailiwick(rr->owner, qry->zone_cut.name) > 0) {
/* SOA below cut in authority indicates different authority,
if (glue_cnt) {
VERBOSE_MSG("<= loaded %d glue addresses\n", glue_cnt);
}
+ if (glue_cnt > GLUE_COUNT_THROTTLE) {
+ VERBOSE_MSG("<= (some may have been omitted due to being too many)\n");
+ }
if ((qry->flags.DNSSEC_WANT) && (result == KR_STATE_CONSUME)) {
/** Fetch address for zone cut. Any rank is accepted (i.e. glue as well). */
static addrset_info_t fetch_addr(pack_t *addrs, const knot_dname_t *ns, uint16_t rrtype,
+ int *addr_budget,
knot_mm_t *mm_pool, const struct kr_query *qry)
// LATER(optim.): excessive data copying
{
return AI_UNKNOWN;
}
+ *addr_budget -= cached_rr.rrs.count - 1;
+ if (*addr_budget < 0) {
+ cached_rr.rrs.count += *addr_budget;
+ *addr_budget = 0;
+ }
+
/* Reserve memory in *addrs. Implementation detail:
* pack_t cares for lengths, so we don't store those in the data. */
const size_t pack_extra_size = cached_rr.rrs.size
return ret;
}
+ /* Consider at most 13 first NSs (like root). It's a trivial approach
+ * to limit our resources when choosing NSs. Otherwise DoS might be viable.
+ * We're not aware of any reasonable use case for having many NSs. */
+ if (ns_rds.count > 13) {
+ if (kr_log_is_debug_qry(ZCUT, qry)) {
+ auto_free char *name_txt = kr_dname_text(name);
+ VERBOSE_MSG(qry, "NS %s too large, reducing from %d names\n",
+ name_txt, (int)ns_rds.count);
+ }
+ ns_rds.count = 13;
+ }
+ /* Also trivially limit the total address count:
+ * first A and first AAAA are for free per NS,
+ * but the rest get a shared small limit and get skipped if exhausted. */
+ int addr_budget = 8;
+
/* Insert name servers for this zone cut, addresses will be looked up
* on-demand (either from cache or iteratively) */
bool all_bad = true; /**< All NSs (seen so far) are in a bad state. */
addrset_info_t infos[2];
/* Fetch NS reputation and decide whether to prefetch A/AAAA records. */
- infos[0] = fetch_addr(*pack, ns_name, KNOT_RRTYPE_A, cut->pool, qry);
- infos[1] = fetch_addr(*pack, ns_name, KNOT_RRTYPE_AAAA, cut->pool, qry);
+ infos[0] = fetch_addr(*pack, ns_name, KNOT_RRTYPE_A, &addr_budget,
+ cut->pool, qry);
+ infos[1] = fetch_addr(*pack, ns_name, KNOT_RRTYPE_AAAA, &addr_budget,
+ cut->pool, qry);
#if 0 /* rather unlikely to be useful unless changing some zcut code */
if (kr_log_is_debug_qry(ZCUT, qry)) {
VERBOSE_MSG(qry, "cut %s: all NSs bad, count = %d\n",
name_txt, (int)ns_rds.count);
}
+
+ kr_assert(addr_budget >= 0);
+ if (addr_budget <= 0 && kr_log_is_debug_qry(ZCUT, qry)) {
+ auto_free char *name_txt = kr_dname_text(name);
+ VERBOSE_MSG(qry, "NS %s have too many addresses together, reduced\n",
+ name_txt);
+ }
+
knot_rdataset_clear(&ns_rds, cut->pool);
return all_bad ? ELOOP : kr_ok();
}