diff options
Diffstat (limited to 'iterator/iterator.c')
-rw-r--r-- | iterator/iterator.c | 153 |
1 files changed, 122 insertions, 31 deletions
diff --git a/iterator/iterator.c b/iterator/iterator.c index 1e0113a8740f..9d36660c0b18 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -72,6 +72,8 @@ /* in msec */ int UNKNOWN_SERVER_NICENESS = 376; +static void target_count_increase_nx(struct iter_qstate* iq, int num); + int iter_init(struct module_env* env, int id) { @@ -150,6 +152,7 @@ iter_new(struct module_qstate* qstate, int id) iq->sent_count = 0; iq->ratelimit_ok = 0; iq->target_count = NULL; + iq->dp_target_count = 0; iq->wait_priming_stub = 0; iq->refetch_glue = 0; iq->dnssec_expected = 0; @@ -221,6 +224,7 @@ final_state(struct iter_qstate* iq) static void error_supers(struct module_qstate* qstate, int id, struct module_qstate* super) { + struct iter_env* ie = (struct iter_env*)qstate->env->modinfo[id]; struct iter_qstate* super_iq = (struct iter_qstate*)super->minfo[id]; if(qstate->qinfo.qtype == LDNS_RR_TYPE_A || @@ -246,7 +250,11 @@ error_supers(struct module_qstate* qstate, int id, struct module_qstate* super) super->region, super_iq->dp)) log_err("out of memory adding missing"); } + delegpt_mark_neg(dpns, qstate->qinfo.qtype); dpns->resolved = 1; /* mark as failed */ + if((dpns->got4 == 2 || !ie->supports_ipv4) && + (dpns->got6 == 2 || !ie->supports_ipv6)) + target_count_increase_nx(super_iq, 1); } if(qstate->qinfo.qtype == LDNS_RR_TYPE_NS) { /* prime failed to get delegation */ @@ -621,7 +629,7 @@ static void target_count_create(struct iter_qstate* iq) { if(!iq->target_count) { - iq->target_count = (int*)calloc(2, sizeof(int)); + iq->target_count = (int*)calloc(3, sizeof(int)); /* if calloc fails we simply do not track this number */ if(iq->target_count) iq->target_count[0] = 1; @@ -634,6 +642,15 @@ target_count_increase(struct iter_qstate* iq, int num) target_count_create(iq); if(iq->target_count) iq->target_count[1] += num; + iq->dp_target_count++; +} + +static void +target_count_increase_nx(struct iter_qstate* iq, int num) +{ + target_count_create(iq); + if(iq->target_count) + iq->target_count[2] += num; } /** @@ -656,13 +673,15 @@ target_count_increase(struct iter_qstate* iq, int num) * @param subq_ret: if newly allocated, the subquerystate, or NULL if it does * not need initialisation. * @param v: if true, validation is done on the subquery. + * @param detached: true if this qstate should not attach to the subquery * @return false on error (malloc). */ static int generate_sub_request(uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, struct module_qstate* qstate, int id, struct iter_qstate* iq, enum iter_state initial_state, - enum iter_state finalstate, struct module_qstate** subq_ret, int v) + enum iter_state finalstate, struct module_qstate** subq_ret, int v, + int detached) { struct module_qstate* subq = NULL; struct iter_qstate* subiq = NULL; @@ -689,11 +708,23 @@ generate_sub_request(uint8_t* qname, size_t qnamelen, uint16_t qtype, valrec = 1; } - /* attach subquery, lookup existing or make a new one */ - fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub)); - if(!(*qstate->env->attach_sub)(qstate, &qinf, qflags, prime, valrec, - &subq)) { - return 0; + if(detached) { + struct mesh_state* sub = NULL; + fptr_ok(fptr_whitelist_modenv_add_sub( + qstate->env->add_sub)); + if(!(*qstate->env->add_sub)(qstate, &qinf, + qflags, prime, valrec, &subq, &sub)){ + return 0; + } + } + else { + /* attach subquery, lookup existing or make a new one */ + fptr_ok(fptr_whitelist_modenv_attach_sub( + qstate->env->attach_sub)); + if(!(*qstate->env->attach_sub)(qstate, &qinf, qflags, prime, + valrec, &subq)) { + return 0; + } } *subq_ret = subq; if(subq) { @@ -716,6 +747,7 @@ generate_sub_request(uint8_t* qname, size_t qnamelen, uint16_t qtype, subiq->target_count = iq->target_count; if(iq->target_count) iq->target_count[0] ++; /* extra reference */ + subiq->dp_target_count = 0; subiq->num_current_queries = 0; subiq->depth = iq->depth+1; outbound_list_init(&subiq->outlist); @@ -759,7 +791,7 @@ prime_root(struct module_qstate* qstate, struct iter_qstate* iq, int id, * the normal INIT state logic (which would cause an infloop). */ if(!generate_sub_request((uint8_t*)"\000", 1, LDNS_RR_TYPE_NS, qclass, qstate, id, iq, QUERYTARGETS_STATE, PRIME_RESP_STATE, - &subq, 0)) { + &subq, 0, 0)) { verbose(VERB_ALGO, "could not prime root"); return 0; } @@ -850,7 +882,7 @@ prime_stub(struct module_qstate* qstate, struct iter_qstate* iq, int id, * redundant INIT state processing. */ if(!generate_sub_request(stub_dp->name, stub_dp->namelen, LDNS_RR_TYPE_NS, qclass, qstate, id, iq, - QUERYTARGETS_STATE, PRIME_RESP_STATE, &subq, 0)) { + QUERYTARGETS_STATE, PRIME_RESP_STATE, &subq, 0, 0)) { verbose(VERB_ALGO, "could not prime stub"); errinf(qstate, "could not generate lookup for stub prime"); (void)error_response(qstate, id, LDNS_RCODE_SERVFAIL); @@ -1025,7 +1057,7 @@ generate_a_aaaa_check(struct module_qstate* qstate, struct iter_qstate* iq, if(!generate_sub_request(s->rk.dname, s->rk.dname_len, ntohs(s->rk.type), ntohs(s->rk.rrset_class), qstate, id, iq, - INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1)) { + INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1, 0)) { verbose(VERB_ALGO, "could not generate addr check"); return; } @@ -1069,7 +1101,7 @@ generate_ns_check(struct module_qstate* qstate, struct iter_qstate* iq, int id) iq->dp->name, LDNS_RR_TYPE_NS, iq->qchase.qclass); if(!generate_sub_request(iq->dp->name, iq->dp->namelen, LDNS_RR_TYPE_NS, iq->qchase.qclass, qstate, id, iq, - INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1)) { + INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1, 0)) { verbose(VERB_ALGO, "could not generate ns check"); return; } @@ -1126,7 +1158,7 @@ generate_dnskey_prefetch(struct module_qstate* qstate, iq->dp->name, LDNS_RR_TYPE_DNSKEY, iq->qchase.qclass); if(!generate_sub_request(iq->dp->name, iq->dp->namelen, LDNS_RR_TYPE_DNSKEY, iq->qchase.qclass, qstate, id, iq, - INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0)) { + INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0, 0)) { /* we'll be slower, but it'll work */ verbose(VERB_ALGO, "could not generate dnskey prefetch"); return; @@ -1315,6 +1347,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, iq->refetch_glue = 0; iq->query_restart_count++; iq->sent_count = 0; + iq->dp_target_count = 0; sock_list_insert(&qstate->reply_origin, NULL, 0, qstate->region); if(qstate->env->cfg->qname_minimisation) iq->minimisation_state = INIT_MINIMISE_STATE; @@ -1693,7 +1726,7 @@ generate_parentside_target_query(struct module_qstate* qstate, { struct module_qstate* subq; if(!generate_sub_request(name, namelen, qtype, qclass, qstate, - id, iq, INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0)) + id, iq, INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0, 0)) return 0; if(subq) { struct iter_qstate* subiq = @@ -1744,7 +1777,7 @@ generate_target_query(struct module_qstate* qstate, struct iter_qstate* iq, { struct module_qstate* subq; if(!generate_sub_request(name, namelen, qtype, qclass, qstate, - id, iq, INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0)) + id, iq, INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0, 0)) return 0; log_nametypeclass(VERB_QUERY, "new target", name, qtype, qclass); return 1; @@ -1783,6 +1816,14 @@ query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq, "number of glue fetches %d", s, iq->target_count[1]); return 0; } + if(iq->dp_target_count > MAX_DP_TARGET_COUNT) { + char s[LDNS_MAX_DOMAINLEN+1]; + dname_str(qstate->qinfo.qname, s); + 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; + } iter_mark_cycle_targets(qstate, iq->dp); missing = (int)delegpt_count_missing_targets(iq->dp); @@ -1896,7 +1937,7 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq, for(a = p->target_list; a; a=a->next_target) { (void)delegpt_add_addr(iq->dp, qstate->region, &a->addr, a->addrlen, a->bogus, - a->lame, a->tls_auth_name); + a->lame, a->tls_auth_name, NULL); } } iq->dp->has_parent_side_NS = 1; @@ -1913,6 +1954,7 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq, iq->refetch_glue = 1; iq->query_restart_count++; iq->sent_count = 0; + iq->dp_target_count = 0; if(qstate->env->cfg->qname_minimisation) iq->minimisation_state = INIT_MINIMISE_STATE; return next_state(iq, INIT_REQUEST_STATE); @@ -2078,7 +2120,7 @@ processDSNSFind(struct module_qstate* qstate, struct iter_qstate* iq, int id) iq->dsns_point, LDNS_RR_TYPE_NS, iq->qchase.qclass); if(!generate_sub_request(iq->dsns_point, iq->dsns_point_len, LDNS_RR_TYPE_NS, iq->qchase.qclass, qstate, id, iq, - INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0)) { + INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0, 0)) { errinf_dname(qstate, "for DS query parent-child nameserver search, could not generate NS lookup for", iq->dsns_point); return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL); } @@ -2136,6 +2178,13 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, errinf(qstate, "exceeded the maximum number of sends"); return error_response(qstate, id, LDNS_RCODE_SERVFAIL); } + if(iq->target_count && iq->target_count[2] > MAX_TARGET_NX) { + verbose(VERB_QUERY, "request has exceeded the maximum " + " number of nxdomain nameserver lookups with %d", + iq->target_count[2]); + errinf(qstate, "exceeded the maximum nameserver nxdomains"); + return error_response(qstate, id, LDNS_RCODE_SERVFAIL); + } /* Make sure we have a delegation point, otherwise priming failed * or another failure occurred */ @@ -2240,12 +2289,41 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, iq->qinfo_out.qtype, iq->qinfo_out.qclass, qstate->query_flags, qstate->region, qstate->env->scratch, 0); - if(msg && msg->rep->an_numrrsets == 0 - && FLAGS_GET_RCODE(msg->rep->flags) == + if(msg && FLAGS_GET_RCODE(msg->rep->flags) == LDNS_RCODE_NOERROR) /* no need to send query if it is already - * cached as NOERROR/NODATA */ + * cached as NOERROR */ return 1; + if(msg && FLAGS_GET_RCODE(msg->rep->flags) == + LDNS_RCODE_NXDOMAIN && + qstate->env->need_to_validate && + qstate->env->cfg->harden_below_nxdomain) { + if(msg->rep->security == sec_status_secure) { + iq->response = msg; + return final_state(iq); + } + if(msg->rep->security == sec_status_unchecked) { + struct module_qstate* subq = NULL; + if(!generate_sub_request( + iq->qinfo_out.qname, + iq->qinfo_out.qname_len, + iq->qinfo_out.qtype, + iq->qinfo_out.qclass, + qstate, id, iq, + INIT_REQUEST_STATE, + FINISHED_STATE, &subq, 1, 1)) + verbose(VERB_ALGO, + "could not validate NXDOMAIN " + "response"); + } + } + if(msg && FLAGS_GET_RCODE(msg->rep->flags) == + LDNS_RCODE_NXDOMAIN) { + /* return and add a label in the next + * minimisation iteration. + */ + return 1; + } } } if(iq->minimisation_state == SKIP_MINIMISE_STATE) { @@ -2321,6 +2399,8 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, * generated query will immediately be discarded due to depth and * that servfail is cached, which is not good as opportunism goes. */ if(iq->depth < ie->max_dependency_depth + && iq->num_target_queries == 0 + && (!iq->target_count || iq->target_count[2]==0) && iq->sent_count < TARGET_FETCH_STOP) { tf_policy = ie->target_fetch_policy[iq->depth]; } @@ -2366,6 +2446,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, iq->num_current_queries++; /* RespState decrements it*/ iq->referral_count++; /* make sure we don't loop */ iq->sent_count = 0; + iq->dp_target_count = 0; iq->state = QUERY_RESP_STATE; return 1; } @@ -2453,6 +2534,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, iq->num_current_queries++; /* RespState decrements it*/ iq->referral_count++; /* make sure we don't loop */ iq->sent_count = 0; + iq->dp_target_count = 0; iq->state = QUERY_RESP_STATE; return 1; } @@ -2747,7 +2829,8 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, /* Make subrequest to validate intermediate * NXDOMAIN if harden-below-nxdomain is * enabled. */ - if(qstate->env->cfg->harden_below_nxdomain) { + if(qstate->env->cfg->harden_below_nxdomain && + qstate->env->need_to_validate) { struct module_qstate* subq = NULL; log_query_info(VERB_QUERY, "schedule NXDOMAIN validation:", @@ -2759,16 +2842,10 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, iq->response->qinfo.qclass, qstate, id, iq, INIT_REQUEST_STATE, - FINISHED_STATE, &subq, 1)) + FINISHED_STATE, &subq, 1, 1)) verbose(VERB_ALGO, "could not validate NXDOMAIN " "response"); - outbound_list_clear(&iq->outlist); - iq->num_current_queries = 0; - fptr_ok(fptr_whitelist_modenv_detach_subs( - qstate->env->detach_subs)); - (*qstate->env->detach_subs)(qstate); - iq->num_target_queries = 0; } } return next_state(iq, QUERYTARGETS_STATE); @@ -2852,6 +2929,7 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, /* Count this as a referral. */ iq->referral_count++; iq->sent_count = 0; + iq->dp_target_count = 0; /* see if the next dp is a trust anchor, or a DS was sent * along, indicating dnssec is expected for next zone */ iq->dnssec_expected = iter_indicates_dnssec(qstate->env, @@ -2928,6 +3006,7 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, iq->dsns_point = NULL; iq->auth_zone_response = 0; iq->sent_count = 0; + iq->dp_target_count = 0; if(iq->minimisation_state != MINIMISE_STATE) /* Only count as query restart when it is not an extra * query as result of qname minimisation. */ @@ -3120,7 +3199,7 @@ processPrimeResponse(struct module_qstate* qstate, int id) if(!generate_sub_request(qstate->qinfo.qname, qstate->qinfo.qname_len, qstate->qinfo.qtype, qstate->qinfo.qclass, qstate, id, iq, - INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1)) { + INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1, 0)) { verbose(VERB_ALGO, "could not generate prime check"); } generate_a_aaaa_check(qstate, iq, id); @@ -3148,6 +3227,7 @@ static void processTargetResponse(struct module_qstate* qstate, int id, struct module_qstate* forq) { + struct iter_env* ie = (struct iter_env*)qstate->env->modinfo[id]; struct iter_qstate* iq = (struct iter_qstate*)qstate->minfo[id]; struct iter_qstate* foriq = (struct iter_qstate*)forq->minfo[id]; struct ub_packed_rrset_key* rrset; @@ -3185,7 +3265,7 @@ processTargetResponse(struct module_qstate* qstate, int id, log_rrset_key(VERB_ALGO, "add parentside glue to dp", iq->pside_glue); if(!delegpt_add_rrset(foriq->dp, forq->region, - iq->pside_glue, 1)) + iq->pside_glue, 1, NULL)) log_err("out of memory adding pside glue"); } @@ -3196,6 +3276,7 @@ processTargetResponse(struct module_qstate* qstate, int id, * response type was ANSWER. */ rrset = reply_find_answer_rrset(&iq->qchase, qstate->return_msg->rep); if(rrset) { + int additions = 0; /* if CNAMEs have been followed - add new NS to delegpt. */ /* BTW. RFC 1918 says NS should not have got CNAMEs. Robust. */ if(!delegpt_find_ns(foriq->dp, rrset->rk.dname, @@ -3207,13 +3288,23 @@ processTargetResponse(struct module_qstate* qstate, int id, } /* if dpns->lame then set the address(es) lame too */ if(!delegpt_add_rrset(foriq->dp, forq->region, rrset, - dpns->lame)) + dpns->lame, &additions)) log_err("out of memory adding targets"); + if(!additions) { + /* no new addresses, increase the nxns counter, like + * this could be a list of wildcards with no new + * addresses */ + target_count_increase_nx(foriq, 1); + } verbose(VERB_ALGO, "added target response"); delegpt_log(VERB_ALGO, foriq->dp); } else { verbose(VERB_ALGO, "iterator TargetResponse failed"); + delegpt_mark_neg(dpns, qstate->qinfo.qtype); dpns->resolved = 1; /* fail the target */ + if((dpns->got4 == 2 || !ie->supports_ipv4) && + (dpns->got6 == 2 || !ie->supports_ipv6)) + target_count_increase_nx(foriq, 1); } } @@ -3387,7 +3478,7 @@ processCollectClass(struct module_qstate* qstate, int id) qstate->qinfo.qname_len, qstate->qinfo.qtype, c, qstate, id, iq, INIT_REQUEST_STATE, FINISHED_STATE, &subq, - (int)!(qstate->query_flags&BIT_CD))) { + (int)!(qstate->query_flags&BIT_CD), 0)) { errinf(qstate, "could not generate class ANY" " lookup query"); return error_response(qstate, id, |