#include "config.h"
#include "iterator/iterator.h"
#include "iterator/iter_utils.h"
#include "iterator/iter_hints.h"
#include "iterator/iter_fwd.h"
#include "iterator/iter_donotq.h"
#include "iterator/iter_delegpt.h"
#include "iterator/iter_resptype.h"
#include "iterator/iter_scrub.h"
#include "iterator/iter_priv.h"
#include "validator/val_neg.h"
#include "services/cache/dns.h"
#include "services/cache/rrset.h"
#include "services/cache/infra.h"
#include "services/authzone.h"
#include "util/module.h"
#include "util/netevent.h"
#include "util/net_help.h"
#include "util/regional.h"
#include "util/data/dname.h"
#include "util/data/msgencode.h"
#include "util/fptr_wlist.h"
#include "util/config_file.h"
#include "util/random.h"
#include "sldns/rrdef.h"
#include "sldns/wire2str.h"
#include "sldns/str2wire.h"
#include "sldns/parseutil.h"
#include "sldns/sbuffer.h"
int MAX_GLOBAL_QUOTA = 200;
int UNKNOWN_SERVER_NICENESS = 376;
int USEFUL_SERVER_TOP_TIMEOUT = 120000;
int BLACKLIST_PENALTY = (120000*4);
int PROBE_MAXRTO = PROBE_MAXRTO_DEFAULT;
static void target_count_increase_nx(struct iter_qstate* iq, int num);
int
iter_init(struct module_env* env, int id)
{
struct iter_env* iter_env = (struct iter_env*)calloc(1,
sizeof(struct iter_env));
if(!iter_env) {
log_err("malloc failure");
return 0;
}
env->modinfo[id] = (void*)iter_env;
lock_basic_init(&iter_env->queries_ratelimit_lock);
lock_protect(&iter_env->queries_ratelimit_lock,
&iter_env->num_queries_ratelimited,
sizeof(iter_env->num_queries_ratelimited));
if(!iter_apply_cfg(iter_env, env->cfg)) {
log_err("iterator: could not apply configuration settings.");
return 0;
}
return 1;
}
void
iter_deinit(struct module_env* env, int id)
{
struct iter_env* iter_env;
if(!env || !env->modinfo[id])
return;
iter_env = (struct iter_env*)env->modinfo[id];
lock_basic_destroy(&iter_env->queries_ratelimit_lock);
free(iter_env->target_fetch_policy);
priv_delete(iter_env->priv);
donotq_delete(iter_env->donotq);
caps_white_delete(iter_env->caps_white);
free(iter_env);
env->modinfo[id] = NULL;
}
static int
iter_new(struct module_qstate* qstate, int id)
{
struct iter_qstate* iq = (struct iter_qstate*)regional_alloc(
qstate->region, sizeof(struct iter_qstate));
qstate->minfo[id] = iq;
if(!iq)
return 0;
memset(iq, 0, sizeof(*iq));
iq->state = INIT_REQUEST_STATE;
iq->final_state = FINISHED_STATE;
iq->an_prepend_list = NULL;
iq->an_prepend_last = NULL;
iq->ns_prepend_list = NULL;
iq->ns_prepend_last = NULL;
iq->dp = NULL;
iq->depth = 0;
iq->num_target_queries = 0;
iq->num_current_queries = 0;
iq->query_restart_count = 0;
iq->referral_count = 0;
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;
iq->dnssec_lame_query = 0;
iq->chase_flags = qstate->query_flags;
iq->qchase = qstate->qinfo;
outbound_list_init(&iq->outlist);
iq->minimise_count = 0;
iq->timeout_count = 0;
if (qstate->env->cfg->qname_minimisation)
iq->minimisation_state = INIT_MINIMISE_STATE;
else
iq->minimisation_state = DONOT_MINIMISE_STATE;
memset(&iq->qinfo_out, 0, sizeof(struct query_info));
return 1;
}
static int
next_state(struct iter_qstate* iq, enum iter_state nextstate)
{
if(iter_state_is_responsestate(nextstate)) {
if(iq->response == NULL) {
log_err("transitioning to response state sans "
"response.");
}
}
iq->state = nextstate;
return 1;
}
static int
final_state(struct iter_qstate* iq)
{
return next_state(iq, iq->final_state);
}
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 ||
qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA) {
struct delegpt_ns* dpns = NULL;
super_iq->num_target_queries--;
if(super_iq->dp)
dpns = delegpt_find_ns(super_iq->dp,
qstate->qinfo.qname, qstate->qinfo.qname_len);
if(!dpns) {
verbose(VERB_ALGO, "subq error, but not interested");
log_query_info(VERB_ALGO, "superq", &super->qinfo);
return;
} else {
if(!cache_fill_missing(super->env, super_iq->qchase.qclass,
super->region, super_iq->dp, 0))
log_err("out of memory adding missing");
}
delegpt_mark_neg(dpns, qstate->qinfo.qtype);
if((dpns->got4 == 2 || (!ie->supports_ipv4 && !ie->nat64.use_nat64)) &&
(dpns->got6 == 2 || !ie->supports_ipv6)) {
dpns->resolved = 1;
target_count_increase_nx(super_iq, 1);
}
}
if(qstate->qinfo.qtype == LDNS_RR_TYPE_NS) {
super_iq->dp = NULL;
}
super_iq->state = QUERYTARGETS_STATE;
}
static int
error_response(struct module_qstate* qstate, int id, int rcode)
{
verbose(VERB_QUERY, "return error response %s",
sldns_lookup_by_id(sldns_rcodes, rcode)?
sldns_lookup_by_id(sldns_rcodes, rcode)->name:"??");
qstate->return_rcode = rcode;
qstate->return_msg = NULL;
qstate->ext_state[id] = module_finished;
return 0;
}
static int
error_response_cache(struct module_qstate* qstate, int id, int rcode)
{
struct reply_info err;
struct msgreply_entry* msg;
if(qstate->no_cache_store) {
return error_response(qstate, id, rcode);
}
if(qstate->prefetch_leeway > NORR_TTL) {
verbose(VERB_ALGO, "error response for prefetch in cache");
if(dns_cache_prefetch_adjust(qstate->env, &qstate->qinfo,
NORR_TTL, qstate->query_flags))
return error_response(qstate, id, rcode);
}
if((msg=msg_cache_lookup(qstate->env,
qstate->qinfo.qname, qstate->qinfo.qname_len,
qstate->qinfo.qtype, qstate->qinfo.qclass,
qstate->query_flags, 0,
qstate->env->cfg->serve_expired)) != NULL) {
struct reply_info* rep = (struct reply_info*)msg->entry.data;
if(qstate->env->cfg->serve_expired && rep) {
if(qstate->env->cfg->serve_expired_ttl_reset &&
*qstate->env->now + qstate->env->cfg->serve_expired_ttl
> rep->serve_expired_ttl) {
verbose(VERB_ALGO, "reset serve-expired-ttl for "
"response in cache");
rep->serve_expired_ttl = *qstate->env->now +
qstate->env->cfg->serve_expired_ttl;
}
verbose(VERB_ALGO, "set serve-expired-norec-ttl for "
"response in cache");
rep->serve_expired_norec_ttl = NORR_TTL +
*qstate->env->now;
}
if(rep && (FLAGS_GET_RCODE(rep->flags) ==
LDNS_RCODE_NOERROR ||
FLAGS_GET_RCODE(rep->flags) ==
LDNS_RCODE_NXDOMAIN ||
FLAGS_GET_RCODE(rep->flags) ==
LDNS_RCODE_YXDOMAIN) &&
(qstate->env->cfg->serve_expired ||
*qstate->env->now <= rep->ttl)) {
lock_rw_unlock(&msg->entry.lock);
return error_response(qstate, id, rcode);
}
lock_rw_unlock(&msg->entry.lock);
}
memset(&err, 0, sizeof(err));
err.flags = (uint16_t)(BIT_QR | BIT_RA);
FLAGS_SET_RCODE(err.flags, rcode);
err.qdcount = 1;
err.ttl = NORR_TTL;
err.prefetch_ttl = PREFETCH_TTL_CALC(err.ttl);
err.serve_expired_ttl = NORR_TTL;
err.security = sec_status_indeterminate;
verbose(VERB_ALGO, "store error response in message cache");
iter_dns_store(qstate->env, &qstate->qinfo, &err, 0, 0, 0, NULL,
qstate->query_flags, qstate->qstarttime, qstate->is_valrec);
return error_response(qstate, id, rcode);
}
static int
prepend_is_duplicate(struct ub_packed_rrset_key** sets, size_t to,
struct ub_packed_rrset_key* dup)
{
size_t i;
for(i=0; i<to; i++) {
if(sets[i]->rk.type == dup->rk.type &&
sets[i]->rk.rrset_class == dup->rk.rrset_class &&
sets[i]->rk.dname_len == dup->rk.dname_len &&
query_dname_compare(sets[i]->rk.dname, dup->rk.dname)
== 0)
return 1;
}
return 0;
}
static int
iter_prepend(struct iter_qstate* iq, struct dns_msg* msg,
struct regional* region)
{
struct iter_prep_list* p;
struct ub_packed_rrset_key** sets;
size_t num_an = 0, num_ns = 0;;
for(p = iq->an_prepend_list; p; p = p->next)
num_an++;
for(p = iq->ns_prepend_list; p; p = p->next)
num_ns++;
if(num_an + num_ns == 0)
return 1;
verbose(VERB_ALGO, "prepending %d rrsets", (int)num_an + (int)num_ns);
if(num_an > RR_COUNT_MAX || num_ns > RR_COUNT_MAX ||
msg->rep->rrset_count > RR_COUNT_MAX) return 0;
sets = regional_alloc(region, (num_an+num_ns+msg->rep->rrset_count) *
sizeof(struct ub_packed_rrset_key*));
if(!sets)
return 0;
num_an = 0;
for(p = iq->an_prepend_list; p; p = p->next) {
sets[num_an++] = p->rrset;
if(ub_packed_rrset_ttl(p->rrset) < msg->rep->ttl) {
msg->rep->ttl = ub_packed_rrset_ttl(p->rrset);
msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl);
msg->rep->serve_expired_ttl = msg->rep->ttl + SERVE_EXPIRED_TTL;
}
}
memcpy(sets+num_an, msg->rep->rrsets, msg->rep->an_numrrsets *
sizeof(struct ub_packed_rrset_key*));
num_ns = 0;
for(p = iq->ns_prepend_list; p; p = p->next) {
if(prepend_is_duplicate(sets+msg->rep->an_numrrsets+num_an,
num_ns, p->rrset) || prepend_is_duplicate(
msg->rep->rrsets+msg->rep->an_numrrsets,
msg->rep->ns_numrrsets, p->rrset))
continue;
sets[msg->rep->an_numrrsets + num_an + num_ns++] = p->rrset;
if(ub_packed_rrset_ttl(p->rrset) < msg->rep->ttl) {
msg->rep->ttl = ub_packed_rrset_ttl(p->rrset);
msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl);
msg->rep->serve_expired_ttl = msg->rep->ttl + SERVE_EXPIRED_TTL;
}
}
memcpy(sets + num_an + msg->rep->an_numrrsets + num_ns,
msg->rep->rrsets + msg->rep->an_numrrsets,
(msg->rep->ns_numrrsets + msg->rep->ar_numrrsets) *
sizeof(struct ub_packed_rrset_key*));
msg->rep->rrset_count += num_an + num_ns;
msg->rep->an_numrrsets += num_an;
msg->rep->ns_numrrsets += num_ns;
msg->rep->rrsets = sets;
return 1;
}
static int
iter_find_rrset_in_prepend_answer(struct iter_qstate* iq,
struct ub_packed_rrset_key* rrset)
{
struct iter_prep_list* p = iq->an_prepend_list;
while(p) {
if(ub_rrset_compare(p->rrset, rrset) == 0 &&
rrsetdata_equal((struct packed_rrset_data*)p->rrset
->entry.data, (struct packed_rrset_data*)rrset
->entry.data))
return 1;
p = p->next;
}
return 0;
}
static int
iter_add_prepend_answer(struct module_qstate* qstate, struct iter_qstate* iq,
struct ub_packed_rrset_key* rrset)
{
struct iter_prep_list* p = (struct iter_prep_list*)regional_alloc(
qstate->region, sizeof(struct iter_prep_list));
if(!p)
return 0;
p->rrset = rrset;
p->next = NULL;
if(iq->an_prepend_last)
iq->an_prepend_last->next = p;
else iq->an_prepend_list = p;
iq->an_prepend_last = p;
return 1;
}
static int
iter_add_prepend_auth(struct module_qstate* qstate, struct iter_qstate* iq,
struct ub_packed_rrset_key* rrset)
{
struct iter_prep_list* p = (struct iter_prep_list*)regional_alloc(
qstate->region, sizeof(struct iter_prep_list));
if(!p)
return 0;
p->rrset = rrset;
p->next = NULL;
if(iq->ns_prepend_last)
iq->ns_prepend_last->next = p;
else iq->ns_prepend_list = p;
iq->ns_prepend_last = p;
return 1;
}
static int
handle_cname_response(struct module_qstate* qstate, struct iter_qstate* iq,
struct dns_msg* msg, uint8_t** mname, size_t* mname_len)
{
size_t i;
*mname = iq->qchase.qname;
*mname_len = iq->qchase.qname_len;
for(i=0; i<msg->rep->an_numrrsets; i++) {
struct ub_packed_rrset_key* r = msg->rep->rrsets[i];
if(ntohs(r->rk.type) == LDNS_RR_TYPE_DNAME &&
dname_strict_subdomain_c(*mname, r->rk.dname) &&
!iter_find_rrset_in_prepend_answer(iq, r)) {
if(!iter_add_prepend_answer(qstate, iq, r))
return 0;
continue;
}
if(ntohs(r->rk.type) == LDNS_RR_TYPE_CNAME &&
query_dname_compare(*mname, r->rk.dname) == 0 &&
!iter_find_rrset_in_prepend_answer(iq, r)) {
if(!iter_add_prepend_answer(qstate, iq, r))
return 0;
get_cname_target(r, mname, mname_len);
}
}
for(i=msg->rep->an_numrrsets; i<msg->rep->an_numrrsets +
msg->rep->ns_numrrsets; i++) {
struct ub_packed_rrset_key* r = msg->rep->rrsets[i];
if(ntohs(r->rk.type) == LDNS_RR_TYPE_NSEC ||
ntohs(r->rk.type) == LDNS_RR_TYPE_NSEC3) {
if(!iter_add_prepend_auth(qstate, iq, r))
return 0;
}
}
return 1;
}
static void
fill_fail_addr(struct iter_qstate* iq, struct sockaddr_storage* addr,
socklen_t addrlen)
{
if(addrlen == 0) {
iq->fail_addr_type = 0;
return;
}
if(((struct sockaddr_in*)addr)->sin_family == AF_INET) {
iq->fail_addr_type = 4;
memcpy(&iq->fail_addr.in,
&((struct sockaddr_in*)addr)->sin_addr,
sizeof(iq->fail_addr.in));
}
#ifdef AF_INET6
else if(((struct sockaddr_in*)addr)->sin_family == AF_INET6) {
iq->fail_addr_type = 6;
memcpy(&iq->fail_addr.in6,
&((struct sockaddr_in6*)addr)->sin6_addr,
sizeof(iq->fail_addr.in6));
}
#endif
else {
iq->fail_addr_type = 0;
}
}
static void
print_fail_addr(struct iter_qstate* iq, char* buf, size_t len)
{
if(iq->fail_addr_type == 4) {
if(inet_ntop(AF_INET, &iq->fail_addr.in, buf,
(socklen_t)len) == 0)
(void)strlcpy(buf, "(inet_ntop error)", len);
}
#ifdef AF_INET6
else if(iq->fail_addr_type == 6) {
if(inet_ntop(AF_INET6, &iq->fail_addr.in6, buf,
(socklen_t)len) == 0)
(void)strlcpy(buf, "(inet_ntop error)", len);
}
#endif
else
(void)strlcpy(buf, "", len);
}
static void
errinf_reply(struct module_qstate* qstate, struct iter_qstate* iq)
{
if(qstate->env->cfg->val_log_level < 2 && !qstate->env->cfg->log_servfail)
return;
if((qstate->reply && qstate->reply->remote_addrlen != 0) ||
(iq->fail_addr_type != 0)) {
char from[256], frm[512];
if(qstate->reply && qstate->reply->remote_addrlen != 0)
addr_to_str(&qstate->reply->remote_addr,
qstate->reply->remote_addrlen, from,
sizeof(from));
else
print_fail_addr(iq, from, sizeof(from));
snprintf(frm, sizeof(frm), "from %s", from);
errinf(qstate, frm);
}
if(iq->scrub_failures || iq->parse_failures) {
if(iq->scrub_failures)
errinf(qstate, "upstream response failed scrub");
if(iq->parse_failures)
errinf(qstate, "could not parse upstream response");
} else if(iq->response == NULL && iq->timeout_count != 0) {
errinf(qstate, "upstream server timeout");
} else if(iq->response == NULL) {
errinf(qstate, "no server to query");
if(iq->dp) {
if(iq->dp->target_list == NULL)
errinf(qstate, "no addresses for nameservers");
else errinf(qstate, "nameserver addresses not usable");
if(iq->dp->nslist == NULL)
errinf(qstate, "have no nameserver names");
if(iq->dp->bogus)
errinf(qstate, "NS record was dnssec bogus");
}
}
if(iq->response && iq->response->rep) {
if(FLAGS_GET_RCODE(iq->response->rep->flags) != 0) {
char rcode[256], rc[32];
(void)sldns_wire2str_rcode_buf(
FLAGS_GET_RCODE(iq->response->rep->flags),
rc, sizeof(rc));
snprintf(rcode, sizeof(rcode), "got %s", rc);
errinf(qstate, rcode);
} else {
if(iq->response->rep->an_numrrsets == 0) {
errinf(qstate, "nodata answer");
}
}
}
}
static int
can_have_last_resort(struct module_env* env, uint8_t* nm, size_t ATTR_UNUSED(nmlen),
uint16_t qclass, int* have_dp, struct delegpt** retdp,
struct regional* region)
{
struct delegpt* dp = NULL;
int nolock = 0;
if(!dname_is_root(nm) &&
(dp = hints_find(env->hints, nm, qclass, nolock)) &&
dp->has_parent_side_NS) {
if(retdp) *retdp = delegpt_copy(dp, region);
lock_rw_unlock(&env->hints->lock);
if(have_dp) *have_dp = 1;
return 0;
}
if(dp) {
lock_rw_unlock(&env->hints->lock);
dp = NULL;
}
if((dp = forwards_find(env->fwds, nm, qclass, nolock)) &&
dp->has_parent_side_NS) {
if(retdp) *retdp = delegpt_copy(dp, region);
lock_rw_unlock(&env->fwds->lock);
if(have_dp) *have_dp = 1;
return 0;
}
if(dp) { lock_rw_unlock(&env->fwds->lock); }
return 1;
}
static int
is_caps_whitelisted(struct iter_env* ie, struct iter_qstate* iq)
{
if(!ie->caps_white) return 0;
return name_tree_lookup(ie->caps_white, iq->qchase.qname,
iq->qchase.qname_len, dname_count_labels(iq->qchase.qname),
iq->qchase.qclass) != NULL;
}
static void
target_count_create(struct iter_qstate* iq)
{
if(!iq->target_count) {
iq->target_count = (int*)calloc(TARGET_COUNT_MAX, sizeof(int));
if(iq->target_count) {
iq->target_count[TARGET_COUNT_REF] = 1;
iq->nxns_dp = (uint8_t**)calloc(1, sizeof(uint8_t*));
}
}
}
static void
target_count_increase(struct iter_qstate* iq, int num)
{
target_count_create(iq);
if(iq->target_count)
iq->target_count[TARGET_COUNT_QUERIES] += 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[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;
}
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,
int detached)
{
struct module_qstate* subq = NULL;
struct iter_qstate* subiq = NULL;
uint16_t qflags = 0;
struct query_info qinf;
int prime = (finalstate == PRIME_RESP_STATE)?1:0;
int valrec = 0;
qinf.qname = qname;
qinf.qname_len = qnamelen;
qinf.qtype = qtype;
qinf.qclass = qclass;
qinf.local_alias = NULL;
if(initial_state == INIT_REQUEST_STATE)
qflags |= BIT_RD;
if(!v) {
qflags |= BIT_CD;
valrec = 1;
}
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 {
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) {
subq->curmod = id;
subq->ext_state[id] = module_state_initial;
subq->minfo[id] = regional_alloc(subq->region,
sizeof(struct iter_qstate));
if(!subq->minfo[id]) {
log_err("init subq: out of memory");
fptr_ok(fptr_whitelist_modenv_kill_sub(
qstate->env->kill_sub));
(*qstate->env->kill_sub)(subq);
return 0;
}
subiq = (struct iter_qstate*)subq->minfo[id];
memset(subiq, 0, sizeof(*subiq));
subiq->num_target_queries = 0;
target_count_create(iq);
subiq->target_count = iq->target_count;
if(iq->target_count) {
iq->target_count[TARGET_COUNT_REF] ++;
subiq->nxns_dp = iq->nxns_dp;
}
subiq->dp_target_count = 0;
subiq->num_current_queries = 0;
subiq->depth = iq->depth+1;
outbound_list_init(&subiq->outlist);
subiq->state = initial_state;
subiq->final_state = finalstate;
subiq->qchase = subq->qinfo;
subiq->chase_flags = subq->query_flags;
subiq->refetch_glue = 0;
if(qstate->env->cfg->qname_minimisation)
subiq->minimisation_state = INIT_MINIMISE_STATE;
else
subiq->minimisation_state = DONOT_MINIMISE_STATE;
memset(&subiq->qinfo_out, 0, sizeof(struct query_info));
}
return 1;
}
static int
prime_root(struct module_qstate* qstate, struct iter_qstate* iq, int id,
uint16_t qclass)
{
struct delegpt* dp;
struct module_qstate* subq;
int nolock = 0;
verbose(VERB_DETAIL, "priming . %s NS",
sldns_lookup_by_id(sldns_rr_classes, (int)qclass)?
sldns_lookup_by_id(sldns_rr_classes, (int)qclass)->name:"??");
dp = hints_find_root(qstate->env->hints, qclass, nolock);
if(!dp) {
verbose(VERB_ALGO, "Cannot prime due to lack of hints");
return 0;
}
if(!generate_sub_request((uint8_t*)"\000", 1, LDNS_RR_TYPE_NS,
qclass, qstate, id, iq, QUERYTARGETS_STATE, PRIME_RESP_STATE,
&subq, 0, 0)) {
lock_rw_unlock(&qstate->env->hints->lock);
verbose(VERB_ALGO, "could not prime root");
return 0;
}
if(subq) {
struct iter_qstate* subiq =
(struct iter_qstate*)subq->minfo[id];
subiq->dp = delegpt_copy(dp, subq->region);
lock_rw_unlock(&qstate->env->hints->lock);
if(!subiq->dp) {
log_err("out of memory priming root, copydp");
fptr_ok(fptr_whitelist_modenv_kill_sub(
qstate->env->kill_sub));
(*qstate->env->kill_sub)(subq);
return 0;
}
subiq->num_target_queries = 0;
subiq->dnssec_expected = iter_indicates_dnssec(
qstate->env, subiq->dp, NULL, subq->qinfo.qclass);
} else {
lock_rw_unlock(&qstate->env->hints->lock);
}
qstate->ext_state[id] = module_wait_subquery;
return 1;
}
static int
prime_stub(struct module_qstate* qstate, struct iter_qstate* iq, int id,
uint8_t* qname, uint16_t qclass)
{
struct iter_hints_stub* stub;
struct delegpt* stub_dp;
struct module_qstate* subq;
int nolock = 0;
if(!qname) return 0;
stub = hints_lookup_stub(qstate->env->hints, qname, qclass, iq->dp,
nolock);
if(!stub) return 0;
stub_dp = stub->dp;
if(!iq->auth_zone_avoid && iq->dp && iq->dp->auth_dp &&
query_dname_compare(iq->dp->name, stub_dp->name) == 0) {
lock_rw_unlock(&qstate->env->hints->lock);
return 0;
}
if(stub->noprime) {
int r = 0;
if(iq->dp == NULL) r = 2;
iq->dp = delegpt_copy(stub_dp, qstate->region);
lock_rw_unlock(&qstate->env->hints->lock);
if(!iq->dp) {
log_err("out of memory priming stub");
errinf(qstate, "malloc failure, priming stub");
(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
return 1;
}
log_nametypeclass(VERB_DETAIL, "use stub", iq->dp->name,
LDNS_RR_TYPE_NS, qclass);
return r;
}
log_nametypeclass(VERB_DETAIL, "priming stub", stub_dp->name,
LDNS_RR_TYPE_NS, qclass);
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, 0)) {
lock_rw_unlock(&qstate->env->hints->lock);
verbose(VERB_ALGO, "could not prime stub");
errinf(qstate, "could not generate lookup for stub prime");
(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
return 1;
}
if(subq) {
struct iter_qstate* subiq =
(struct iter_qstate*)subq->minfo[id];
subiq->dp = delegpt_copy(stub_dp, subq->region);
lock_rw_unlock(&qstate->env->hints->lock);
if(!subiq->dp) {
log_err("out of memory priming stub, copydp");
fptr_ok(fptr_whitelist_modenv_kill_sub(
qstate->env->kill_sub));
(*qstate->env->kill_sub)(subq);
errinf(qstate, "malloc failure, in stub prime");
(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
return 1;
}
subiq->num_target_queries = 0;
subiq->wait_priming_stub = 1;
subiq->dnssec_expected = iter_indicates_dnssec(
qstate->env, subiq->dp, NULL, subq->qinfo.qclass);
} else {
lock_rw_unlock(&qstate->env->hints->lock);
}
qstate->ext_state[id] = module_wait_subquery;
return 1;
}
static int
auth_zone_delegpt(struct module_qstate* qstate, struct iter_qstate* iq,
uint8_t* delname, size_t delnamelen)
{
struct auth_zone* z;
if(iq->auth_zone_avoid)
return 1;
if(!delname) {
delname = iq->qchase.qname;
delnamelen = iq->qchase.qname_len;
}
lock_rw_rdlock(&qstate->env->auth_zones->lock);
z = auth_zones_find_zone(qstate->env->auth_zones, delname, delnamelen,
qstate->qinfo.qclass);
if(!z) {
lock_rw_unlock(&qstate->env->auth_zones->lock);
return 1;
}
lock_rw_rdlock(&z->lock);
lock_rw_unlock(&qstate->env->auth_zones->lock);
if(z->for_upstream) {
if(iq->dp && query_dname_compare(z->name, iq->dp->name) == 0
&& iq->dp->auth_dp && qstate->blacklist &&
z->fallback_enabled) {
if(verbosity>=VERB_ALGO) {
char buf[LDNS_MAX_DOMAINLEN];
dname_str(z->name, buf);
verbose(VERB_ALGO, "auth_zone %s "
"fallback because cache blacklisted",
buf);
}
lock_rw_unlock(&z->lock);
iq->dp = NULL;
return 1;
}
if(iq->dp==NULL || dname_subdomain_c(z->name, iq->dp->name)) {
struct delegpt* dp;
if(qstate->blacklist && z->fallback_enabled) {
if(verbosity>=VERB_ALGO) {
char buf[LDNS_MAX_DOMAINLEN];
dname_str(z->name, buf);
verbose(VERB_ALGO, "auth_zone %s "
"fallback because cache blacklisted",
buf);
}
lock_rw_unlock(&z->lock);
return 1;
}
dp = (struct delegpt*)regional_alloc_zero(
qstate->region, sizeof(*dp));
if(!dp) {
log_err("alloc failure");
if(z->fallback_enabled) {
lock_rw_unlock(&z->lock);
return 1;
}
lock_rw_unlock(&z->lock);
errinf(qstate, "malloc failure");
return 0;
}
dp->name = regional_alloc_init(qstate->region,
z->name, z->namelen);
if(!dp->name) {
log_err("alloc failure");
if(z->fallback_enabled) {
lock_rw_unlock(&z->lock);
return 1;
}
lock_rw_unlock(&z->lock);
errinf(qstate, "malloc failure");
return 0;
}
dp->namelen = z->namelen;
dp->namelabs = z->namelabs;
dp->auth_dp = 1;
iq->dp = dp;
}
}
lock_rw_unlock(&z->lock);
return 1;
}
static void
generate_a_aaaa_check(struct module_qstate* qstate, struct iter_qstate* iq,
int id)
{
struct iter_env* ie = (struct iter_env*)qstate->env->modinfo[id];
struct module_qstate* subq;
size_t i;
struct reply_info* rep = iq->response->rep;
struct ub_packed_rrset_key* s;
log_assert(iq->dp);
if(iq->depth == ie->max_dependency_depth)
return;
for(i=rep->an_numrrsets+rep->ns_numrrsets; i<rep->rrset_count; i++) {
s = rep->rrsets[i];
if( !(ntohs(s->rk.type)==LDNS_RR_TYPE_A ||
ntohs(s->rk.type)==LDNS_RR_TYPE_AAAA)) {
continue;
}
if(qstate->qinfo.qtype == ntohs(s->rk.type) &&
qstate->qinfo.qclass == ntohs(s->rk.rrset_class) &&
query_dname_compare(qstate->qinfo.qname,
s->rk.dname)==0 &&
(qstate->query_flags&BIT_RD) &&
!(qstate->query_flags&BIT_CD))
continue;
log_nametypeclass(VERB_ALGO, "schedule addr fetch",
s->rk.dname, ntohs(s->rk.type),
ntohs(s->rk.rrset_class));
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, 0)) {
verbose(VERB_ALGO, "could not generate addr check");
return;
}
}
}
static void
generate_ns_check(struct module_qstate* qstate, struct iter_qstate* iq, int id)
{
struct iter_env* ie = (struct iter_env*)qstate->env->modinfo[id];
struct module_qstate* subq;
log_assert(iq->dp);
if(iq->depth == ie->max_dependency_depth)
return;
if(!can_have_last_resort(qstate->env, iq->dp->name, iq->dp->namelen,
iq->qchase.qclass, NULL, NULL, NULL))
return;
if(qstate->qinfo.qtype == LDNS_RR_TYPE_NS &&
query_dname_compare(iq->dp->name, qstate->qinfo.qname)==0 &&
(qstate->query_flags&BIT_RD) && !(qstate->query_flags&BIT_CD)){
generate_a_aaaa_check(qstate, iq, id);
return;
}
if(qstate->qinfo.qtype == LDNS_RR_TYPE_DS)
return;
log_nametypeclass(VERB_ALGO, "schedule ns fetch",
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, 0)) {
verbose(VERB_ALGO, "could not generate ns check");
return;
}
if(subq) {
struct iter_qstate* subiq =
(struct iter_qstate*)subq->minfo[id];
subiq->refetch_glue = 1;
subiq->dp = delegpt_copy(iq->dp, subq->region);
if(!subiq->dp) {
log_err("out of memory generating ns check, copydp");
fptr_ok(fptr_whitelist_modenv_kill_sub(
qstate->env->kill_sub));
(*qstate->env->kill_sub)(subq);
return;
}
}
}
static void
generate_dnskey_prefetch(struct module_qstate* qstate,
struct iter_qstate* iq, int id)
{
struct module_qstate* subq;
log_assert(iq->dp);
if(qstate->qinfo.qtype == LDNS_RR_TYPE_DNSKEY &&
query_dname_compare(iq->dp->name, qstate->qinfo.qname)==0 &&
(qstate->query_flags&BIT_RD) && !(qstate->query_flags&BIT_CD)){
return;
}
if(mesh_jostle_exceeded(qstate->env->mesh))
return;
log_nametypeclass(VERB_ALGO, "schedule dnskey prefetch",
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, 0)) {
verbose(VERB_ALGO, "could not generate dnskey prefetch");
return;
}
if(subq) {
struct iter_qstate* subiq =
(struct iter_qstate*)subq->minfo[id];
subiq->dp = delegpt_copy(iq->dp, subq->region);
}
}
static int
forward_request(struct module_qstate* qstate, struct iter_qstate* iq)
{
struct delegpt* dp;
uint8_t* delname = iq->qchase.qname;
size_t delnamelen = iq->qchase.qname_len;
int nolock = 0;
if(iq->refetch_glue && iq->dp) {
delname = iq->dp->name;
delnamelen = iq->dp->namelen;
}
if( (iq->qchase.qtype == LDNS_RR_TYPE_DS || iq->refetch_glue)
&& !dname_is_root(iq->qchase.qname))
dname_remove_label(&delname, &delnamelen);
dp = forwards_lookup(qstate->env->fwds, delname, iq->qchase.qclass,
nolock);
if(!dp) return 0;
iq->chase_flags |= BIT_RD;
iq->dp = delegpt_copy(dp, qstate->region);
lock_rw_unlock(&qstate->env->fwds->lock);
verbose(VERB_ALGO, "forwarding request");
return 1;
}
static int
processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
struct iter_env* ie, int id)
{
uint8_t dpname_storage[LDNS_MAX_DOMAINLEN+1];
uint8_t* delname, *dpname=NULL;
size_t delnamelen, dpnamelen=0;
struct dns_msg* msg = NULL;
log_query_info(VERB_DETAIL, "resolving", &qstate->qinfo);
if(iq->query_restart_count > ie->max_query_restarts) {
verbose(VERB_QUERY, "request has exceeded the maximum number"
" of query restarts with %d", iq->query_restart_count);
errinf(qstate, "request has exceeded the maximum number "
"restarts (eg. indirections)");
if(iq->qchase.qname)
errinf_dname(qstate, "stop at", iq->qchase.qname);
return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
}
verbose(VERB_ALGO, "request has dependency depth of %d", iq->depth);
if(iq->depth > ie->max_dependency_depth) {
verbose(VERB_QUERY, "request has exceeded the maximum "
"dependency depth with depth of %d", iq->depth);
errinf(qstate, "request has exceeded the maximum dependency "
"depth (eg. nameserver lookup recursion)");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
if(qstate->qinfo.qclass == LDNS_RR_CLASS_ANY) {
iq->qchase.qclass = 0;
return next_state(iq, COLLECT_CLASS_STATE);
}
if (iq->refetch_glue &&
iq->dp &&
!can_have_last_resort(qstate->env, iq->dp->name,
iq->dp->namelen, iq->qchase.qclass, NULL, NULL, NULL)) {
iq->refetch_glue = 0;
}
if(qstate->env->auth_zones) {
struct dns_msg* forged_response =
rpz_callback_from_iterator_cname(qstate, iq);
if(forged_response) {
uint8_t* sname = 0;
size_t slen = 0;
int count = 0;
while(forged_response && reply_find_rrset_section_an(
forged_response->rep, iq->qchase.qname,
iq->qchase.qname_len, LDNS_RR_TYPE_CNAME,
iq->qchase.qclass) &&
iq->qchase.qtype != LDNS_RR_TYPE_CNAME &&
count++ < ie->max_query_restarts) {
if(!handle_cname_response(qstate, iq, forged_response,
&sname, &slen)) {
errinf(qstate, "malloc failure, CNAME info");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
iq->qchase.qname = sname;
iq->qchase.qname_len = slen;
forged_response =
rpz_callback_from_iterator_cname(qstate, iq);
}
if(forged_response != NULL) {
qstate->ext_state[id] = module_finished;
qstate->return_rcode = LDNS_RCODE_NOERROR;
qstate->return_msg = forged_response;
iq->response = forged_response;
next_state(iq, FINISHED_STATE);
if(!iter_prepend(iq, qstate->return_msg, qstate->region)) {
log_err("rpz: after cached cname, prepend rrsets: out of memory");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
qstate->return_msg->qinfo = qstate->qinfo;
return 0;
}
iq->dp = NULL;
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;
return next_state(iq, INIT_REQUEST_STATE);
}
}
if (iter_stub_fwd_no_cache(qstate, &iq->qchase, &dpname, &dpnamelen,
dpname_storage, sizeof(dpname_storage))) {
verbose(VERB_ALGO, "no-cache set, going to the network");
qstate->no_cache_lookup = 1;
qstate->no_cache_store = 1;
msg = NULL;
} else if(qstate->blacklist) {
verbose(VERB_ALGO, "cache blacklisted, going to the network");
msg = NULL;
} else if(!qstate->no_cache_lookup) {
msg = dns_cache_lookup(qstate->env, iq->qchase.qname,
iq->qchase.qname_len, iq->qchase.qtype,
iq->qchase.qclass, qstate->query_flags,
qstate->region, qstate->env->scratch, 0, dpname,
dpnamelen);
if(!msg && qstate->env->neg_cache &&
iter_qname_indicates_dnssec(qstate->env, &iq->qchase)) {
msg = val_neg_getmsg(qstate->env->neg_cache, &iq->qchase,
qstate->region, qstate->env->rrset_cache,
qstate->env->scratch_buffer,
*qstate->env->now, 1, NULL,
qstate->env->cfg);
}
if(msg && query_dname_compare(qstate->qinfo.qname,
iq->qchase.qname) != 0)
msg->rep->security = sec_status_unchecked;
}
if(msg) {
enum response_type type = response_type_from_cache(msg,
&iq->qchase);
if(verbosity >= VERB_ALGO) {
log_dns_msg("msg from cache lookup", &msg->qinfo,
msg->rep);
verbose(VERB_ALGO, "msg ttl is %d, prefetch ttl %d",
(int)msg->rep->ttl,
(int)msg->rep->prefetch_ttl);
}
if(type == RESPONSE_TYPE_CNAME) {
uint8_t* sname = 0;
size_t slen = 0;
verbose(VERB_ALGO, "returning CNAME response from "
"cache");
if(!handle_cname_response(qstate, iq, msg,
&sname, &slen)) {
errinf(qstate, "failed to prepend CNAME "
"components, malloc failure");
return error_response(qstate, id,
LDNS_RCODE_SERVFAIL);
}
iq->qchase.qname = sname;
iq->qchase.qname_len = slen;
iq->dp = NULL;
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;
return next_state(iq, INIT_REQUEST_STATE);
}
if(qstate->reply_origin)
sock_list_insert(&qstate->reply_origin, NULL, 0, qstate->region);
if(FLAGS_GET_RCODE(msg->rep->flags) == LDNS_RCODE_SERVFAIL)
errinf(qstate, "SERVFAIL in cache");
verbose(VERB_ALGO, "returning answer from cache.");
iq->response = msg;
return final_state(iq);
}
if(forward_request(qstate, iq))
{
if(!iq->dp) {
log_err("alloc failure for forward dp");
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, 0)) {
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) {
verbose(VERB_ALGO, "cannot forward RD=0 query, to stop query loops");
errinf(qstate, "cannot forward RD=0 query");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
iq->refetch_glue = 0;
iq->minimisation_state = DONOT_MINIMISE_STATE;
return next_state(iq, QUERYTARGETS_STATE);
}
if(iq->refetch_glue) {
if(!iq->dp) {
log_err("internal or malloc fail: no dp for refetch");
errinf(qstate, "malloc failure, for delegation info");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
delname = iq->dp->name;
delnamelen = iq->dp->namelen;
} else {
delname = iq->qchase.qname;
delnamelen = iq->qchase.qname_len;
}
if(iq->qchase.qtype == LDNS_RR_TYPE_DS || iq->refetch_glue ||
(iq->qchase.qtype == LDNS_RR_TYPE_NS && qstate->prefetch_leeway
&& can_have_last_resort(qstate->env, delname, delnamelen, iq->qchase.qclass, NULL, NULL, NULL))) {
if(dname_is_root(delname) && (iq->refetch_glue ||
(iq->qchase.qtype == LDNS_RR_TYPE_NS &&
qstate->prefetch_leeway)))
delname = NULL;
else dname_remove_label(&delname, &delnamelen);
}
while(1) {
if(delname)
iq->dp = dns_cache_find_delegation(qstate->env, delname,
delnamelen, iq->qchase.qtype, iq->qchase.qclass,
qstate->region, &iq->deleg_msg,
*qstate->env->now+qstate->prefetch_leeway, 1,
dpname, dpnamelen);
else iq->dp = NULL;
if(iq->dp == NULL) {
int r;
int nolock = 0;
if(!auth_zone_delegpt(qstate, iq, delname, delnamelen))
return error_response(qstate, id,
LDNS_RCODE_SERVFAIL);
if(iq->dp)
return next_state(iq, INIT_REQUEST_2_STATE);
r = prime_stub(qstate, iq, id, delname,
iq->qchase.qclass);
if(r == 2)
break;
else if(r)
return 0;
if(forwards_lookup_root(qstate->env->fwds,
iq->qchase.qclass, nolock)) {
lock_rw_unlock(&qstate->env->fwds->lock);
iq->dp = hints_find_root(qstate->env->hints,
iq->qchase.qclass, nolock);
if(!iq->dp) {
log_err("internal error: no hints dp");
errinf(qstate, "no hints for this class");
return error_response_cache(qstate, id,
LDNS_RCODE_SERVFAIL);
}
iq->dp = delegpt_copy(iq->dp, qstate->region);
lock_rw_unlock(&qstate->env->hints->lock);
if(!iq->dp) {
log_err("out of memory in safety belt");
errinf(qstate, "malloc failure, in safety belt");
return error_response(qstate, id,
LDNS_RCODE_SERVFAIL);
}
return next_state(iq, INIT_REQUEST_2_STATE);
}
if(!prime_root(qstate, iq, id, iq->qchase.qclass))
return error_response(qstate, id,
LDNS_RCODE_REFUSED);
return 0;
}
if(!iq->ratelimit_ok && qstate->prefetch_leeway)
iq->ratelimit_ok = 1;
if(iter_dp_is_useless(&qstate->qinfo, qstate->query_flags,
iq->dp, ie->supports_ipv4, ie->supports_ipv6,
ie->nat64.use_nat64)) {
int have_dp = 0;
if(!can_have_last_resort(qstate->env, iq->dp->name, iq->dp->namelen, iq->qchase.qclass, &have_dp, &iq->dp, qstate->region)) {
if(have_dp) {
verbose(VERB_QUERY, "cache has stub "
"or fwd but no addresses, "
"fallback to config");
if(have_dp && !iq->dp) {
log_err("out of memory in "
"stub/fwd fallback");
errinf(qstate, "malloc failure, for fallback to config");
return error_response(qstate,
id, LDNS_RCODE_SERVFAIL);
}
break;
}
verbose(VERB_ALGO, "useless dp "
"but cannot go up, servfail");
delegpt_log(VERB_ALGO, iq->dp);
errinf(qstate, "no useful nameservers, "
"and cannot go up");
errinf_dname(qstate, "for zone", iq->dp->name);
return error_response(qstate, id,
LDNS_RCODE_SERVFAIL);
}
if(dname_is_root(iq->dp->name)) {
int nolock = 0;
verbose(VERB_QUERY, "Cache has root NS but "
"no addresses. Fallback to the safety belt.");
iq->dp = hints_find_root(qstate->env->hints,
iq->qchase.qclass, nolock);
if(!iq->dp) {
log_err("internal error: no hints dp");
return error_response(qstate, id,
LDNS_RCODE_REFUSED);
}
iq->dp = delegpt_copy(iq->dp, qstate->region);
lock_rw_unlock(&qstate->env->hints->lock);
if(!iq->dp) {
log_err("out of memory in safety belt");
errinf(qstate, "malloc failure, in safety belt, for root");
return error_response(qstate, id,
LDNS_RCODE_SERVFAIL);
}
break;
} else {
verbose(VERB_ALGO,
"cache delegation was useless:");
delegpt_log(VERB_ALGO, iq->dp);
delname = iq->dp->name;
delnamelen = iq->dp->namelen;
dname_remove_label(&delname, &delnamelen);
}
} else break;
}
verbose(VERB_ALGO, "cache delegation returns delegpt");
delegpt_log(VERB_ALGO, iq->dp);
return next_state(iq, INIT_REQUEST_2_STATE);
}
static int
processInitRequest2(struct module_qstate* qstate, struct iter_qstate* iq,
int id)
{
uint8_t* delname;
size_t delnamelen;
log_query_info(VERB_QUERY, "resolving (init part 2): ",
&qstate->qinfo);
delname = iq->qchase.qname;
delnamelen = iq->qchase.qname_len;
if(iq->refetch_glue) {
struct iter_hints_stub* stub;
int nolock = 0;
if(!iq->dp) {
log_err("internal or malloc fail: no dp for refetch");
errinf(qstate, "malloc failure, no delegation info");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
stub = hints_lookup_stub(
qstate->env->hints, iq->qchase.qname, iq->qchase.qclass,
iq->dp, nolock);
if(!stub || !stub->dp->has_parent_side_NS ||
dname_subdomain_c(iq->dp->name, stub->dp->name)) {
delname = iq->dp->name;
delnamelen = iq->dp->namelen;
}
if(stub) { lock_rw_unlock(&qstate->env->hints->lock); }
}
if(iq->qchase.qtype == LDNS_RR_TYPE_DS || iq->refetch_glue) {
if(!dname_is_root(delname))
dname_remove_label(&delname, &delnamelen);
iq->refetch_glue = 0;
}
if(!auth_zone_delegpt(qstate, iq, delname, delnamelen))
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
if(prime_stub(qstate, iq, id, delname, iq->qchase.qclass)) {
return 0;
}
return next_state(iq, INIT_REQUEST_3_STATE);
}
static int
processInitRequest3(struct module_qstate* qstate, struct iter_qstate* iq,
int id)
{
log_query_info(VERB_QUERY, "resolving (init part 3): ",
&qstate->qinfo);
iq->dnssec_expected = iter_indicates_dnssec(qstate->env, iq->dp,
iq->deleg_msg, iq->qchase.qclass);
if(!(qstate->query_flags & BIT_RD) && iq->deleg_msg) {
iq->response = iq->deleg_msg;
if(verbosity >= VERB_ALGO && iq->response)
log_dns_msg("no RD requested, using delegation msg",
&iq->response->qinfo, iq->response->rep);
if(qstate->reply_origin)
sock_list_insert(&qstate->reply_origin, NULL, 0, qstate->region);
return final_state(iq);
}
iq->chase_flags &= ~BIT_RD;
if(iq->dnssec_expected && qstate->env->cfg->prefetch_key &&
!(qstate->query_flags&BIT_CD)) {
generate_dnskey_prefetch(qstate, iq, id);
fptr_ok(fptr_whitelist_modenv_detach_subs(
qstate->env->detach_subs));
(*qstate->env->detach_subs)(qstate);
}
return next_state(iq, QUERYTARGETS_STATE);
}
static int
generate_parentside_target_query(struct module_qstate* qstate,
struct iter_qstate* iq, int id, uint8_t* name, size_t namelen,
uint16_t qtype, uint16_t qclass)
{
struct module_qstate* subq;
if(!generate_sub_request(name, namelen, qtype, qclass, qstate,
id, iq, INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0, 0))
return 0;
if(subq) {
struct iter_qstate* subiq =
(struct iter_qstate*)subq->minfo[id];
sock_list_insert(&subq->blacklist, NULL, 0, subq->region);
subiq->query_for_pside_glue = 1;
if(dname_subdomain_c(name, iq->dp->name)) {
subiq->dp = delegpt_copy(iq->dp, subq->region);
subiq->dnssec_expected = iter_indicates_dnssec(
qstate->env, subiq->dp, NULL,
subq->qinfo.qclass);
subiq->refetch_glue = 1;
} else {
subiq->dp = dns_cache_find_delegation(qstate->env,
name, namelen, qtype, qclass, subq->region,
&subiq->deleg_msg,
*qstate->env->now+subq->prefetch_leeway,
1, NULL, 0);
if(subiq->dp) {
subiq->dnssec_expected = iter_indicates_dnssec(
qstate->env, subiq->dp, NULL,
subq->qinfo.qclass);
subiq->refetch_glue = 1;
}
}
}
log_nametypeclass(VERB_QUERY, "new pside target", name, qtype, qclass);
return 1;
}
static int
generate_target_query(struct module_qstate* qstate, struct iter_qstate* iq,
int id, uint8_t* name, size_t namelen, uint16_t qtype, uint16_t qclass)
{
struct module_qstate* subq;
if(!generate_sub_request(name, namelen, qtype, qclass, qstate,
id, iq, INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0, 0))
return 0;
log_nametypeclass(VERB_QUERY, "new target", name, qtype, qclass);
return 1;
}
static int
query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq,
struct iter_env* ie, int id, int maxtargets, int* num)
{
int query_count = 0;
struct delegpt_ns* ns;
int missing;
int toget = 0;
iter_mark_cycle_targets(qstate, iq->dp);
missing = (int)delegpt_count_missing_targets(iq->dp, NULL);
log_assert(maxtargets != 0);
if(maxtargets < 0 || maxtargets > missing)
toget = missing;
else toget = maxtargets;
if(toget == 0) {
*num = 0;
return 0;
}
if(iq->depth == ie->max_dependency_depth)
return 1;
if(iq->depth > 0 && iq->target_count &&
iq->target_count[TARGET_COUNT_QUERIES] > MAX_TARGET_COUNT) {
char s[LDNS_MAX_DOMAINLEN];
dname_str(qstate->qinfo.qname, s);
verbose(VERB_QUERY, "request %s has exceeded the maximum "
"number of glue fetches %d", s,
iq->target_count[TARGET_COUNT_QUERIES]);
return 2;
}
if(iq->dp_target_count > MAX_DP_TARGET_COUNT) {
char s[LDNS_MAX_DOMAINLEN];
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 2;
}
log_assert(toget <= missing);
for(ns = iq->dp->nslist; ns; ns = ns->next) {
if(ns->resolved)
continue;
if(!iter_ns_probability(qstate->env->rnd, toget, missing)) {
missing --;
continue;
}
if(ie->supports_ipv6 &&
((ns->lame && !ns->done_pside6) ||
(!ns->lame && !ns->got6))) {
if(!generate_target_query(qstate, iq, id,
ns->name, ns->namelen,
LDNS_RR_TYPE_AAAA, iq->qchase.qclass)) {
*num = query_count;
if(query_count > 0)
qstate->ext_state[id] = module_wait_subquery;
return 1;
}
query_count++;
if(mesh_jostle_exceeded(qstate->env->mesh)) {
if(!((ie->supports_ipv4 || ie->nat64.use_nat64) &&
((ns->lame && !ns->done_pside4) ||
(!ns->lame && !ns->got4)))) {
ns->resolved = 1;
}
break;
}
}
if((ie->supports_ipv4 || ie->nat64.use_nat64) &&
((ns->lame && !ns->done_pside4) ||
(!ns->lame && !ns->got4))) {
if(!generate_target_query(qstate, iq, id,
ns->name, ns->namelen,
LDNS_RR_TYPE_A, iq->qchase.qclass)) {
*num = query_count;
if(query_count > 0)
qstate->ext_state[id] = module_wait_subquery;
return 1;
}
query_count++;
if(mesh_jostle_exceeded(qstate->env->mesh)) {
ns->resolved = 1;
break;
}
}
ns->resolved = 1;
missing--;
toget--;
if(toget == 0)
break;
}
*num = query_count;
if(query_count > 0)
qstate->ext_state[id] = module_wait_subquery;
return 0;
}
static int
processLastResort(struct module_qstate* qstate, struct iter_qstate* iq,
struct iter_env* ie, int id)
{
struct delegpt_ns* ns;
int query_count = 0;
verbose(VERB_ALGO, "No more query targets, attempting last resort");
log_assert(iq->dp);
if(!can_have_last_resort(qstate->env, iq->dp->name, iq->dp->namelen,
iq->qchase.qclass, NULL, NULL, NULL)) {
errinf(qstate, "all the configured stub or forward servers failed,");
errinf_dname(qstate, "at zone", iq->dp->name);
errinf_reply(qstate, iq);
verbose(VERB_QUERY, "configured stub or forward servers failed -- returning SERVFAIL");
return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
}
iq->dp->fallback_to_parent_side_NS = 1;
if(qstate->env->cfg->harden_unverified_glue) {
if(!cache_fill_missing(qstate->env, iq->qchase.qclass,
qstate->region, iq->dp, PACKED_RRSET_UNVERIFIED_GLUE))
log_err("out of memory in cache_fill_missing");
if(iq->dp->usable_list) {
verbose(VERB_ALGO, "try unverified glue from cache");
return next_state(iq, QUERYTARGETS_STATE);
}
}
if(!iq->dp->has_parent_side_NS && dname_is_root(iq->dp->name)) {
struct delegpt* dp;
int nolock = 0;
dp = hints_find_root(qstate->env->hints,
iq->qchase.qclass, nolock);
if(dp) {
struct delegpt_addr* a;
iq->chase_flags &= ~BIT_RD;
for(ns = dp->nslist; ns; ns=ns->next) {
(void)delegpt_add_ns(iq->dp, qstate->region,
ns->name, ns->lame, ns->tls_auth_name,
ns->port);
}
for(a = dp->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, -1, NULL);
}
lock_rw_unlock(&qstate->env->hints->lock);
iq->dp->tcp_upstream = dp->tcp_upstream;
iq->dp->ssl_upstream = dp->ssl_upstream;
}
iq->dp->has_parent_side_NS = 1;
} else if(!iq->dp->has_parent_side_NS) {
if(!iter_lookup_parent_NS_from_cache(qstate->env, iq->dp,
qstate->region, &qstate->qinfo)
|| !iq->dp->has_parent_side_NS) {
verbose(VERB_ALGO, "try to grab parent NS");
iq->store_parent_NS = iq->dp;
iq->chase_flags &= ~BIT_RD;
iq->deleg_msg = NULL;
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);
}
}
if(!cache_fill_missing(qstate->env, iq->qchase.qclass,
qstate->region, iq->dp, 0))
log_err("out of memory in cache_fill_missing");
if(iq->dp->usable_list) {
verbose(VERB_ALGO, "try parent-side-name, w. glue from cache");
return next_state(iq, QUERYTARGETS_STATE);
}
if(iter_lookup_parent_glue_from_cache(qstate->env, iq->dp,
qstate->region, &qstate->qinfo)) {
verbose(VERB_ALGO, "try parent-side glue from cache");
return next_state(iq, QUERYTARGETS_STATE);
}
if(delegpt_count_missing_targets(iq->dp, NULL) > 0) {
int qs = 0, ret;
verbose(VERB_ALGO, "try parent-side target name");
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);
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);
if(qs != 0) {
qstate->ext_state[id] = module_wait_subquery;
return 0;
}
}
if(iq->depth == ie->max_dependency_depth) {
verbose(VERB_QUERY, "maxdepth and need more nameservers, fail");
errinf(qstate, "cannot fetch more nameservers because at max dependency depth");
return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
}
if(iq->depth > 0 && iq->target_count &&
iq->target_count[TARGET_COUNT_QUERIES] > MAX_TARGET_COUNT) {
char s[LDNS_MAX_DOMAINLEN];
dname_str(qstate->qinfo.qname, s);
verbose(VERB_QUERY, "request %s has exceeded the maximum "
"number of glue fetches %d", s,
iq->target_count[TARGET_COUNT_QUERIES]);
errinf(qstate, "exceeded the maximum number of glue fetches");
return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
}
iter_mark_pside_cycle_targets(qstate, iq->dp);
for(ns = iq->dp->nslist; ns; ns = ns->next) {
if( ((ie->supports_ipv6 && !ns->done_pside6) ||
((ie->supports_ipv4 || ie->nat64.use_nat64) && !ns->done_pside4)) &&
!can_have_last_resort(qstate->env, ns->name, ns->namelen,
iq->qchase.qclass, NULL, NULL, NULL)) {
log_nametypeclass(VERB_ALGO, "cannot pside lookup ns "
"because it is also a stub/forward,",
ns->name, LDNS_RR_TYPE_NS, iq->qchase.qclass);
if(ie->supports_ipv6) ns->done_pside6 = 1;
if(ie->supports_ipv4 || ie->nat64.use_nat64) ns->done_pside4 = 1;
continue;
}
if(ie->supports_ipv6 && !ns->done_pside6) {
if(!generate_parentside_target_query(qstate, iq, id,
ns->name, ns->namelen,
LDNS_RR_TYPE_AAAA, iq->qchase.qclass)) {
errinf_dname(qstate, "could not generate nameserver AAAA lookup for", ns->name);
return error_response(qstate, id,
LDNS_RCODE_SERVFAIL);
}
ns->done_pside6 = 1;
query_count++;
if(mesh_jostle_exceeded(qstate->env->mesh)) {
verbose(VERB_ALGO, "try parent-side glue lookup");
iq->num_target_queries += query_count;
target_count_increase(iq, query_count);
qstate->ext_state[id] = module_wait_subquery;
return 0;
}
}
if((ie->supports_ipv4 || ie->nat64.use_nat64) && !ns->done_pside4) {
if(!generate_parentside_target_query(qstate, iq, id,
ns->name, ns->namelen,
LDNS_RR_TYPE_A, iq->qchase.qclass)) {
errinf_dname(qstate, "could not generate nameserver A lookup for", ns->name);
return error_response(qstate, id,
LDNS_RCODE_SERVFAIL);
}
ns->done_pside4 = 1;
query_count++;
}
if(query_count != 0) {
verbose(VERB_ALGO, "try parent-side glue lookup");
iq->num_target_queries += query_count;
target_count_increase(iq, query_count);
qstate->ext_state[id] = module_wait_subquery;
return 0;
}
}
if(!qstate->no_cache_store && iq->query_for_pside_glue
&& !iq->pside_glue)
iter_store_parentside_neg(qstate->env, &qstate->qinfo,
iq->deleg_msg?iq->deleg_msg->rep:
(iq->response?iq->response->rep:NULL));
errinf(qstate, "all servers for this domain failed,");
errinf_dname(qstate, "at zone", iq->dp->name);
errinf_reply(qstate, iq);
verbose(VERB_QUERY, "out of query targets -- returning SERVFAIL");
return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
}
static int
processDSNSFind(struct module_qstate* qstate, struct iter_qstate* iq, int id)
{
struct module_qstate* subq = NULL;
verbose(VERB_ALGO, "processDSNSFind");
if(!iq->dsns_point) {
iq->dsns_point = iq->qchase.qname;
iq->dsns_point_len = iq->qchase.qname_len;
}
if(!dname_subdomain_c(iq->dsns_point, iq->dp->name)) {
errinf_dname(qstate, "for DS query parent-child nameserver search the query is not under the zone", iq->dp->name);
return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
}
dname_remove_label(&iq->dsns_point, &iq->dsns_point_len);
if(query_dname_compare(iq->dsns_point, iq->dp->name) == 0) {
iq->state = QUERYTARGETS_STATE;
return 1;
}
iq->state = DSNS_FIND_STATE;
log_nametypeclass(VERB_ALGO, "fetch nameservers",
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, 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);
}
return 0;
}
static void
check_waiting_queries(struct iter_qstate* iq, struct module_qstate* qstate,
int id)
{
if(iq->num_target_queries>0 && iq->num_current_queries>0) {
verbose(VERB_ALGO, "waiting for %d targets to "
"resolve or %d outstanding queries to "
"respond", iq->num_target_queries,
iq->num_current_queries);
qstate->ext_state[id] = module_wait_reply;
} else if(iq->num_target_queries>0) {
verbose(VERB_ALGO, "waiting for %d targets to "
"resolve", iq->num_target_queries);
qstate->ext_state[id] = module_wait_subquery;
} else {
verbose(VERB_ALGO, "waiting for %d "
"outstanding queries to respond",
iq->num_current_queries);
qstate->ext_state[id] = module_wait_reply;
}
}
static int
processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
struct iter_env* ie, int id)
{
int tf_policy;
struct delegpt_addr* target;
struct outbound_entry* outq;
struct sockaddr_storage real_addr;
socklen_t real_addrlen;
int auth_fallback = 0;
uint8_t* qout_orig = NULL;
size_t qout_orig_len = 0;
int sq_check_ratelimit = 1;
int sq_was_ratelimited = 0;
int can_do_promisc = 0;
log_query_info(VERB_QUERY, "processQueryTargets:", &qstate->qinfo);
verbose(VERB_ALGO, "processQueryTargets: targetqueries %d, "
"currentqueries %d sentcount %d", iq->num_target_queries,
iq->num_current_queries, iq->sent_count);
if(iq->referral_count > MAX_REFERRAL_COUNT) {
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_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_cache(qstate, id, LDNS_RCODE_SERVFAIL);
}
if(iq->target_count && !*iq->nxns_dp &&
iq->target_count[TARGET_COUNT_NX] > MAX_TARGET_NX) {
struct delegpt_ns* ns;
if(iq->num_target_queries>0 || iq->num_current_queries>0) {
check_waiting_queries(iq, qstate, id);
return 0;
}
verbose(VERB_ALGO, "request has exceeded the maximum "
"number of nxdomain nameserver lookups (%d) with %d",
MAX_TARGET_NX, iq->target_count[TARGET_COUNT_NX]);
if(!iq->dp) {
verbose(VERB_QUERY, "Failed to get a delegation, "
"giving up");
errinf(qstate, "failed to get a delegation (eg. prime "
"failure)");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
if(iq->dp->has_parent_side_NS) {
verbose(VERB_ALGO, "parent-side information is "
"already present for the delegation point, no "
"fallback possible");
errinf(qstate, "exceeded the maximum nameserver nxdomains");
return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
}
verbose(VERB_ALGO, "initiating parent-side fallback for "
"nxdomain nameserver lookups");
for(ns=iq->dp->nslist; ns; ns=ns->next) {
ns->resolved = 1;
}
*iq->nxns_dp = malloc(iq->dp->namelen);
if(!*iq->nxns_dp) {
verbose(VERB_ALGO, "out of memory while initiating "
"fallback");
errinf(qstate, "exceeded the maximum nameserver "
"nxdomains (malloc)");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
memcpy(*iq->nxns_dp, iq->dp->name, iq->dp->namelen);
} else if(iq->target_count && *iq->nxns_dp) {
if(iq->num_target_queries>0 || iq->num_current_queries>0) {
check_waiting_queries(iq, qstate, id);
return 0;
}
if(!iq->dp) {
verbose(VERB_QUERY, "Failed to get a delegation, "
"giving up");
errinf(qstate, "failed to get a delegation (eg. prime "
"failure)");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
if(iq->target_count[TARGET_COUNT_NX] > MAX_TARGET_NX_FALLBACK) {
verbose(VERB_ALGO, "request has exceeded the maximum "
"number of fallback nxdomain nameserver "
"lookups (%d) with %d", MAX_TARGET_NX_FALLBACK,
iq->target_count[TARGET_COUNT_NX]);
errinf(qstate, "exceeded the maximum nameserver nxdomains");
return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
}
if(!iq->dp->has_parent_side_NS) {
struct delegpt_ns* ns;
if(!dname_canonical_compare(*iq->nxns_dp, iq->dp->name)) {
verbose(VERB_ALGO, "this delegation point "
"initiated the fallback, marking the "
"nslist as resolved");
for(ns=iq->dp->nslist; ns; ns=ns->next) {
ns->resolved = 1;
}
}
}
}
if(!iq->dp) {
verbose(VERB_QUERY, "Failed to get a delegation, giving up");
errinf(qstate, "failed to get a delegation (eg. prime failure)");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
if(!ie->supports_ipv6)
delegpt_no_ipv6(iq->dp);
if(!ie->supports_ipv4 && !ie->nat64.use_nat64)
delegpt_no_ipv4(iq->dp);
delegpt_log(VERB_ALGO, iq->dp);
if(iq->num_current_queries>0) {
verbose(VERB_ALGO, "woke up, but wait for outstanding query");
qstate->ext_state[id] = module_wait_reply;
return 0;
}
if(iq->minimisation_state == INIT_MINIMISE_STATE
&& !(iq->chase_flags & BIT_RD)) {
if(!(iq->qinfo_out.qname_len
&& dname_subdomain_c(iq->qchase.qname,
iq->qinfo_out.qname)
&& dname_subdomain_c(iq->qinfo_out.qname,
iq->dp->name))) {
iq->qinfo_out.qname = iq->dp->name;
iq->qinfo_out.qname_len = iq->dp->namelen;
iq->qinfo_out.qtype = LDNS_RR_TYPE_A;
iq->qinfo_out.qclass = iq->qchase.qclass;
iq->qinfo_out.local_alias = NULL;
iq->minimise_count = 0;
}
iq->minimisation_state = MINIMISE_STATE;
}
if(iq->minimisation_state == MINIMISE_STATE) {
int qchaselabs = dname_count_labels(iq->qchase.qname);
int labdiff = qchaselabs -
dname_count_labels(iq->qinfo_out.qname);
qout_orig = iq->qinfo_out.qname;
qout_orig_len = iq->qinfo_out.qname_len;
iq->qinfo_out.qname = iq->qchase.qname;
iq->qinfo_out.qname_len = iq->qchase.qname_len;
iq->minimise_count++;
iq->timeout_count = 0;
iter_dec_attempts(iq->dp, 1, ie->outbound_msg_retry);
if(qchaselabs > MAX_MINIMISE_COUNT && labdiff > 1 &&
iq->minimise_count > MINIMISE_ONE_LAB) {
if(iq->minimise_count < MAX_MINIMISE_COUNT) {
int multilabs = qchaselabs - 1 -
MINIMISE_ONE_LAB;
int extralabs = multilabs /
MINIMISE_MULTIPLE_LABS;
if (MAX_MINIMISE_COUNT - iq->minimise_count >=
multilabs % MINIMISE_MULTIPLE_LABS)
extralabs--;
if (extralabs < labdiff)
labdiff -= extralabs;
else
labdiff = 1;
}
else
labdiff = 1;
}
if(labdiff > 1) {
verbose(VERB_QUERY, "removing %d labels", labdiff-1);
dname_remove_labels(&iq->qinfo_out.qname,
&iq->qinfo_out.qname_len,
labdiff-1);
}
if(labdiff < 1 || (labdiff < 2
&& (iq->qchase.qtype == LDNS_RR_TYPE_DS
|| iq->qchase.qtype == LDNS_RR_TYPE_A)))
iq->minimisation_state = DONOT_MINIMISE_STATE;
else if(!qstate->no_cache_lookup) {
struct dns_msg* msg = dns_cache_lookup(qstate->env,
iq->qinfo_out.qname, iq->qinfo_out.qname_len,
iq->qinfo_out.qtype, iq->qinfo_out.qclass,
qstate->query_flags, qstate->region,
qstate->env->scratch, 0, iq->dp->name,
iq->dp->namelen);
if(msg && FLAGS_GET_RCODE(msg->rep->flags) ==
LDNS_RCODE_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 1;
}
}
}
if(iq->minimisation_state == SKIP_MINIMISE_STATE) {
if(iq->timeout_count < MAX_MINIMISE_TIMEOUT_COUNT)
iq->minimisation_state = MINIMISE_STATE;
else if(!qstate->env->cfg->qname_minimisation_strict)
iq->minimisation_state = DONOT_MINIMISE_STATE;
}
if(iq->minimisation_state == DONOT_MINIMISE_STATE)
iq->qinfo_out = iq->qchase;
if(!iq->auth_zone_avoid && qstate->blacklist) {
if(auth_zones_can_fallback(qstate->env->auth_zones,
iq->dp->name, iq->dp->namelen, iq->qinfo_out.qclass)) {
iq->auth_zone_avoid = 1;
}
}
if(iq->auth_zone_avoid) {
iq->auth_zone_avoid = 0;
auth_fallback = 1;
} else if(auth_zones_lookup(qstate->env->auth_zones, &iq->qinfo_out,
qstate->region, &iq->response, &auth_fallback, iq->dp->name,
iq->dp->namelen)) {
if(verbosity >= VERB_ALGO) {
log_dns_msg("msg from auth zone",
&iq->response->qinfo, iq->response->rep);
}
if((iq->chase_flags&BIT_RD) && !(iq->response->rep->flags&BIT_AA)) {
verbose(VERB_ALGO, "forwarder, ignoring referral from auth zone");
} else {
qstate->env->mesh->num_query_authzone_up++;
iq->num_current_queries++;
iq->chase_to_rd = 0;
iq->dnssec_lame_query = 0;
iq->auth_zone_response = 1;
return next_state(iq, QUERY_RESP_STATE);
}
}
iq->auth_zone_response = 0;
if(auth_fallback == 0) {
verbose(VERB_ALGO, "auth zone lookup failed, no fallback,"
" servfail");
errinf(qstate, "auth zone lookup failed, fallback is off");
return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
}
if(iq->dp->auth_dp) {
iq->auth_zone_avoid = 1;
return next_state(iq, INIT_REQUEST_STATE);
}
tf_policy = 0;
if(iq->depth < ie->max_dependency_depth
&& iq->num_target_queries == 0
&& (!iq->target_count || iq->target_count[TARGET_COUNT_NX]==0)
&& iq->sent_count < TARGET_FETCH_STOP) {
can_do_promisc = 1;
}
if(!iq->dp->fallback_to_parent_side_NS && can_do_promisc
&& !mesh_jostle_exceeded(qstate->env->mesh)) {
tf_policy = ie->target_fetch_policy[iq->depth];
}
if(iq->caps_fallback) {
int extra = 0, ret;
size_t naddr, nres, navail;
if((ret=query_for_targets(qstate, iq, ie, id, -1, &extra))!=0) {
errinf(qstate, "could not fetch nameservers for 0x20 fallback");
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);
if(iq->num_target_queries > 0) {
verbose(VERB_ALGO, "wait for all targets for fallback");
qstate->ext_state[id] = module_wait_reply;
if(qout_orig && iq->minimise_count > 0) {
iq->minimise_count--;
iq->qinfo_out.qname = qout_orig;
iq->qinfo_out.qname_len = qout_orig_len;
}
return 0;
}
delegpt_count_addr(iq->dp, &naddr, &nres, &navail);
if(iq->caps_server+1 >= naddr*3 ||
iq->caps_server*2+2 >= (size_t)ie->max_sent_count) {
verbose(VERB_ALGO, "0x20 fallback had %d responses "
"match for %d wanted, done.",
(int)iq->caps_server+1, (int)naddr*3);
iq->response = iq->caps_response;
iq->caps_fallback = 0;
iter_dec_attempts(iq->dp, 3, ie->outbound_msg_retry);
iq->num_current_queries++;
iq->referral_count++;
iq->sent_count = 0;
iq->dp_target_count = 0;
iq->state = QUERY_RESP_STATE;
return 1;
}
verbose(VERB_ALGO, "0x20 fallback number %d",
(int)iq->caps_server);
} else if(tf_policy != 0) {
int extra = 0;
verbose(VERB_ALGO, "attempt to get extra %d targets",
tf_policy);
(void)query_for_targets(qstate, iq, ie, id, tf_policy, &extra);
iq->num_target_queries += extra;
target_count_increase(iq, extra);
}
delegpt_add_unused_targets(iq->dp);
if(qstate->env->auth_zones) {
uint8_t* sname = NULL;
size_t snamelen = 0;
struct dns_msg* forged_response_after_cname;
struct dns_msg* forged_response = rpz_callback_from_iterator_module(qstate, iq);
int count = 0;
while(forged_response && reply_find_rrset_section_an(
forged_response->rep, iq->qchase.qname,
iq->qchase.qname_len, LDNS_RR_TYPE_CNAME,
iq->qchase.qclass) &&
iq->qchase.qtype != LDNS_RR_TYPE_CNAME &&
count++ < ie->max_query_restarts) {
if(!handle_cname_response(qstate, iq, forged_response,
&sname, &snamelen)) {
errinf(qstate, "malloc failure, CNAME info");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
iq->qchase.qname = sname;
iq->qchase.qname_len = snamelen;
forged_response_after_cname =
rpz_callback_from_iterator_cname(qstate, iq);
if(forged_response_after_cname) {
forged_response = forged_response_after_cname;
} else {
iq->deleg_msg = NULL;
iq->dp = NULL;
iq->dsns_point = NULL;
iq->auth_zone_response = 0;
iq->refetch_glue = 0;
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;
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, INIT_REQUEST_STATE);
}
}
if(forged_response != NULL) {
qstate->ext_state[id] = module_finished;
qstate->return_rcode = LDNS_RCODE_NOERROR;
qstate->return_msg = forged_response;
iq->response = forged_response;
next_state(iq, FINISHED_STATE);
if(!iter_prepend(iq, qstate->return_msg, qstate->region)) {
log_err("rpz: prepend rrsets: out of memory");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
return 0;
}
}
target = iter_server_selection(ie, qstate->env, iq->dp,
iq->dp->name, iq->dp->namelen, iq->qchase.qtype,
&iq->dnssec_lame_query, &iq->chase_to_rd,
iq->num_target_queries, qstate->blacklist,
qstate->prefetch_leeway);
if(!target) {
if(iq->num_target_queries==0 && iq->num_current_queries==0) {
if(delegpt_count_missing_targets(iq->dp, NULL) > 0) {
int qs = 0, ret;
verbose(VERB_ALGO, "querying for next "
"missing target");
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);
if(ret == 1)
return error_response(qstate, id,
LDNS_RCODE_SERVFAIL);
return error_response_cache(qstate, id,
LDNS_RCODE_SERVFAIL);
}
if(qs == 0 &&
delegpt_count_missing_targets(iq->dp, NULL) == 0){
return 1;
}
if(qs == 0) {
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);
}
if(iq->num_target_queries == 0) {
if(iq->caps_fallback && iq->caps_reply) {
verbose(VERB_ALGO, "0x20 fallback had %d responses, "
"but no more servers except "
"last resort, done.",
(int)iq->caps_server+1);
iq->response = iq->caps_response;
iq->caps_fallback = 0;
iter_dec_attempts(iq->dp, 3, ie->outbound_msg_retry);
iq->num_current_queries++;
iq->referral_count++;
iq->sent_count = 0;
iq->dp_target_count = 0;
iq->state = QUERY_RESP_STATE;
return 1;
}
return processLastResort(qstate, iq, ie, id);
}
}
verbose(VERB_ALGO, "no current targets");
check_waiting_queries(iq, qstate, id);
if(qout_orig && iq->minimise_count > 0) {
iq->minimise_count--;
iq->qinfo_out.qname = qout_orig;
iq->qinfo_out.qname_len = qout_orig_len;
}
return 0;
}
if(can_do_promisc && tf_policy == 0 && iq->depth == 0
&& iq->depth < ie->max_dependency_depth
&& ie->target_fetch_policy[iq->depth] != 0
&& iq->dp_target_count == 0
&& !ub_random_max(qstate->env->rnd, 10)) {
int extra = 0;
verbose(VERB_ALGO, "available target exists in cache but "
"attempt to get extra 1 target");
(void)query_for_targets(qstate, iq, ie, id, 1, &extra);
if(extra > 0) {
iq->num_target_queries += extra;
target_count_increase(iq, extra);
check_waiting_queries(iq, qstate, id);
if(qout_orig && iq->minimise_count > 0) {
iq->minimise_count--;
iq->qinfo_out.qname = qout_orig;
iq->qinfo_out.qname_len = qout_orig_len;
}
return 0;
}
}
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];
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);
}
sq_check_ratelimit = (!(iq->chase_flags & BIT_RD) && !iq->ratelimit_ok);
if(verbosity >= VERB_QUERY) {
log_query_info(VERB_QUERY, "sending query:", &iq->qinfo_out);
log_name_addr(VERB_QUERY, "sending to target:", iq->dp->name,
&target->addr, target->addrlen);
verbose(VERB_ALGO, "dnssec status: %s%s",
iq->dnssec_expected?"expected": "not expected",
iq->dnssec_lame_query?" but lame_query anyway": "");
}
real_addr = target->addr;
real_addrlen = target->addrlen;
if(ie->nat64.use_nat64 && target->addr.ss_family == AF_INET) {
addr_to_nat64(&target->addr, &ie->nat64.nat64_prefix_addr,
ie->nat64.nat64_prefix_addrlen, ie->nat64.nat64_prefix_net,
&real_addr, &real_addrlen);
log_name_addr(VERB_QUERY, "applied NAT64:",
iq->dp->name, &real_addr, real_addrlen);
}
fptr_ok(fptr_whitelist_modenv_send_query(qstate->env->send_query));
outq = (*qstate->env->send_query)(&iq->qinfo_out,
iq->chase_flags | (iq->chase_to_rd?BIT_RD:0),
(qstate->env->cfg->disable_edns_do?0:EDNS_DO)|
((iq->chase_to_rd||(iq->chase_flags&BIT_RD)!=0)&&
!qstate->blacklist&&(!iter_qname_indicates_dnssec(qstate->env,
&iq->qinfo_out)||target->attempts==1)?0:BIT_CD),
iq->dnssec_expected, iq->caps_fallback || is_caps_whitelisted(
ie, iq), sq_check_ratelimit, &real_addr, real_addrlen,
iq->dp->name, iq->dp->namelen,
(iq->dp->tcp_upstream || qstate->env->cfg->tcp_upstream),
(iq->dp->ssl_upstream || qstate->env->cfg->ssl_upstream),
target->tls_auth_name, qstate, &sq_was_ratelimited);
if(!outq) {
if(sq_was_ratelimited) {
lock_basic_lock(&ie->queries_ratelimit_lock);
ie->num_queries_ratelimited++;
lock_basic_unlock(&ie->queries_ratelimit_lock);
verbose(VERB_ALGO, "query exceeded ratelimits");
qstate->was_ratelimited = 1;
errinf_dname(qstate, "exceeded ratelimit for zone",
iq->dp->name);
return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
}
log_addr(VERB_QUERY, "error sending query to auth server",
&real_addr, real_addrlen);
if(qstate->env->cfg->qname_minimisation)
iq->minimisation_state = SKIP_MINIMISE_STATE;
return next_state(iq, QUERYTARGETS_STATE);
}
outbound_list_insert(&iq->outlist, outq);
iq->num_current_queries++;
iq->sent_count++;
qstate->ext_state[id] = module_wait_reply;
return 0;
}
static struct ub_packed_rrset_key*
find_NS(struct reply_info* rep, size_t from, size_t to)
{
size_t i;
for(i=from; i<to; i++) {
if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NS)
return rep->rrsets[i];
}
return NULL;
}
static int
processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
struct iter_env* ie, int id)
{
int dnsseclame = 0, origtypecname = 0, orig_empty_nodata_found;
enum response_type type;
iq->num_current_queries--;
if(!inplace_cb_query_response_call(qstate->env, qstate, iq->response))
log_err("unable to call query_response callback");
if(iq->response == NULL) {
if(qstate->env->cfg->qname_minimisation) {
iq->minimisation_state = SKIP_MINIMISE_STATE;
}
iq->timeout_count++;
iq->chase_to_rd = 0;
iq->dnssec_lame_query = 0;
verbose(VERB_ALGO, "query response was timeout");
return next_state(iq, QUERYTARGETS_STATE);
}
iq->timeout_count = 0;
orig_empty_nodata_found = iq->empty_nodata_found;
type = response_type_from_server(
(int)((iq->chase_flags&BIT_RD) || iq->chase_to_rd),
iq->response, &iq->qinfo_out, iq->dp, &iq->empty_nodata_found);
iq->chase_to_rd = 0;
iq->response->rep->flags &= ~BIT_TC;
if(orig_empty_nodata_found != iq->empty_nodata_found &&
iq->empty_nodata_found < EMPTY_NODATA_RETRY_COUNT) {
if(qstate->reply) {
struct delegpt_addr* a = delegpt_find_addr(
iq->dp, &qstate->reply->remote_addr,
qstate->reply->remote_addrlen);
if(a) a->lame = 1;
}
return next_state(iq, QUERYTARGETS_STATE);
}
if(type == RESPONSE_TYPE_REFERRAL && (iq->chase_flags&BIT_RD) &&
!iq->auth_zone_response) {
type = RESPONSE_TYPE_ANSWER;
}
if(!qstate->env->cfg->disable_dnssec_lame_check && iq->dnssec_expected
&& !iq->dnssec_lame_query &&
!(iq->chase_flags&BIT_RD)
&& iq->sent_count < DNSSEC_LAME_DETECT_COUNT
&& type != RESPONSE_TYPE_LAME
&& type != RESPONSE_TYPE_REC_LAME
&& type != RESPONSE_TYPE_THROWAWAY
&& type != RESPONSE_TYPE_UNTYPED) {
if(!iter_msg_has_dnssec(iq->response)) {
if(qstate->reply) {
struct delegpt_addr* a = delegpt_find_addr(
iq->dp, &qstate->reply->remote_addr,
qstate->reply->remote_addrlen);
if(a) a->dnsseclame = 1;
}
if(!iter_msg_from_zone(iq->response, iq->dp, type,
iq->qchase.qclass))
qstate->reply = NULL;
type = RESPONSE_TYPE_LAME;
dnsseclame = 1;
}
} else iq->dnssec_lame_query = 0;
if(type == RESPONSE_TYPE_REFERRAL) {
struct ub_packed_rrset_key* ns = find_NS(
iq->response->rep, iq->response->rep->an_numrrsets,
iq->response->rep->an_numrrsets
+ iq->response->rep->ns_numrrsets);
if(!ns) ns = find_NS(iq->response->rep, 0,
iq->response->rep->an_numrrsets);
if(!ns || !dname_strict_subdomain_c(ns->rk.dname, iq->dp->name)
|| !dname_subdomain_c(iq->qchase.qname, ns->rk.dname)){
verbose(VERB_ALGO, "bad referral, throwaway");
type = RESPONSE_TYPE_THROWAWAY;
} else
iter_scrub_ds(iq->response, ns, iq->dp->name);
} else iter_scrub_ds(iq->response, NULL, NULL);
if(type == RESPONSE_TYPE_THROWAWAY &&
FLAGS_GET_RCODE(iq->response->rep->flags) == LDNS_RCODE_YXDOMAIN) {
type = RESPONSE_TYPE_ANSWER;
}
if(type == RESPONSE_TYPE_CNAME)
origtypecname = 1;
if(type == RESPONSE_TYPE_CNAME && iq->response->rep->an_numrrsets >= 1
&& ntohs(iq->response->rep->rrsets[0]->rk.type) == LDNS_RR_TYPE_DNAME) {
uint8_t* sname = NULL;
size_t snamelen = 0;
get_cname_target(iq->response->rep->rrsets[0], &sname,
&snamelen);
if(snamelen && dname_subdomain_c(sname, iq->response->rep->rrsets[0]->rk.dname)) {
type = RESPONSE_TYPE_ANSWER;
}
}
if(type == RESPONSE_TYPE_CNAME &&
(iq->qchase.qtype == LDNS_RR_TYPE_CNAME ||
iq->qchase.qtype == LDNS_RR_TYPE_ANY) &&
iq->minimisation_state == MINIMISE_STATE &&
query_dname_compare(iq->qchase.qname, iq->qinfo_out.qname) == 0) {
type = RESPONSE_TYPE_ANSWER;
}
if(type == RESPONSE_TYPE_ANSWER) {
if(verbosity >= VERB_DETAIL) {
verbose(VERB_DETAIL, "query response was %s",
FLAGS_GET_RCODE(iq->response->rep->flags)
==LDNS_RCODE_NXDOMAIN?"NXDOMAIN ANSWER":
(iq->response->rep->an_numrrsets?"ANSWER":
"nodata ANSWER"));
}
if(iq->qchase.qtype == LDNS_RR_TYPE_DS && !iq->dsns_point
&& !(iq->chase_flags&BIT_RD)
&& iter_ds_toolow(iq->response, iq->dp)
&& iter_dp_cangodown(&iq->qchase, iq->dp)) {
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 processDSNSFind(qstate, iq, id);
}
if(iq->qchase.qtype == LDNS_RR_TYPE_DNSKEY && SERVE_EXPIRED
&& qstate->is_valrec &&
reply_find_answer_rrset(&iq->qchase, iq->response->rep) != NULL) {
verbose(VERB_ALGO, "make DNSKEY minimal for serve "
"expired");
iter_make_minimal(iq->response->rep);
}
if(!qstate->no_cache_store)
iter_dns_store(qstate->env, &iq->response->qinfo,
iq->response->rep,
iq->qchase.qtype != iq->response->qinfo.qtype,
qstate->prefetch_leeway,
iq->dp&&iq->dp->has_parent_side_NS,
qstate->region, qstate->query_flags,
qstate->qstarttime, qstate->is_valrec);
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;
if(qstate->reply)
sock_list_insert(&qstate->reply_origin,
&qstate->reply->remote_addr,
qstate->reply->remote_addrlen, qstate->region);
if(iq->minimisation_state != DONOT_MINIMISE_STATE
&& !(iq->chase_flags & BIT_RD)) {
if(FLAGS_GET_RCODE(iq->response->rep->flags) !=
LDNS_RCODE_NOERROR) {
if(qstate->env->cfg->qname_minimisation_strict) {
if(FLAGS_GET_RCODE(iq->response->rep->flags) ==
LDNS_RCODE_NXDOMAIN) {
iter_scrub_nxdomain(iq->response);
return final_state(iq);
}
return error_response_cache(qstate, id,
LDNS_RCODE_SERVFAIL);
}
iq->minimisation_state = DONOT_MINIMISE_STATE;
}
if(FLAGS_GET_RCODE(iq->response->rep->flags) ==
LDNS_RCODE_NXDOMAIN && !origtypecname) {
if(iq->dnssec_expected)
return final_state(iq);
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:",
&iq->response->qinfo);
if(!generate_sub_request(
iq->response->qinfo.qname,
iq->response->qinfo.qname_len,
iq->response->qinfo.qtype,
iq->response->qinfo.qclass,
qstate, id, iq,
INIT_REQUEST_STATE,
FINISHED_STATE, &subq, 1, 1))
verbose(VERB_ALGO,
"could not validate NXDOMAIN "
"response");
}
}
return next_state(iq, QUERYTARGETS_STATE);
}
return final_state(iq);
} else if(type == RESPONSE_TYPE_REFERRAL) {
struct delegpt* old_dp = NULL;
verbose(VERB_DETAIL, "query response was REFERRAL");
if(!qstate->no_cache_store &&
(!qstate->env->cfg->harden_referral_path ||
( qstate->qinfo.qtype == LDNS_RR_TYPE_NS
&& (qstate->query_flags&BIT_RD)
&& !(qstate->query_flags&BIT_CD)
&& (
reply_find_rrset_section_ns(iq->response->rep,
iq->qchase.qname, iq->qchase.qname_len,
LDNS_RR_TYPE_NS, iq->qchase.qclass)
|| reply_find_rrset_section_an(iq->response->rep,
iq->qchase.qname, iq->qchase.qname_len,
LDNS_RR_TYPE_NS, iq->qchase.qclass)
)
))) {
iter_dns_store(qstate->env, &iq->response->qinfo,
iq->response->rep, 1, 0, 0, NULL, 0,
qstate->qstarttime, qstate->is_valrec);
if(iq->store_parent_NS)
iter_store_parentside_NS(qstate->env,
iq->response->rep);
if(qstate->env->neg_cache)
val_neg_addreferral(qstate->env->neg_cache,
iq->response->rep, iq->dp->name);
}
if(!qstate->no_cache_store && iq->query_for_pside_glue
&& !iq->pside_glue) {
iq->pside_glue = reply_find_rrset(iq->response->rep,
iq->qchase.qname, iq->qchase.qname_len,
iq->qchase.qtype, iq->qchase.qclass);
if(iq->pside_glue) {
log_rrset_key(VERB_ALGO, "found parent-side "
"glue", iq->pside_glue);
iter_store_parentside_rrset(qstate->env,
iq->pside_glue);
}
}
iq->deleg_msg = iq->response;
old_dp = iq->dp;
iq->dp = delegpt_from_message(iq->response, qstate->region);
if (qstate->env->cfg->qname_minimisation)
iq->minimisation_state = INIT_MINIMISE_STATE;
if(!iq->dp) {
errinf(qstate, "malloc failure, for delegation point");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
if(old_dp->namelabs + 1 < iq->dp->namelabs) {
uint8_t* qname = iq->dp->name;
size_t qnamelen = iq->dp->namelen;
rrset_cache_remove_above(qstate->env->rrset_cache,
&qname, &qnamelen, LDNS_RR_TYPE_NS,
iq->qchase.qclass, *qstate->env->now,
old_dp->name, old_dp->namelen);
}
if(!cache_fill_missing(qstate->env, iq->qchase.qclass,
qstate->region, iq->dp, 0)) {
errinf(qstate, "malloc failure, copy extra info into delegation point");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
if(iq->store_parent_NS && query_dname_compare(iq->dp->name,
iq->store_parent_NS->name) == 0)
iter_merge_retry_counts(iq->dp, iq->store_parent_NS,
ie->outbound_msg_retry);
delegpt_log(VERB_ALGO, iq->dp);
iq->referral_count++;
iq->sent_count = 0;
iq->dp_target_count = 0;
iq->dnssec_expected = iter_indicates_dnssec(qstate->env,
iq->dp, iq->response, iq->qchase.qclass);
if(iq->dnssec_expected && qstate->env->cfg->prefetch_key &&
!(qstate->query_flags&BIT_CD))
generate_dnskey_prefetch(qstate, iq, id);
if(qstate->env->cfg->harden_referral_path)
generate_ns_check(qstate, iq, id);
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;
iq->response = NULL;
iq->fail_addr_type = 0;
verbose(VERB_ALGO, "cleared outbound list for next round");
return next_state(iq, QUERYTARGETS_STATE);
} else if(type == RESPONSE_TYPE_CNAME) {
uint8_t* sname = NULL;
size_t snamelen = 0;
verbose(VERB_DETAIL, "query response was CNAME");
if(verbosity >= VERB_ALGO)
log_dns_msg("cname msg", &iq->response->qinfo,
iq->response->rep);
if(iq->qchase.qtype == LDNS_RR_TYPE_DS && !iq->dsns_point
&& !(iq->chase_flags&BIT_RD)
&& iter_ds_toolow(iq->response, iq->dp)
&& iter_dp_cangodown(&iq->qchase, iq->dp)) {
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 processDSNSFind(qstate, iq, id);
}
if(iq->minimisation_state == MINIMISE_STATE &&
query_dname_compare(iq->qchase.qname,
iq->qinfo_out.qname) != 0) {
verbose(VERB_ALGO, "continue query minimisation, "
"downwards, after CNAME response for "
"intermediate label");
return next_state(iq, QUERYTARGETS_STATE);
}
if(!handle_cname_response(qstate, iq, iq->response,
&sname, &snamelen)) {
errinf(qstate, "malloc failure, CNAME info");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
if(!qstate->no_cache_store)
iter_dns_store(qstate->env, &iq->response->qinfo,
iq->response->rep, 1, qstate->prefetch_leeway,
iq->dp&&iq->dp->has_parent_side_NS, NULL,
qstate->query_flags, qstate->qstarttime,
qstate->is_valrec);
iq->qchase.qname = sname;
iq->qchase.qname_len = snamelen;
if(qstate->env->auth_zones) {
struct dns_msg* forged_response =
rpz_callback_from_iterator_cname(qstate, iq);
int count = 0;
while(forged_response && reply_find_rrset_section_an(
forged_response->rep, iq->qchase.qname,
iq->qchase.qname_len, LDNS_RR_TYPE_CNAME,
iq->qchase.qclass) &&
iq->qchase.qtype != LDNS_RR_TYPE_CNAME &&
count++ < ie->max_query_restarts) {
if(!handle_cname_response(qstate, iq, forged_response,
&sname, &snamelen)) {
errinf(qstate, "malloc failure, CNAME info");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
iq->qchase.qname = sname;
iq->qchase.qname_len = snamelen;
forged_response =
rpz_callback_from_iterator_cname(qstate, iq);
}
if(forged_response != NULL) {
qstate->ext_state[id] = module_finished;
qstate->return_rcode = LDNS_RCODE_NOERROR;
qstate->return_msg = forged_response;
iq->response = forged_response;
next_state(iq, FINISHED_STATE);
if(!iter_prepend(iq, qstate->return_msg, qstate->region)) {
log_err("rpz: after cname, prepend rrsets: out of memory");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
qstate->return_msg->qinfo = qstate->qinfo;
return 0;
}
}
iq->deleg_msg = NULL;
iq->dp = NULL;
iq->dsns_point = NULL;
iq->auth_zone_response = 0;
iq->sent_count = 0;
iq->dp_target_count = 0;
iq->query_restart_count++;
if(qstate->env->cfg->qname_minimisation)
iq->minimisation_state = INIT_MINIMISE_STATE;
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;
if(qstate->reply)
sock_list_insert(&qstate->reply_origin,
&qstate->reply->remote_addr,
qstate->reply->remote_addrlen, qstate->region);
verbose(VERB_ALGO, "cleared outbound list for query restart");
return next_state(iq, INIT_REQUEST_STATE);
} else if(type == RESPONSE_TYPE_LAME) {
verbose(VERB_DETAIL, "query response was %sLAME",
dnsseclame?"DNSSEC ":"");
if(!dname_subdomain_c(iq->qchase.qname, iq->dp->name)) {
log_err("mark lame: mismatch in qname and dpname");
} else if(qstate->reply) {
if(!infra_set_lame(qstate->env->infra_cache,
&qstate->reply->remote_addr,
qstate->reply->remote_addrlen,
iq->dp->name, iq->dp->namelen,
*qstate->env->now, dnsseclame, 0,
iq->qchase.qtype))
log_err("mark host lame: out of memory");
}
} else if(type == RESPONSE_TYPE_REC_LAME) {
verbose(VERB_DETAIL, "query response REC_LAME: "
"recursive but not authoritative server");
if(!dname_subdomain_c(iq->qchase.qname, iq->dp->name)) {
log_err("mark rec_lame: mismatch in qname and dpname");
} else if(qstate->reply) {
verbose(VERB_DETAIL, "mark as REC_LAME");
if(!infra_set_lame(qstate->env->infra_cache,
&qstate->reply->remote_addr,
qstate->reply->remote_addrlen,
iq->dp->name, iq->dp->namelen,
*qstate->env->now, 0, 1, iq->qchase.qtype))
log_err("mark host lame: out of memory");
}
} else if(type == RESPONSE_TYPE_THROWAWAY) {
verbose(VERB_DETAIL, "query response was THROWAWAY");
} else {
log_warn("A query response came back with an unknown type: %d",
(int)type);
}
if (qstate->env->cfg->qname_minimisation &&
!qstate->env->cfg->qname_minimisation_strict)
iq->minimisation_state = DONOT_MINIMISE_STATE;
if(iq->auth_zone_response) {
iq->auth_zone_response = 0;
if(!auth_zones_can_fallback(qstate->env->auth_zones,
iq->dp->name, iq->dp->namelen, qstate->qinfo.qclass)) {
verbose(VERB_ALGO, "auth zone response bad, and no"
" fallback possible, servfail");
errinf_dname(qstate, "response is bad, no fallback, "
"for auth zone", iq->dp->name);
return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
}
verbose(VERB_ALGO, "auth zone response was bad, "
"fallback enabled");
iq->auth_zone_avoid = 1;
if(iq->dp->auth_dp) {
iq->dp = NULL;
return next_state(iq, INIT_REQUEST_STATE);
}
}
return next_state(iq, QUERYTARGETS_STATE);
}
static void
prime_supers(struct module_qstate* qstate, int id, struct module_qstate* forq)
{
struct iter_qstate* foriq = (struct iter_qstate*)forq->minfo[id];
struct delegpt* dp = NULL;
log_assert(qstate->is_priming || foriq->wait_priming_stub);
log_assert(qstate->return_rcode == LDNS_RCODE_NOERROR);
dp = delegpt_from_message(qstate->return_msg, forq->region);
if(!dp) {
verbose(VERB_ALGO, "prime response was not a positive "
"ANSWER; failing");
foriq->dp = NULL;
foriq->state = QUERYTARGETS_STATE;
return;
}
log_query_info(VERB_DETAIL, "priming successful for", &qstate->qinfo);
delegpt_log(VERB_ALGO, dp);
foriq->dp = dp;
foriq->deleg_msg = dns_copy_msg(qstate->return_msg, forq->region);
if(!foriq->deleg_msg) {
log_err("copy prime response: out of memory");
foriq->dp = NULL;
foriq->state = QUERYTARGETS_STATE;
return;
}
if(foriq->wait_priming_stub) {
foriq->state = INIT_REQUEST_3_STATE;
foriq->wait_priming_stub = 0;
} else foriq->state = INIT_REQUEST_2_STATE;
}
static int
processPrimeResponse(struct module_qstate* qstate, int id)
{
struct iter_qstate* iq = (struct iter_qstate*)qstate->minfo[id];
enum response_type type;
iq->response->rep->flags &= ~(BIT_RD|BIT_RA);
type = response_type_from_server(
(int)((iq->chase_flags&BIT_RD) || iq->chase_to_rd),
iq->response, &iq->qchase, iq->dp, NULL);
if(type == RESPONSE_TYPE_ANSWER) {
qstate->return_rcode = LDNS_RCODE_NOERROR;
qstate->return_msg = iq->response;
} else {
errinf(qstate, "prime response did not get an answer");
errinf_dname(qstate, "for", qstate->qinfo.qname);
qstate->return_rcode = LDNS_RCODE_SERVFAIL;
qstate->return_msg = NULL;
}
if(qstate->env->cfg->harden_referral_path) {
struct module_qstate* subq = NULL;
log_nametypeclass(VERB_ALGO, "schedule prime validation",
qstate->qinfo.qname, qstate->qinfo.qtype,
qstate->qinfo.qclass);
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, 0)) {
verbose(VERB_ALGO, "could not generate prime check");
}
generate_a_aaaa_check(qstate, iq, id);
}
qstate->ext_state[id] = module_finished;
return 0;
}
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;
struct delegpt_ns* dpns;
log_assert(qstate->return_rcode == LDNS_RCODE_NOERROR);
foriq->state = QUERYTARGETS_STATE;
log_query_info(VERB_ALGO, "processTargetResponse", &qstate->qinfo);
log_query_info(VERB_ALGO, "processTargetResponse super", &forq->qinfo);
foriq->num_target_queries--;
if(!foriq->dp) {
verbose(VERB_ALGO, "subq: parent not interested, was reset");
return;
}
dpns = delegpt_find_ns(foriq->dp, qstate->qinfo.qname,
qstate->qinfo.qname_len);
if(!dpns) {
verbose(VERB_ALGO, "subq: parent not interested anymore");
return;
}
if(iq->pside_glue) {
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, NULL))
log_err("out of memory adding pside glue");
}
rrset = reply_find_answer_rrset(&iq->qchase, qstate->return_msg->rep);
if(rrset) {
int additions = 0;
if(!delegpt_find_ns(foriq->dp, rrset->rk.dname,
rrset->rk.dname_len)) {
if(!delegpt_add_ns(foriq->dp, forq->region,
rrset->rk.dname, dpns->lame, dpns->tls_auth_name,
dpns->port))
log_err("out of memory adding cnamed-ns");
}
if(!delegpt_add_rrset(foriq->dp, forq->region, rrset,
dpns->lame, &additions))
log_err("out of memory adding targets");
if(!additions) {
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);
if((dpns->got4 == 2 || (!ie->supports_ipv4 && !ie->nat64.use_nat64)) &&
(dpns->got6 == 2 || !ie->supports_ipv6)) {
dpns->resolved = 1;
if(qstate->reply_origin && qstate->reply_origin->len != 0) {
target_count_increase_nx(foriq, 1);
}
}
}
}
static void
processDSNSResponse(struct module_qstate* qstate, int id,
struct module_qstate* forq)
{
struct iter_qstate* foriq = (struct iter_qstate*)forq->minfo[id];
if(qstate->return_rcode != LDNS_RCODE_NOERROR)
return;
if(!reply_find_rrset(qstate->return_msg->rep, qstate->qinfo.qname,
qstate->qinfo.qname_len, LDNS_RR_TYPE_NS,
qstate->qinfo.qclass)){
return;
}
foriq->state = QUERYTARGETS_STATE;
foriq->dp = delegpt_from_message(qstate->return_msg, forq->region);
if(!foriq->dp) {
log_err("out of memory in dsns dp alloc");
errinf(qstate, "malloc failure, in DS search");
return;
}
}
static void
processClassResponse(struct module_qstate* qstate, int id,
struct module_qstate* forq)
{
struct iter_qstate* foriq = (struct iter_qstate*)forq->minfo[id];
struct dns_msg* from = qstate->return_msg;
log_query_info(VERB_ALGO, "processClassResponse", &qstate->qinfo);
log_query_info(VERB_ALGO, "processClassResponse super", &forq->qinfo);
if(qstate->return_rcode != LDNS_RCODE_NOERROR) {
foriq->response = NULL;
foriq->state = FINISHED_STATE;
return;
}
if(!foriq->response) {
foriq->response = dns_copy_msg(from, forq->region);
if(!foriq->response) {
log_err("malloc failed for qclass ANY response");
foriq->state = FINISHED_STATE;
return;
}
foriq->response->qinfo.qclass = forq->qinfo.qclass;
foriq->response->rep->authoritative = 0;
} else {
struct dns_msg* to = foriq->response;
if(from->rep->rrset_count != 0) {
size_t n = from->rep->rrset_count+to->rep->rrset_count;
struct ub_packed_rrset_key** dest, **d;
to->rep->flags = from->rep->flags;
if(from->rep->rrset_count > RR_COUNT_MAX ||
to->rep->rrset_count > RR_COUNT_MAX) {
log_err("malloc failed (too many rrsets) in collect ANY");
foriq->state = FINISHED_STATE;
return;
}
dest = regional_alloc(forq->region, sizeof(dest[0])*n);
if(!dest) {
log_err("malloc failed in collect ANY");
foriq->state = FINISHED_STATE;
return;
}
d = dest;
memcpy(dest, to->rep->rrsets, to->rep->an_numrrsets
* sizeof(dest[0]));
dest += to->rep->an_numrrsets;
memcpy(dest, from->rep->rrsets, from->rep->an_numrrsets
* sizeof(dest[0]));
dest += from->rep->an_numrrsets;
memcpy(dest, to->rep->rrsets+to->rep->an_numrrsets,
to->rep->ns_numrrsets * sizeof(dest[0]));
dest += to->rep->ns_numrrsets;
memcpy(dest, from->rep->rrsets+from->rep->an_numrrsets,
from->rep->ns_numrrsets * sizeof(dest[0]));
dest += from->rep->ns_numrrsets;
memcpy(dest, to->rep->rrsets+to->rep->an_numrrsets+
to->rep->ns_numrrsets,
to->rep->ar_numrrsets * sizeof(dest[0]));
dest += to->rep->ar_numrrsets;
memcpy(dest, from->rep->rrsets+from->rep->an_numrrsets+
from->rep->ns_numrrsets,
from->rep->ar_numrrsets * sizeof(dest[0]));
to->rep->rrsets = d;
to->rep->an_numrrsets += from->rep->an_numrrsets;
to->rep->ns_numrrsets += from->rep->ns_numrrsets;
to->rep->ar_numrrsets += from->rep->ar_numrrsets;
to->rep->rrset_count = n;
}
if(from->rep->security < to->rep->security)
to->rep->security = from->rep->security;
if(from->rep->qdcount != 0)
to->rep->qdcount = from->rep->qdcount;
if(from->rep->ttl < to->rep->ttl)
to->rep->ttl = from->rep->ttl;
if(from->rep->prefetch_ttl < to->rep->prefetch_ttl)
to->rep->prefetch_ttl = from->rep->prefetch_ttl;
if(from->rep->serve_expired_ttl < to->rep->serve_expired_ttl)
to->rep->serve_expired_ttl = from->rep->serve_expired_ttl;
if(from->rep->serve_expired_norec_ttl < to->rep->serve_expired_norec_ttl)
to->rep->serve_expired_norec_ttl = from->rep->serve_expired_norec_ttl;
}
foriq->num_current_queries --;
if(foriq->num_current_queries == 0)
foriq->state = FINISHED_STATE;
}
static int
processCollectClass(struct module_qstate* qstate, int id)
{
struct iter_qstate* iq = (struct iter_qstate*)qstate->minfo[id];
struct module_qstate* subq;
if(iq->qchase.qclass == 0) {
uint16_t c = 0;
iq->qchase.qclass = LDNS_RR_CLASS_ANY;
while(iter_get_next_root(qstate->env->hints,
qstate->env->fwds, &c)) {
log_nametypeclass(VERB_ALGO, "spawn collect query",
qstate->qinfo.qname, qstate->qinfo.qtype, c);
if(!generate_sub_request(qstate->qinfo.qname,
qstate->qinfo.qname_len, qstate->qinfo.qtype,
c, qstate, id, iq, INIT_REQUEST_STATE,
FINISHED_STATE, &subq,
(int)!(qstate->query_flags&BIT_CD), 0)) {
errinf(qstate, "could not generate class ANY"
" lookup query");
return error_response(qstate, id,
LDNS_RCODE_SERVFAIL);
}
iq->num_current_queries ++;
if(c == 0xffff)
break;
else c++;
}
if(iq->num_current_queries == 0) {
verbose(VERB_ALGO, "No root hints or fwds, giving up "
"on qclass ANY");
return error_response_cache(qstate, id, LDNS_RCODE_REFUSED);
}
}
return 0;
}
static int
processFinished(struct module_qstate* qstate, struct iter_qstate* iq,
int id)
{
log_query_info(VERB_QUERY, "finishing processing for",
&qstate->qinfo);
if(!qstate->no_cache_store && iq->query_for_pside_glue
&& !iq->pside_glue)
iter_store_parentside_neg(qstate->env, &qstate->qinfo,
iq->deleg_msg?iq->deleg_msg->rep:
(iq->response?iq->response->rep:NULL));
if(!iq->response) {
verbose(VERB_ALGO, "No response is set, servfail");
errinf(qstate, "(no response found at query finish)");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
iq->response->rep->flags |= BIT_RA;
iq->response->rep->flags &= ~BIT_AA;
iq->response->rep->flags |= BIT_QR;
iq->response->rep->reason_bogus_str = NULL;
if((qstate->env->cfg->val_log_level >= 2 ||
qstate->env->cfg->log_servfail) && qstate->errinf &&
!qstate->env->cfg->val_log_squelch) {
char* err_str = errinf_to_str_misc(qstate);
if(err_str) {
verbose(VERB_ALGO, "iterator EDE: %s", err_str);
iq->response->rep->reason_bogus_str = err_str;
}
}
qstate->ext_state[id] = module_finished;
if(iq->an_prepend_list || iq->ns_prepend_list) {
if(!iter_prepend(iq, iq->response, qstate->region)) {
log_err("prepend rrsets: out of memory");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
iq->response->qinfo = qstate->qinfo;
iq->response->rep->security = sec_status_unchecked;
if(!qstate->no_cache_store && (qstate->query_flags&BIT_RD)) {
iter_dns_store(qstate->env, &qstate->qinfo,
iq->response->rep, 0, qstate->prefetch_leeway,
iq->dp&&iq->dp->has_parent_side_NS,
qstate->region, qstate->query_flags,
qstate->qstarttime, qstate->is_valrec);
}
}
qstate->return_rcode = LDNS_RCODE_NOERROR;
qstate->return_msg = iq->response;
return 0;
}
void
iter_inform_super(struct module_qstate* qstate, int id,
struct module_qstate* super)
{
if(!qstate->is_priming && super->qinfo.qclass == LDNS_RR_CLASS_ANY)
processClassResponse(qstate, id, super);
else if(super->qinfo.qtype == LDNS_RR_TYPE_DS && ((struct iter_qstate*)
super->minfo[id])->state == DSNS_FIND_STATE)
processDSNSResponse(qstate, id, super);
else if(qstate->return_rcode != LDNS_RCODE_NOERROR)
error_supers(qstate, id, super);
else if(qstate->is_priming)
prime_supers(qstate, id, super);
else processTargetResponse(qstate, id, super);
}
static void
iter_handle(struct module_qstate* qstate, struct iter_qstate* iq,
struct iter_env* ie, int id)
{
int cont = 1;
while(cont) {
verbose(VERB_ALGO, "iter_handle processing q with state %s",
iter_state_to_string(iq->state));
switch(iq->state) {
case INIT_REQUEST_STATE:
cont = processInitRequest(qstate, iq, ie, id);
break;
case INIT_REQUEST_2_STATE:
cont = processInitRequest2(qstate, iq, id);
break;
case INIT_REQUEST_3_STATE:
cont = processInitRequest3(qstate, iq, id);
break;
case QUERYTARGETS_STATE:
cont = processQueryTargets(qstate, iq, ie, id);
break;
case QUERY_RESP_STATE:
cont = processQueryResponse(qstate, iq, ie, id);
break;
case PRIME_RESP_STATE:
cont = processPrimeResponse(qstate, id);
break;
case COLLECT_CLASS_STATE:
cont = processCollectClass(qstate, id);
break;
case DSNS_FIND_STATE:
cont = processDSNSFind(qstate, iq, id);
break;
case FINISHED_STATE:
cont = processFinished(qstate, iq, id);
break;
default:
log_warn("iterator: invalid state: %d",
iq->state);
cont = 0;
break;
}
}
}
static void
process_request(struct module_qstate* qstate, struct iter_qstate* iq,
struct iter_env* ie, int id)
{
iq->state = INIT_REQUEST_STATE;
iq->final_state = FINISHED_STATE;
verbose(VERB_ALGO, "process_request: new external request event");
iter_handle(qstate, iq, ie, id);
}
static void
process_response(struct module_qstate* qstate, struct iter_qstate* iq,
struct iter_env* ie, int id, struct outbound_entry* outbound,
enum module_ev event)
{
struct msg_parse* prs;
struct edns_data edns;
sldns_buffer* pkt;
verbose(VERB_ALGO, "process_response: new external response event");
iq->response = NULL;
iq->state = QUERY_RESP_STATE;
if(event == module_event_noreply || event == module_event_error) {
if(event == module_event_noreply && iq->timeout_count >= 3 &&
qstate->env->cfg->use_caps_bits_for_id &&
!iq->caps_fallback && !is_caps_whitelisted(ie, iq)) {
iq->caps_fallback = 1;
iq->caps_server = 0;
iq->caps_reply = NULL;
iq->caps_response = NULL;
iq->caps_minimisation_state = DONOT_MINIMISE_STATE;
iq->state = QUERYTARGETS_STATE;
iq->num_current_queries--;
iter_dec_attempts(iq->dp, 3, ie->outbound_msg_retry);
verbose(VERB_DETAIL, "Capsforid: timeouts, starting fallback");
goto handle_it;
}
goto handle_it;
}
if( (event != module_event_reply && event != module_event_capsfail)
|| !qstate->reply) {
log_err("Bad event combined with response");
outbound_list_remove(&iq->outlist, outbound);
errinf(qstate, "module iterator received wrong internal event with a response message");
(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
return;
}
fill_fail_addr(iq, &qstate->reply->remote_addr,
qstate->reply->remote_addrlen);
prs = (struct msg_parse*)regional_alloc(qstate->env->scratch,
sizeof(struct msg_parse));
if(!prs) {
log_err("out of memory on incoming message");
goto handle_it;
}
memset(prs, 0, sizeof(*prs));
memset(&edns, 0, sizeof(edns));
pkt = qstate->reply->c->buffer;
sldns_buffer_set_position(pkt, 0);
if(parse_packet(pkt, prs, qstate->env->scratch) != LDNS_RCODE_NOERROR) {
verbose(VERB_ALGO, "parse error on reply packet");
iq->parse_failures++;
goto handle_it;
}
if(parse_extract_edns_from_response_msg(prs, &edns, qstate->env->scratch) !=
LDNS_RCODE_NOERROR) {
iq->parse_failures++;
goto handle_it;
}
qstate->edns_opts_back_in = NULL;
if(edns.opt_list_in) {
qstate->edns_opts_back_in = edns_opt_copy_region(edns.opt_list_in,
qstate->region);
if(!qstate->edns_opts_back_in) {
log_err("out of memory on incoming message");
goto handle_it;
}
}
if(!inplace_cb_edns_back_parsed_call(qstate->env, qstate)) {
log_err("unable to call edns_back_parsed callback");
goto handle_it;
}
prs->flags &= ~BIT_CD;
if(!scrub_message(pkt, prs, &iq->qinfo_out, iq->dp->name,
qstate->env->scratch, qstate->env, qstate, ie)) {
if(event == module_event_capsfail && !iq->caps_fallback) {
iq->caps_fallback = 1;
iq->caps_server = 0;
iq->caps_reply = NULL;
iq->caps_response = NULL;
iq->caps_minimisation_state = DONOT_MINIMISE_STATE;
iq->state = QUERYTARGETS_STATE;
iq->num_current_queries--;
verbose(VERB_DETAIL, "Capsforid: scrub failed, starting fallback with no response");
}
iq->scrub_failures++;
goto handle_it;
}
iq->response = dns_alloc_msg(pkt, prs, qstate->region);
if(!iq->response)
goto handle_it;
log_query_info(VERB_DETAIL, "response for", &qstate->qinfo);
log_name_addr(VERB_DETAIL, "reply from", iq->dp->name,
&qstate->reply->remote_addr, qstate->reply->remote_addrlen);
if(verbosity >= VERB_ALGO)
log_dns_msg("incoming scrubbed packet:", &iq->response->qinfo,
iq->response->rep);
if(qstate->env->cfg->aggressive_nsec) {
limit_nsec_ttl(iq->response);
}
if(event == module_event_capsfail || iq->caps_fallback) {
if(qstate->env->cfg->qname_minimisation &&
iq->minimisation_state != DONOT_MINIMISE_STATE) {
iq->minimisation_state = SKIP_MINIMISE_STATE;
}
caps_strip_reply(iq->response->rep);
if(iq->caps_fallback &&
iq->caps_minimisation_state != iq->minimisation_state) {
iq->caps_fallback = 0;
}
if(!iq->caps_fallback) {
iq->caps_fallback = 1;
iq->caps_server = 0;
iq->caps_reply = iq->response->rep;
iq->caps_response = iq->response;
iq->caps_minimisation_state = iq->minimisation_state;
iq->state = QUERYTARGETS_STATE;
iq->num_current_queries--;
verbose(VERB_DETAIL, "Capsforid: starting fallback");
goto handle_it;
} else {
if(!iq->caps_reply) {
iq->caps_reply = iq->response->rep;
iq->caps_response = iq->response;
iq->caps_server = -1;
} else if(caps_failed_rcode(iq->caps_reply) &&
!caps_failed_rcode(iq->response->rep)) {
iq->caps_reply = iq->response->rep;
iq->caps_response = iq->response;
} else if(!caps_failed_rcode(iq->caps_reply) &&
caps_failed_rcode(iq->response->rep)) {
} else if(caps_failed_rcode(iq->caps_reply) &&
caps_failed_rcode(iq->response->rep)) {
} else if(!reply_equal(iq->response->rep, iq->caps_reply,
qstate->env->scratch)) {
verbose(VERB_DETAIL, "Capsforid fallback: "
"getting different replies, failed");
outbound_list_remove(&iq->outlist, outbound);
errinf(qstate, "0x20 failed, then got different replies in fallback");
(void)error_response_cache(qstate, id,
LDNS_RCODE_SERVFAIL);
return;
}
iq->caps_server++;
iq->state = QUERYTARGETS_STATE;
iq->num_current_queries--;
verbose(VERB_DETAIL, "Capsforid: reply is equal. "
"go to next fallback");
goto handle_it;
}
}
iq->caps_fallback = 0;
handle_it:
outbound_list_remove(&iq->outlist, outbound);
iter_handle(qstate, iq, ie, id);
}
void
iter_operate(struct module_qstate* qstate, enum module_ev event, int id,
struct outbound_entry* outbound)
{
struct iter_env* ie = (struct iter_env*)qstate->env->modinfo[id];
struct iter_qstate* iq = (struct iter_qstate*)qstate->minfo[id];
verbose(VERB_QUERY, "iterator[module %d] operate: extstate:%s event:%s",
id, strextstate(qstate->ext_state[id]), strmodulevent(event));
if(iq) log_query_info(VERB_QUERY, "iterator operate: query",
&qstate->qinfo);
if(iq && qstate->qinfo.qname != iq->qchase.qname)
log_query_info(VERB_QUERY, "iterator operate: chased to",
&iq->qchase);
if((event == module_event_new || event == module_event_pass) &&
iq == NULL) {
if(!iter_new(qstate, id)) {
errinf(qstate, "malloc failure, new iterator module allocation");
(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
return;
}
iq = (struct iter_qstate*)qstate->minfo[id];
process_request(qstate, iq, ie, id);
return;
}
if(iq && event == module_event_pass) {
iter_handle(qstate, iq, ie, id);
return;
}
if(iq && outbound) {
process_response(qstate, iq, ie, id, outbound, event);
return;
}
if(event == module_event_error) {
verbose(VERB_ALGO, "got called with event error, giving up");
errinf(qstate, "iterator module got the error event");
(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
return;
}
log_err("bad event for iterator");
errinf(qstate, "iterator module received wrong event");
(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
void
iter_clear(struct module_qstate* qstate, int id)
{
struct iter_qstate* iq;
if(!qstate)
return;
iq = (struct iter_qstate*)qstate->minfo[id];
if(iq) {
outbound_list_clear(&iq->outlist);
if(iq->target_count && --iq->target_count[TARGET_COUNT_REF] == 0) {
free(iq->target_count);
if(*iq->nxns_dp) free(*iq->nxns_dp);
free(iq->nxns_dp);
}
iq->num_current_queries = 0;
}
qstate->minfo[id] = NULL;
}
size_t
iter_get_mem(struct module_env* env, int id)
{
struct iter_env* ie = (struct iter_env*)env->modinfo[id];
if(!ie)
return 0;
return sizeof(*ie) + sizeof(int)*((size_t)ie->max_dependency_depth+1)
+ donotq_get_mem(ie->donotq) + priv_get_mem(ie->priv);
}
static struct module_func_block iter_block = {
"iterator",
NULL, NULL, &iter_init, &iter_deinit, &iter_operate,
&iter_inform_super, &iter_clear, &iter_get_mem
};
struct module_func_block*
iter_get_funcblock(void)
{
return &iter_block;
}
const char*
iter_state_to_string(enum iter_state state)
{
switch (state)
{
case INIT_REQUEST_STATE :
return "INIT REQUEST STATE";
case INIT_REQUEST_2_STATE :
return "INIT REQUEST STATE (stage 2)";
case INIT_REQUEST_3_STATE:
return "INIT REQUEST STATE (stage 3)";
case QUERYTARGETS_STATE :
return "QUERY TARGETS STATE";
case PRIME_RESP_STATE :
return "PRIME RESPONSE STATE";
case COLLECT_CLASS_STATE :
return "COLLECT CLASS STATE";
case DSNS_FIND_STATE :
return "DSNS FIND STATE";
case QUERY_RESP_STATE :
return "QUERY RESPONSE STATE";
case FINISHED_STATE :
return "FINISHED RESPONSE STATE";
default :
return "UNKNOWN ITER STATE";
}
}
int
iter_state_is_responsestate(enum iter_state s)
{
switch(s) {
case INIT_REQUEST_STATE :
case INIT_REQUEST_2_STATE :
case INIT_REQUEST_3_STATE :
case QUERYTARGETS_STATE :
case COLLECT_CLASS_STATE :
return 0;
default:
break;
}
return 1;
}