summaryrefslogtreecommitdiff
path: root/iterator
diff options
context:
space:
mode:
authorCy Schubert <cy@FreeBSD.org>2024-08-16 16:41:16 +0000
committerCy Schubert <cy@FreeBSD.org>2024-08-16 16:41:16 +0000
commit96ef46e5cff01648c80c09c4364d10bc6f58119d (patch)
treea759010619ad11a8eaaaed7269bb06a9dfc2fa16 /iterator
parentc2a80056864d6eda0398fd127dc0ae515b39752b (diff)
Diffstat (limited to 'iterator')
-rw-r--r--iterator/iter_scrub.c82
-rw-r--r--iterator/iter_utils.c3
-rw-r--r--iterator/iterator.c124
-rw-r--r--iterator/iterator.h6
4 files changed, 180 insertions, 35 deletions
diff --git a/iterator/iter_scrub.c b/iterator/iter_scrub.c
index 48867e50c557..f038ad69af0e 100644
--- a/iterator/iter_scrub.c
+++ b/iterator/iter_scrub.c
@@ -367,6 +367,47 @@ type_allowed_in_additional_section(uint16_t tp)
return 0;
}
+/** Shorten RRset */
+static void
+shorten_rrset(sldns_buffer* pkt, struct rrset_parse* rrset, int count)
+{
+ /* The too large NS RRset is shortened. This is so that too large
+ * content does not overwhelm the cache. It may make the rrset
+ * bogus if it was signed, and then the domain is not resolved any
+ * more, that is okay, the NS RRset was too large. During a referral
+ * it can be shortened and then the first part of the list could
+ * be used to resolve. The scrub continues to disallow glue for the
+ * removed nameserver RRs and removes that too. Because the glue
+ * is not marked as okay, since the RRs have been removed here. */
+ int i;
+ struct rr_parse* rr = rrset->rr_first, *prev = NULL;
+ if(!rr)
+ return;
+ for(i=0; i<count; i++) {
+ prev = rr;
+ rr = rr->next;
+ if(!rr)
+ return; /* The RRset is already short. */
+ }
+ if(verbosity >= VERB_QUERY
+ && rrset->dname_len <= LDNS_MAX_DOMAINLEN) {
+ uint8_t buf[LDNS_MAX_DOMAINLEN+1];
+ dname_pkt_copy(pkt, buf, rrset->dname);
+ log_nametypeclass(VERB_QUERY, "normalize: shorten RRset:", buf,
+ rrset->type, ntohs(rrset->rrset_class));
+ }
+ /* remove further rrs */
+ rrset->rr_last = prev;
+ rrset->rr_count = count;
+ while(rr) {
+ rrset->size -= rr->size;
+ rr = rr->next;
+ }
+ if(rrset->rr_last)
+ rrset->rr_last->next = NULL;
+ else rrset->rr_first = NULL;
+}
+
/**
* This routine normalizes a response. This includes removing "irrelevant"
* records from the answer and additional sections and (re)synthesizing
@@ -387,6 +428,7 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg,
uint8_t* sname = qinfo->qname;
size_t snamelen = qinfo->qname_len;
struct rrset_parse* rrset, *prev, *nsset=NULL;
+ int cname_length = 0; /* number of CNAMEs, or DNAMEs */
if(FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NOERROR &&
FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NXDOMAIN)
@@ -401,6 +443,16 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg,
prev = NULL;
rrset = msg->rrset_first;
while(rrset && rrset->section == LDNS_SECTION_ANSWER) {
+ if(cname_length > 11 /* env->cfg.iter_scrub_cname */) {
+ /* Too many CNAMEs, or DNAMEs, from the authority
+ * server, scrub down the length to something
+ * shorter. This deletes everything after the limit
+ * is reached. The iterator is going to look up
+ * the content one by one anyway. */
+ remove_rrset("normalize: removing because too many cnames:",
+ pkt, msg, prev, &rrset);
+ continue;
+ }
if(rrset->type == LDNS_RR_TYPE_DNAME &&
pkt_strict_sub(pkt, sname, rrset->dname)) {
/* check if next rrset is correct CNAME. else,
@@ -420,6 +472,7 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg,
"too long");
return 0;
}
+ cname_length++;
if(nx && nx->type == LDNS_RR_TYPE_CNAME &&
dname_pkt_compare(pkt, sname, nx->dname) == 0) {
/* check next cname */
@@ -460,6 +513,7 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg,
if(rrset->type == LDNS_RR_TYPE_CNAME) {
struct rrset_parse* nx = rrset->rrset_all_next;
uint8_t* oldsname = sname;
+ cname_length++;
/* see if the next one is a DNAME, if so, swap them */
if(nx && nx->section == LDNS_SECTION_ANSWER &&
nx->type == LDNS_RR_TYPE_DNAME &&
@@ -507,6 +561,10 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg,
LDNS_SECTION_ANSWER &&
dname_pkt_compare(pkt, oldsname,
rrset->dname) == 0) {
+ if(rrset->type == LDNS_RR_TYPE_NS &&
+ rrset->rr_count > 20 /* env->cfg->iter_scrub_ns */) {
+ shorten_rrset(pkt, rrset, 20 /* env->cfg->iter_scrub_ns */);
+ }
prev = rrset;
rrset = rrset->rrset_all_next;
}
@@ -522,6 +580,11 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg,
continue;
}
+ if(rrset->type == LDNS_RR_TYPE_NS &&
+ rrset->rr_count > 20 /* env->cfg->iter_scrub_ns */) {
+ shorten_rrset(pkt, rrset, 20 /* env->cfg->iter_scrub_ns */);
+ }
+
/* Mark the additional names from relevant rrset as OK. */
/* only for RRsets that match the query name, other ones
* will be removed by sanitize, so no additional for them */
@@ -578,6 +641,25 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg,
"RRset:", pkt, msg, prev, &rrset);
continue;
}
+ if(rrset->rr_count > 20 /* env->cfg->iter_scrub_ns */) {
+ /* If this is not a referral, and the NS RRset
+ * is signed, then remove it entirely, so
+ * that when it becomes bogus it does not
+ * make the message that is otherwise fine
+ * into a bogus message. */
+ if(!(msg->an_rrsets == 0 &&
+ FLAGS_GET_RCODE(msg->flags) ==
+ LDNS_RCODE_NOERROR &&
+ !soa_in_auth(msg) &&
+ !(msg->flags & BIT_AA)) &&
+ rrset->rrsig_count != 0) {
+ remove_rrset("normalize: removing too large NS "
+ "RRset:", pkt, msg, prev, &rrset);
+ continue;
+ } else {
+ shorten_rrset(pkt, rrset, 20 /* env->cfg->iter_scrub_ns */);
+ }
+ }
}
/* if this is type DS and we query for type DS we just got
* a referral answer for our type DS query, fix packet */
diff --git a/iterator/iter_utils.c b/iterator/iter_utils.c
index f291178d2319..1b4f5f6ebb4f 100644
--- a/iterator/iter_utils.c
+++ b/iterator/iter_utils.c
@@ -279,9 +279,10 @@ iter_filter_unsuitable(struct iter_env* iter_env, struct module_env* env,
name, namelen, qtype, &lame, &dnsseclame, &reclame,
&rtt, now)) {
log_addr(VERB_ALGO, "servselect", &a->addr, a->addrlen);
- verbose(VERB_ALGO, " rtt=%d%s%s%s%s", rtt,
+ verbose(VERB_ALGO, " rtt=%d%s%s%s%s%s", rtt,
lame?" LAME":"",
dnsseclame?" DNSSEC_LAME":"",
+ a->dnsseclame?" ADDR_DNSSEC_LAME":"",
reclame?" REC_LAME":"",
a->lame?" ADDR_LAME":"");
if(lame)
diff --git a/iterator/iterator.c b/iterator/iterator.c
index 5732a414857e..228f5dfaef30 100644
--- a/iterator/iterator.c
+++ b/iterator/iterator.c
@@ -760,6 +760,14 @@ target_count_increase_nx(struct iter_qstate* iq, int num)
iq->target_count[TARGET_COUNT_NX] += num;
}
+static void
+target_count_increase_global_quota(struct iter_qstate* iq, int num)
+{
+ target_count_create(iq);
+ if(iq->target_count)
+ iq->target_count[TARGET_COUNT_GLOBAL_QUOTA] += num;
+}
+
/**
* Generate a subrequest.
* Generate a local request event. Local events are tied to this module, and
@@ -1378,7 +1386,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
"restarts (eg. indirections)");
if(iq->qchase.qname)
errinf_dname(qstate, "stop at", iq->qchase.qname);
- return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+ return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
}
/* We enforce a maximum recursion/dependency depth -- in general,
@@ -1560,6 +1568,11 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
errinf(qstate, "malloc failure for forward zone");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
+ if(!cache_fill_missing(qstate->env, iq->qchase.qclass,
+ qstate->region, iq->dp)) {
+ errinf(qstate, "malloc failure, copy extra info into delegation point");
+ return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+ }
if((qstate->query_flags&BIT_RD)==0) {
/* If the server accepts RD=0 queries and forwards
* with RD=1, then if the server is listed as an NS
@@ -1654,7 +1667,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
if(!iq->dp) {
log_err("internal error: no hints dp");
errinf(qstate, "no hints for this class");
- return error_response(qstate, id,
+ return error_response_cache(qstate, id,
LDNS_RCODE_SERVFAIL);
}
iq->dp = delegpt_copy(iq->dp, qstate->region);
@@ -1974,7 +1987,8 @@ generate_target_query(struct module_qstate* qstate, struct iter_qstate* iq,
* if it is negative, there is no maximum number of targets.
* @param num: returns the number of queries generated and processed,
* which may be zero if there were no missing targets.
- * @return false on error.
+ * @return 0 on success, nonzero on error. 1 means temporary failure and
+ * 2 means the failure can be cached.
*/
static int
query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq,
@@ -1997,13 +2011,13 @@ query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq,
else toget = maxtargets;
if(toget == 0) {
*num = 0;
- return 1;
+ return 0;
}
/* now that we are sure that a target query is going to be made,
* check the limits. */
if(iq->depth == ie->max_dependency_depth)
- return 0;
+ return 1;
if(iq->depth > 0 && iq->target_count &&
iq->target_count[TARGET_COUNT_QUERIES] > MAX_TARGET_COUNT) {
char s[LDNS_MAX_DOMAINLEN+1];
@@ -2011,7 +2025,7 @@ query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq,
verbose(VERB_QUERY, "request %s has exceeded the maximum "
"number of glue fetches %d", s,
iq->target_count[TARGET_COUNT_QUERIES]);
- return 0;
+ return 2;
}
if(iq->dp_target_count > MAX_DP_TARGET_COUNT) {
char s[LDNS_MAX_DOMAINLEN+1];
@@ -2019,7 +2033,7 @@ query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq,
verbose(VERB_QUERY, "request %s has exceeded the maximum "
"number of glue fetches %d to a single delegation point",
s, iq->dp_target_count);
- return 0;
+ return 2;
}
/* select 'toget' items from the total of 'missing' items */
@@ -2048,7 +2062,7 @@ query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq,
*num = query_count;
if(query_count > 0)
qstate->ext_state[id] = module_wait_subquery;
- return 0;
+ return 1;
}
query_count++;
/* If the mesh query list is full, exit the loop here.
@@ -2057,8 +2071,16 @@ query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq,
* increase, because the spawned state uses cpu and a
* socket while this state waits for that spawned
* state. Next time we can look up further targets */
- if(mesh_jostle_exceeded(qstate->env->mesh))
+ if(mesh_jostle_exceeded(qstate->env->mesh)) {
+ /* If no ip4 query is possible, that makes
+ * this ns resolved. */
+ if(!((ie->supports_ipv4 || ie->use_nat64) &&
+ ((ns->lame && !ns->done_pside4) ||
+ (!ns->lame && !ns->got4)))) {
+ ns->resolved = 1;
+ }
break;
+ }
}
/* Send the A request. */
if((ie->supports_ipv4 || ie->use_nat64) &&
@@ -2070,12 +2092,17 @@ query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq,
*num = query_count;
if(query_count > 0)
qstate->ext_state[id] = module_wait_subquery;
- return 0;
+ return 1;
}
query_count++;
/* If the mesh query list is full, exit the loop. */
- if(mesh_jostle_exceeded(qstate->env->mesh))
+ if(mesh_jostle_exceeded(qstate->env->mesh)) {
+ /* With the ip6 query already checked for,
+ * this makes the ns resolved. It is no longer
+ * a missing target. */
+ ns->resolved = 1;
break;
+ }
}
/* mark this target as in progress. */
@@ -2089,7 +2116,7 @@ query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq,
if(query_count > 0)
qstate->ext_state[id] = module_wait_subquery;
- return 1;
+ return 0;
}
/**
@@ -2180,12 +2207,14 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq,
}
/* query for an extra name added by the parent-NS record */
if(delegpt_count_missing_targets(iq->dp, NULL) > 0) {
- int qs = 0;
+ int qs = 0, ret;
verbose(VERB_ALGO, "try parent-side target name");
- if(!query_for_targets(qstate, iq, ie, id, 1, &qs)) {
+ if((ret=query_for_targets(qstate, iq, ie, id, 1, &qs))!=0) {
errinf(qstate, "could not fetch nameserver");
errinf_dname(qstate, "at zone", iq->dp->name);
- return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+ if(ret == 1)
+ return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+ return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
}
iq->num_target_queries += qs;
target_count_increase(iq, qs);
@@ -2414,13 +2443,13 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
verbose(VERB_QUERY, "request has exceeded the maximum "
"number of referrrals with %d", iq->referral_count);
errinf(qstate, "exceeded the maximum of referrals");
- return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+ return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
}
if(iq->sent_count > ie->max_sent_count) {
verbose(VERB_QUERY, "request has exceeded the maximum "
"number of sends with %d", iq->sent_count);
errinf(qstate, "exceeded the maximum number of sends");
- return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+ return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
}
/* Check if we reached MAX_TARGET_NX limit without a fallback activation. */
@@ -2450,7 +2479,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
"already present for the delegation point, no "
"fallback possible");
errinf(qstate, "exceeded the maximum nameserver nxdomains");
- return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+ return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
}
verbose(VERB_ALGO, "initiating parent-side fallback for "
"nxdomain nameserver lookups");
@@ -2493,7 +2522,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
"lookups (%d) with %d", MAX_TARGET_NX_FALLBACK,
iq->target_count[TARGET_COUNT_NX]);
errinf(qstate, "exceeded the maximum nameserver nxdomains");
- return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+ return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
}
if(!iq->dp->has_parent_side_NS) {
@@ -2707,7 +2736,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
verbose(VERB_ALGO, "auth zone lookup failed, no fallback,"
" servfail");
errinf(qstate, "auth zone lookup failed, fallback is off");
- return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+ return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
}
if(iq->dp->auth_dp) {
/* we wanted to fallback, but had no delegpt, only the
@@ -2736,11 +2765,13 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
/* if in 0x20 fallback get as many targets as possible */
if(iq->caps_fallback) {
- int extra = 0;
+ int extra = 0, ret;
size_t naddr, nres, navail;
- if(!query_for_targets(qstate, iq, ie, id, -1, &extra)) {
+ if((ret=query_for_targets(qstate, iq, ie, id, -1, &extra))!=0) {
errinf(qstate, "could not fetch nameservers for 0x20 fallback");
- return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+ if(ret == 1)
+ return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+ return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
}
iq->num_target_queries += extra;
target_count_increase(iq, extra);
@@ -2883,14 +2914,17 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
* to distinguish between generating (a) new target
* query, or failing. */
if(delegpt_count_missing_targets(iq->dp, NULL) > 0) {
- int qs = 0;
+ int qs = 0, ret;
verbose(VERB_ALGO, "querying for next "
"missing target");
- if(!query_for_targets(qstate, iq, ie, id,
- 1, &qs)) {
+ if((ret=query_for_targets(qstate, iq, ie, id,
+ 1, &qs))!=0) {
errinf(qstate, "could not fetch nameserver");
errinf_dname(qstate, "at zone", iq->dp->name);
- return error_response(qstate, id,
+ if(ret == 1)
+ return error_response(qstate, id,
+ LDNS_RCODE_SERVFAIL);
+ return error_response_cache(qstate, id,
LDNS_RCODE_SERVFAIL);
}
if(qs == 0 &&
@@ -2902,6 +2936,17 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
* so this is not a loop. */
return 1;
}
+ if(qs == 0) {
+ /* There should be targets now, and
+ * if there are not, it should not
+ * wait for no targets. Stop it from
+ * waiting forever, or looping to
+ * here, as a safeguard. */
+ errinf(qstate, "could not generate nameserver lookups");
+ errinf_dname(qstate, "at zone", iq->dp->name);
+ return error_response(qstate, id,
+ LDNS_RCODE_SERVFAIL);
+ }
iq->num_target_queries += qs;
target_count_increase(iq, qs);
}
@@ -2976,6 +3021,17 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
}
}
+ target_count_increase_global_quota(iq, 1);
+ if(iq->target_count && iq->target_count[TARGET_COUNT_GLOBAL_QUOTA]
+ > MAX_GLOBAL_QUOTA) {
+ char s[LDNS_MAX_DOMAINLEN+1];
+ dname_str(qstate->qinfo.qname, s);
+ verbose(VERB_QUERY, "request %s has exceeded the maximum "
+ "global quota on number of upstream queries %d", s,
+ iq->target_count[TARGET_COUNT_GLOBAL_QUOTA]);
+ return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
+ }
+
/* Do not check ratelimit for forwarding queries or if we already got a
* pass. */
sq_check_ratelimit = (!(iq->chase_flags & BIT_RD) && !iq->ratelimit_ok);
@@ -3025,7 +3081,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
qstate->was_ratelimited = 1;
errinf_dname(qstate, "exceeded ratelimit for zone",
iq->dp->name);
- return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+ return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
}
log_addr(VERB_QUERY, "error sending query to auth server",
&real_addr, real_addrlen);
@@ -3247,7 +3303,7 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
iter_scrub_nxdomain(iq->response);
return final_state(iq);
}
- return error_response(qstate, id,
+ return error_response_cache(qstate, id,
LDNS_RCODE_SERVFAIL);
}
/* Best effort qname-minimisation.
@@ -3582,7 +3638,7 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
" fallback possible, servfail");
errinf_dname(qstate, "response is bad, no fallback, "
"for auth zone", iq->dp->name);
- return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+ return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
}
verbose(VERB_ALGO, "auth zone response was bad, "
"fallback enabled");
@@ -3990,7 +4046,7 @@ processCollectClass(struct module_qstate* qstate, int id)
if(iq->num_current_queries == 0) {
verbose(VERB_ALGO, "No root hints or fwds, giving up "
"on qclass ANY");
- return error_response(qstate, id, LDNS_RCODE_REFUSED);
+ return error_response_cache(qstate, id, LDNS_RCODE_REFUSED);
}
/* return false, wait for queries to return */
}
@@ -4357,7 +4413,7 @@ process_response(struct module_qstate* qstate, struct iter_qstate* iq,
"getting different replies, failed");
outbound_list_remove(&iq->outlist, outbound);
errinf(qstate, "0x20 failed, then got different replies in fallback");
- (void)error_response(qstate, id,
+ (void)error_response_cache(qstate, id,
LDNS_RCODE_SERVFAIL);
return;
}
@@ -4457,8 +4513,8 @@ iter_get_mem(struct module_env* env, int id)
*/
static struct module_func_block iter_block = {
"iterator",
- &iter_init, &iter_deinit, &iter_operate, &iter_inform_super,
- &iter_clear, &iter_get_mem
+ NULL, NULL, &iter_init, &iter_deinit, &iter_operate,
+ &iter_inform_super, &iter_clear, &iter_get_mem
};
struct module_func_block*
diff --git a/iterator/iterator.h b/iterator/iterator.h
index e253f3f7e2bd..70b11df7ebcf 100644
--- a/iterator/iterator.h
+++ b/iterator/iterator.h
@@ -55,6 +55,9 @@ struct rbtree_type;
/** max number of targets spawned for a query and its subqueries */
#define MAX_TARGET_COUNT 64
+/** max number of upstream queries for a query and its subqueries, it is
+ * never reset. */
+#define MAX_GLOBAL_QUOTA 128
/** max number of target lookups per qstate, per delegation point */
#define MAX_DP_TARGET_COUNT 16
/** max number of nxdomains allowed for target lookups for a query and
@@ -248,6 +251,9 @@ enum target_count_variables {
TARGET_COUNT_QUERIES,
/** Number of nxdomain responses encountered. */
TARGET_COUNT_NX,
+ /** Global quota on number of queries to upstream servers per
+ * client request, that is never reset. */
+ TARGET_COUNT_GLOBAL_QUOTA,
/** This should stay last here, it is used for the allocation */
TARGET_COUNT_MAX,