#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/infra.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 "ldns/rrdef.h"
#include "ldns/wire2str.h"
#include "ldns/parseutil.h"
#include "ldns/sbuffer.h"
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;
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];
free(iter_env->target_fetch_policy);
priv_delete(iter_env->priv);
donotq_delete(iter_env->donotq);
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->target_count = NULL;
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);
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_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;
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);
if(super_iq->dp)
delegpt_log(VERB_ALGO, super_iq->dp);
log_assert(0);
return;
} else {
if(!cache_fill_missing(super->env,
super_iq->qchase.qclass, super->region,
super_iq->dp))
log_err("out of memory adding missing");
}
dpns->resolved = 1;
super_iq->num_target_queries--;
}
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;
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);
}
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.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);
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);
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;
}
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;
}
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_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)) {
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) {
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
target_count_create(struct iter_qstate* iq)
{
if(!iq->target_count) {
iq->target_count = (int*)calloc(2, sizeof(int));
if(iq->target_count)
iq->target_count[0] = 1;
}
}
static void
target_count_increase(struct iter_qstate* iq, int num)
{
target_count_create(iq);
if(iq->target_count)
iq->target_count[1] += 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)
{
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;
if(initial_state == INIT_REQUEST_STATE)
qflags |= BIT_RD;
if(!v) {
qflags |= BIT_CD;
valrec = 1;
}
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[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;
}
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;
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_lookup_root(qstate->env->hints, qclass);
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)) {
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);
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);
}
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;
if(!qname) return 0;
stub = hints_lookup_stub(qstate->env->hints, qname, qclass, iq->dp);
if(!stub)
return 0;
stub_dp = stub->dp;
if(stub->noprime) {
int r = 0;
if(iq->dp == NULL) r = 2;
iq->dp = delegpt_copy(stub_dp, qstate->region);
if(!iq->dp) {
log_err("out of memory priming stub");
(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
return 1;
}
log_nametypeclass(VERB_DETAIL, "use stub", stub_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)) {
verbose(VERB_ALGO, "could not prime stub");
(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);
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);
(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);
}
qstate->ext_state[id] = module_wait_subquery;
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)) {
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(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;
}
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)) {
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;
}
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)) {
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;
if(iq->refetch_glue) {
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);
if(!dp)
return 0;
iq->chase_flags |= BIT_RD;
iq->dp = delegpt_copy(dp, qstate->region);
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* delname;
size_t delnamelen;
struct dns_msg* msg;
log_query_info(VERB_DETAIL, "resolving", &qstate->qinfo);
if(iq->query_restart_count > MAX_RESTART_COUNT) {
verbose(VERB_QUERY, "request has exceeded the maximum number"
" of query restarts with %d", iq->query_restart_count);
return error_response(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);
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(qstate->blacklist) {
verbose(VERB_ALGO, "cache blacklisted, going to the network");
msg = NULL;
} else {
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);
if(!msg && qstate->env->neg_cache) {
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);
}
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))
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;
sock_list_insert(&qstate->reply_origin, NULL, 0, qstate->region);
return next_state(iq, INIT_REQUEST_STATE);
}
if(qstate->reply_origin)
sock_list_insert(&qstate->reply_origin, NULL, 0, qstate->region);
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");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
iq->refetch_glue = 0;
return next_state(iq, QUERYTARGETS_STATE);
}
if(iq->refetch_glue) {
if(!iq->dp) {
log_err("internal or malloc fail: no dp for refetch");
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)) {
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);
else iq->dp = NULL;
if(iq->dp == NULL) {
int 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)) {
iq->dp = hints_lookup_root(qstate->env->hints,
iq->qchase.qclass);
if(!iq->dp) {
log_err("internal error: no hints dp");
return error_response(qstate, id,
LDNS_RCODE_SERVFAIL);
}
iq->dp = delegpt_copy(iq->dp, qstate->region);
if(!iq->dp) {
log_err("out of memory 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(iter_dp_is_useless(&qstate->qinfo, qstate->query_flags,
iq->dp)) {
if(dname_is_root(iq->dp->name)) {
verbose(VERB_QUERY, "Cache has root NS but "
"no addresses. Fallback to the safety belt.");
iq->dp = hints_lookup_root(qstate->env->hints,
iq->qchase.qclass);
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);
if(!iq->dp) {
log_err("out of memory in safety belt");
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);
if(iq->refetch_glue) {
if(!iq->dp) {
log_err("internal or malloc fail: no dp for refetch");
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) {
if(!dname_is_root(delname))
dname_remove_label(&delname, &delnamelen);
iq->refetch_glue = 0;
}
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->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))
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);
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))
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;
if(iq->depth == ie->max_dependency_depth)
return 0;
if(iq->depth > 0 && iq->target_count &&
iq->target_count[1] > MAX_TARGET_COUNT) {
verbose(VERB_QUERY, "request has exceeded the maximum "
"number of glue fetches %d", iq->target_count[1]);
return 0;
}
iter_mark_cycle_targets(qstate, iq->dp);
missing = (int)delegpt_count_missing_targets(iq->dp);
log_assert(maxtargets != 0);
if(maxtargets < 0 || maxtargets > missing)
toget = missing;
else toget = maxtargets;
if(toget == 0) {
*num = 0;
return 1;
}
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->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 0;
}
query_count++;
}
if(ie->supports_ipv4 && !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 0;
}
query_count++;
}
ns->resolved = 1;
missing--;
toget--;
if(toget == 0)
break;
}
*num = query_count;
if(query_count > 0)
qstate->ext_state[id] = module_wait_subquery;
return 1;
}
static int
can_have_last_resort(struct module_env* env, struct delegpt* dp,
struct iter_qstate* iq)
{
struct delegpt* fwddp;
struct iter_hints_stub* stub;
if(!dname_is_root(dp->name) && (stub = (struct iter_hints_stub*)
name_tree_find(&env->hints->tree, dp->name, dp->namelen,
dp->namelabs, iq->qchase.qclass)) &&
stub->dp->has_parent_side_NS) {
verbose(VERB_QUERY, "configured stub servers failed -- returning SERVFAIL");
return 0;
}
if((fwddp = forwards_find(env->fwds, dp->name, iq->qchase.qclass)) &&
fwddp->has_parent_side_NS) {
verbose(VERB_QUERY, "configured forward servers failed -- returning SERVFAIL");
return 0;
}
return 1;
}
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, iq)) {
return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
}
if(!iq->dp->has_parent_side_NS && dname_is_root(iq->dp->name)) {
struct delegpt* p = hints_lookup_root(qstate->env->hints,
iq->qchase.qclass);
if(p) {
struct delegpt_ns* ns;
struct delegpt_addr* a;
iq->chase_flags &= ~BIT_RD;
for(ns = p->nslist; ns; ns=ns->next) {
(void)delegpt_add_ns(iq->dp, qstate->region,
ns->name, ns->lame);
}
for(a = p->target_list; a; a=a->next_target) {
(void)delegpt_add_addr(iq->dp, qstate->region,
&a->addr, a->addrlen, a->bogus,
a->lame);
}
}
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;
return next_state(iq, INIT_REQUEST_STATE);
}
}
if(!cache_fill_missing(qstate->env, iq->qchase.qclass,
qstate->region, iq->dp))
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) > 0) {
int qs = 0;
verbose(VERB_ALGO, "try parent-side target name");
if(!query_for_targets(qstate, iq, ie, id, 1, &qs)) {
return error_response(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");
return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
}
if(iq->depth > 0 && iq->target_count &&
iq->target_count[1] > MAX_TARGET_COUNT) {
verbose(VERB_QUERY, "request has exceeded the maximum "
"number of glue fetches %d", iq->target_count[1]);
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) {
if(!generate_parentside_target_query(qstate, iq, id,
ns->name, ns->namelen,
LDNS_RR_TYPE_AAAA, iq->qchase.qclass))
return error_response(qstate, id,
LDNS_RCODE_SERVFAIL);
ns->done_pside6 = 1;
query_count++;
}
if(ie->supports_ipv4 && !ns->done_pside4) {
if(!generate_parentside_target_query(qstate, iq, id,
ns->name, ns->namelen,
LDNS_RR_TYPE_A, iq->qchase.qclass))
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(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));
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)) {
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)) {
return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
}
return 0;
}
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;
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);
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
if(iq->sent_count > MAX_SENT_COUNT) {
verbose(VERB_QUERY, "request has exceeded the maximum "
"number of sends with %d", iq->sent_count);
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
if(!iq->dp) {
verbose(VERB_QUERY, "Failed to get a delegation, giving up");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
if(!ie->supports_ipv6)
delegpt_no_ipv6(iq->dp);
if(!ie->supports_ipv4)
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;
}
tf_policy = 0;
if(iq->depth < ie->max_dependency_depth
&& iq->sent_count < TARGET_FETCH_STOP) {
tf_policy = ie->target_fetch_policy[iq->depth];
}
if(iq->caps_fallback) {
int extra = 0;
size_t naddr, nres, navail;
if(!query_for_targets(qstate, iq, ie, id, -1, &extra)) {
return error_response(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;
return 0;
}
delegpt_count_addr(iq->dp, &naddr, &nres, &navail);
if(iq->caps_server+1 >= naddr*3 ||
iq->caps_server+1 >= 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->caps_fallback = 0;
iter_dec_attempts(iq->dp, 3);
iq->num_current_queries++;
iq->referral_count++;
iq->sent_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);
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);
if(!target) {
if(iq->num_target_queries==0 && iq->num_current_queries==0) {
if(delegpt_count_missing_targets(iq->dp) > 0) {
int qs = 0;
verbose(VERB_ALGO, "querying for next "
"missing target");
if(!query_for_targets(qstate, iq, ie, id,
1, &qs)) {
return error_response(qstate, id,
LDNS_RCODE_SERVFAIL);
}
if(qs == 0 &&
delegpt_count_missing_targets(iq->dp) == 0){
return 1;
}
iq->num_target_queries += qs;
target_count_increase(iq, qs);
}
if(iq->num_target_queries == 0) {
return processLastResort(qstate, iq, ie, id);
}
}
if(iq->num_target_queries>0 && iq->num_current_queries>0) {
verbose(VERB_ALGO, "no current targets -- 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, "no current targets -- waiting "
"for %d targets to resolve.",
iq->num_target_queries);
qstate->ext_state[id] = module_wait_subquery;
} else {
verbose(VERB_ALGO, "no current targets -- waiting "
"for %d outstanding queries to respond.",
iq->num_current_queries);
qstate->ext_state[id] = module_wait_reply;
}
return 0;
}
if(verbosity >= VERB_QUERY) {
log_query_info(VERB_QUERY, "sending query:", &iq->qchase);
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": "");
}
fptr_ok(fptr_whitelist_modenv_send_query(qstate->env->send_query));
outq = (*qstate->env->send_query)(
iq->qchase.qname, iq->qchase.qname_len,
iq->qchase.qtype, iq->qchase.qclass,
iq->chase_flags | (iq->chase_to_rd?BIT_RD:0), EDNS_DO|BIT_CD,
iq->dnssec_expected, iq->caps_fallback, &target->addr,
target->addrlen, iq->dp->name, iq->dp->namelen, qstate);
if(!outq) {
log_addr(VERB_DETAIL, "error sending query to auth server",
&target->addr, target->addrlen);
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,
int id)
{
int dnsseclame = 0;
enum response_type type;
iq->num_current_queries--;
if(iq->response == NULL) {
iq->chase_to_rd = 0;
iq->dnssec_lame_query = 0;
verbose(VERB_ALGO, "query response was timeout");
return next_state(iq, QUERYTARGETS_STATE);
}
type = response_type_from_server(
(int)((iq->chase_flags&BIT_RD) || iq->chase_to_rd),
iq->response, &iq->qchase, iq->dp);
iq->chase_to_rd = 0;
if(type == RESPONSE_TYPE_REFERRAL && (iq->chase_flags&BIT_RD)) {
type = RESPONSE_TYPE_ANSWER;
}
if(iq->dnssec_expected && !iq->dnssec_lame_query &&
!(iq->chase_flags&BIT_RD)
&& 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->addr,
qstate->reply->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_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);
}
iter_dns_store(qstate->env, &iq->response->qinfo,
iq->response->rep, 0, qstate->prefetch_leeway,
iq->dp&&iq->dp->has_parent_side_NS,
qstate->region, qstate->query_flags);
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->addr, qstate->reply->addrlen,
qstate->region);
return final_state(iq);
} else if(type == RESPONSE_TYPE_REFERRAL) {
verbose(VERB_DETAIL, "query response was REFERRAL");
if(!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);
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(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;
iq->dp = delegpt_from_message(iq->response, qstate->region);
if(!iq->dp)
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
if(!cache_fill_missing(qstate->env, iq->qchase.qclass,
qstate->region, iq->dp))
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);
delegpt_log(VERB_ALGO, iq->dp);
iq->referral_count++;
iq->sent_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;
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(!handle_cname_response(qstate, iq, iq->response,
&sname, &snamelen))
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
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);
iq->qchase.qname = sname;
iq->qchase.qname_len = snamelen;
iq->deleg_msg = NULL;
iq->dp = NULL;
iq->dsns_point = NULL;
iq->query_restart_count++;
iq->sent_count = 0;
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->addr, qstate->reply->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->addr, qstate->reply->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->addr, qstate->reply->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);
}
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);
if(type == RESPONSE_TYPE_ANSWER) {
qstate->return_rcode = LDNS_RCODE_NOERROR;
qstate->return_msg = iq->response;
} else {
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)) {
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_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);
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;
}
foriq->num_target_queries--;
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))
log_err("out of memory adding pside glue");
}
rrset = reply_find_answer_rrset(&iq->qchase, qstate->return_msg->rep);
if(rrset) {
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))
log_err("out of memory adding cnamed-ns");
}
if(!delegpt_add_rrset(foriq->dp, forq->region, rrset,
dpns->lame))
log_err("out of memory adding targets");
verbose(VERB_ALGO, "added target response");
delegpt_log(VERB_ALGO, foriq->dp);
} else {
verbose(VERB_ALGO, "iterator TargetResponse failed");
dpns->resolved = 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");
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;
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;
}
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))) {
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(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(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");
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;
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->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->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, 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->sent_count >= 3 &&
qstate->env->cfg->use_caps_bits_for_id &&
!iq->caps_fallback) {
iq->caps_fallback = 1;
iq->caps_server = 0;
iq->caps_reply = NULL;
iq->state = QUERYTARGETS_STATE;
iq->num_current_queries--;
iter_dec_attempts(iq->dp, 3);
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);
(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
return;
}
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");
goto handle_it;
}
if(parse_extract_edns(prs, &edns) != LDNS_RCODE_NOERROR)
goto handle_it;
prs->flags &= ~BIT_CD;
if(!scrub_message(pkt, prs, &iq->qchase, iq->dp->name,
qstate->env->scratch, qstate->env, ie))
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->addr, qstate->reply->addrlen);
if(verbosity >= VERB_ALGO)
log_dns_msg("incoming scrubbed packet:", &iq->response->qinfo,
iq->response->rep);
if(event == module_event_capsfail || iq->caps_fallback) {
if(!iq->caps_fallback) {
iq->caps_fallback = 1;
iq->caps_server = 0;
iq->caps_reply = iq->response->rep;
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_server = -1;
} 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);
(void)error_response(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)) {
(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");
(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
return;
}
log_err("bad event for iterator");
(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[0] == 0)
free(iq->target_count);
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",
&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;
}