/** delete subrequest */
static void
-qstate_free(struct worker* worker, struct module_qstate* qstate)
+qstate_cleanup(struct worker* worker, struct module_qstate* qstate)
{
int i;
if(!qstate)
return;
- /* remove subqueries */
- while(qstate->subquery_first) {
- qstate_free(worker, qstate->subquery_first);
- }
- log_assert(qstate->subquery_first == NULL);
/* call de-init while all is OK */
for(i=0; i<worker->daemon->num_modules; i++)
(*worker->daemon->modfunc[i]->clear)(qstate, i);
region_free_all(qstate->region);
query_info_clear(&qstate->qinfo);
if(qstate->parent) {
- module_subreq_remove(qstate);
+ /* subquery of parent */
+ module_subreq_remove(&qstate->parent->subquery_first, qstate);
+ region_destroy(qstate->region);
+ free(qstate);
+ } else if (!qstate->work_info) {
+ /* slumbering query */
+ module_subreq_remove(&worker->slumber_list, qstate);
region_destroy(qstate->region);
free(qstate);
+ verbose(VERB_ALGO, "cleanup: slumber list has %d entries",
+ module_subreq_num(worker->slumber_list));
+ }
+}
+
+/** delete subrequest recursively */
+static void
+qstate_free_recurs_list(struct worker* worker, struct module_qstate* list)
+{
+ struct module_qstate* n;
+ /* remove subqueries */
+ while(list) {
+ n = list->subquery_next;
+ qstate_free_recurs_list(worker, list->subquery_first);
+ qstate_cleanup(worker, list);
+ list = n;
}
}
+/** delete subrequest */
+static void
+qstate_free(struct worker* worker, struct module_qstate* qstate)
+{
+ if(!qstate)
+ return;
+ while(qstate->subquery_first) {
+ /* put subqueries on slumber list */
+ struct module_qstate* s = qstate->subquery_first;
+ module_subreq_remove(&qstate->subquery_first, s);
+ s->parent = NULL;
+ s->work_info = NULL;
+ s->subquery_next = worker->slumber_list;
+ s->subquery_prev = NULL;
+ worker->slumber_list = s;
+ }
+ verbose(VERB_ALGO, "worker: slumber list has %d entries",
+ module_subreq_num(worker->slumber_list));
+ qstate_cleanup(worker, qstate);
+}
+
/** release workrequest back to the freelist,
* note that the w->qinfo still needs to be cleared after this.
*/
qstate_free(worker, qstate);
qstate = up;
entry = NULL;
- event = module_event_error;
+ event = module_event_subq_error;
continue;
}
if(s == module_finished && qstate->parent) {
}
/* request done */
if(s == module_error) {
- replyerror(LDNS_RCODE_SERVFAIL, w);
+ if(w) {
+ replyerror(LDNS_RCODE_SERVFAIL, w);
+ req_release(w);
+ }
qstate_free(worker, qstate);
return;
}
if(s == module_finished) {
- memcpy(ldns_buffer_begin(w->query_reply.c->buffer),
- &w->query_id, sizeof(w->query_id));
- comm_point_send_reply(&w->query_reply);
+ if(w) {
+ memcpy(ldns_buffer_begin(w->query_reply.c->buffer),
+ &w->query_id, sizeof(w->query_id));
+ comm_point_send_reply(&w->query_reply);
+ req_release(w);
+ }
qstate_free(worker, qstate);
- req_release(w);
return;
}
/* suspend, waits for wakeup callback */
{
struct outbound_entry* e = (struct outbound_entry*)arg;
struct work_query* w = e->qstate->work_info;
- struct worker* worker = w->state.env->worker;
+ struct worker* worker = e->qstate->env->worker;
e->qstate->reply = reply_info;
if(error != 0) {
n = q->all_next;
log_assert(q->state.env->worker == worker);
/* comm_reply closed in outside_network_delete */
- qstate_free(worker, &q->state);
+ qstate_free_recurs_list(worker, q->state.subquery_first);
+ qstate_cleanup(worker, &q->state);
region_destroy(q->state.region);
free(q);
q = n;
worker_delete(worker);
return 0;
}
+ worker->slumber_list = NULL;
server_stats_init(&worker->stats);
alloc_init(&worker->alloc, &worker->daemon->superalloc,
return;
server_stats_log(&worker->stats, worker->thread_num);
reqs_delete(worker);
+ qstate_free_recurs_list(worker, worker->slumber_list);
listen_delete(worker->front);
outside_network_delete(worker->back);
comm_signal_delete(worker->comsig);
static int
error_response(struct module_qstate* qstate, int id, int rcode)
{
+ struct iter_qstate* iq = (struct iter_qstate*)qstate->minfo[id];
log_info("err response %s", ldns_lookup_by_id(ldns_rcodes, rcode)?
ldns_lookup_by_id(ldns_rcodes, rcode)->name:"??");
+ if(iq && iq->orig_qname) {
+ /* encode original query */
+ qstate->qinfo.qname = iq->orig_qname;
+ qstate->qinfo.qname_len = iq->orig_qnamelen;
+ qstate->query_flags = iq->orig_qflags;
+ }
qinfo_query_encode(qstate->buf, &qstate->qinfo);
LDNS_RCODE_SET(ldns_buffer_begin(qstate->buf), rcode);
+ LDNS_RA_SET(ldns_buffer_begin(qstate->buf));
LDNS_QR_SET(ldns_buffer_begin(qstate->buf));
+ if((qstate->query_flags & BIT_RD))
+ LDNS_RD_SET(ldns_buffer_begin(qstate->buf));
+ if((qstate->query_flags & BIT_CD))
+ LDNS_CD_SET(ldns_buffer_begin(qstate->buf));
+
+ if(qstate->parent) {
+ /* return subquery error module event to parent */
+ qstate->ext_state[id] = module_error;
+ return 0;
+ }
+ /* return to client */
qstate->ext_state[id] = module_finished;
return 0;
}
subq->work_info = qstate->work_info;
subq->parent = qstate;
subq->subquery_next = qstate->subquery_first;
+ subq->subquery_prev = NULL;
qstate->subquery_first = subq;
subiq = (struct iter_qstate*)subq->minfo[id];
QUERYTARGETS_STATE, PRIME_RESP_STATE);
if(!subq) {
log_err("out of memory priming stub");
- qstate->ext_state[id] = module_error;
+ (void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
return 1; /* return 1 to make module stop, with error */
}
subiq = (struct iter_qstate*)subq->minfo[id];
subiq = (struct iter_qstate*)subq->minfo[id];
subiq->dp = delegpt_copy(iq->dp, subq->region);
if(!subiq->dp) {
- subq->ext_state[id] = module_error;
+ module_subreq_remove(&qstate->subquery_first, subq);
+ region_destroy(subq->region);
+ free(subq->qinfo.qname);
+ free(subq);
return 0;
}
return 1;
{
enum response_type type;
iq->num_current_queries--;
- qstate->ext_state[id] = module_error; /* debug, must be overridden */
if(iq->response == NULL) {
verbose(VERB_ALGO, "query response was timeout");
return next_state(qstate, iq, QUERYTARGETS_STATE);
struct delegpt* dp = NULL;
enum response_type type = response_type_from_server(iq->response,
&qstate->qinfo, iq->dp);
- log_assert(qstate->parent); /* this is a subrequest of another */
+
+ /* This event is finished. */
+ qstate->ext_state[id] = module_finished;
+
+ if(!qstate->parent) {
+ /* no more parent - it is not interested anymore */
+ return 0;
+ }
if(type == RESPONSE_TYPE_ANSWER) {
/* Convert our response to a delegation point */
dp = delegpt_from_message(iq->response, forq->region);
* the ANSWER type was (presumably) a negative answer. */
verbose(VERB_ALGO, "prime response was not a positive "
"ANSWER; failing");
- /* note that this will call the forevent with event error. */
- qstate->ext_state[id] = module_error;
- return 0;
+ return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
log_nametypeclass("priming successful for", qstate->qinfo.qname,
qstate->qinfo.qtype, qstate->qinfo.qclass);
- /* This event is finished. */
- qstate->ext_state[id] = module_finished;
foriq = (struct iter_qstate*)forq->minfo[id];
foriq->dp = dp;
foriq->response = dns_copy_msg(iq->response, forq->region);
if(!foriq->response) {
log_err("copy prime response: out of memory");
- /* note that this will call the forevent with event error. */
- qstate->ext_state[id] = module_error;
- return 0;
+ return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
/* root priming responses go to init stage 2, priming stub
struct delegpt_ns* dpns;
struct module_qstate* forq = qstate->parent;
struct iter_qstate* foriq;
- log_assert(qstate->parent); /* fetch targets for a parent */
- foriq = (struct iter_qstate*)forq->minfo[id];
+
qstate->ext_state[id] = module_finished;
+ if(!qstate->parent) {
+ /* no parent, it is not interested anymore */
+ return 0;
+ }
+
+ foriq = (struct iter_qstate*)forq->minfo[id];
/* check to see if parent event is still interested. */
if(iq->orig_qname)
if(event != module_event_reply || !qstate->reply) {
log_err("Bad event combined with response");
outbound_list_remove(&iq->outlist, outbound);
- qstate->ext_state[id] = module_error;
+ (void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
return;
}
outbound_list_remove(&iq->outlist, outbound);
iter_handle(qstate, iq, ie, id);
}
+/**
+ * Handles subquery errors. Checks if query is still relevant, and adjusts
+ * the state.
+ * @param qstate: query state.
+ * @param ie: iterator shared global environment.
+ * @param iq: iterator query state.
+ * @param id: module id.
+ */
+static void
+process_subq_error(struct module_qstate* qstate, struct iter_qstate* iq,
+ struct iter_env* ie, int id)
+{
+ struct query_info errinf;
+ struct delegpt_ns* dpns = NULL;
+ if(!query_info_parse(&errinf, qstate->buf)) {
+ log_err("Could not parse error from sub module");
+ return;
+ }
+ if(errinf.qtype == LDNS_RR_TYPE_NS) {
+ /* a priming query has failed. */
+ iter_handle(qstate, iq, ie, id);
+ return;
+ }
+ if(errinf.qtype != LDNS_RR_TYPE_A &&
+ errinf.qtype != LDNS_RR_TYPE_AAAA) {
+ log_err("Bad error from sub module");
+ return;
+ }
+ /* see if we are still interested in this subquery result */
+
+ if(!iq->dp)
+ dpns = delegpt_find_ns(iq->dp, errinf.qname,
+ errinf.qname_len);
+ if(!dpns) {
+ /* not interested */
+ return;
+ }
+ dpns->resolved = 1; /* mark as failed */
+ iq->num_target_queries--; /* and the query is finished */
+ iq->state = QUERYTARGETS_STATE; /* evaluate targets again */
+ iter_handle(qstate, iq, ie, id);
+}
/** iterator operate on a query */
static void
if(event == module_event_new && iq == NULL) {
log_info("iter state machine");
if(!iter_new(qstate, id)) {
- qstate->ext_state[id] = module_error;
+ (void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
return;
}
iq = (struct iter_qstate*)qstate->minfo[id];
process_response(qstate, iq, ie, id, outbound, event);
return;
}
+ if(event == module_event_subq_done) {
+ /* subquery has set our state correctly */
+ iter_handle(qstate, iq, ie, id);
+ return;
+ }
+ if(event == module_event_subq_error) {
+ /* need to delist subquery and continue processing */
+ process_subq_error(qstate, iq, ie, id);
+ return;
+ }
if(event == module_event_error) {
verbose(VERB_ALGO, "got called with event error, giving up");
- qstate->ext_state[id] = module_error;
+ (void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
return;
}
log_err("bad event for iterator");
- qstate->ext_state[id] = module_error;
+ (void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
/** iterator cleanup query state */
module_event_mod_done,
/** subquery finished */
module_event_subq_done,
+ /** subquery finished with error */
+ module_event_subq_error,
/** error */
module_event_error
};
void* minfo[MAX_MODULE];
/** environment for this query */
struct module_env* env;
- /** worker related state for this query */
+ /** worker related state for this query. NULL for queries that do
+ * not need to have answers sent to a client. */
struct work_query* work_info;
/** parent query, only nonNULL for subqueries */
struct module_qstate* parent;
/** pointer to first subquery below this one; makes list with next */
struct module_qstate* subquery_first;
+
/** pointer to next sibling subquery (not above or below this one) */
struct module_qstate* subquery_next;
+ /** pointer to prev sibling subquery (not above or below this one) */
+ struct module_qstate* subquery_prev;
};
/**
/**
* Remove subqrequest from list.
+ * @param head: List head. pointer to start of subquery_next/prev sibling list.
+ * mostly reference to the parent subquery_first.
* @param sub: subrequest. Parent pointer used to access list.
* It is snipped off.
*/
-void module_subreq_remove(struct module_qstate* sub);
+void module_subreq_remove(struct module_qstate** head,
+ struct module_qstate* sub);
/**
* Calculate depth of subrequest
*/
int module_subreq_depth(struct module_qstate* sub);
+/**
+ * Calculate number of queries in the query list.
+ * @param q: the start of the list, pass subquery_first.
+ * @return: number, 0 if q was NULL.
+ */
+int module_subreq_num(struct module_qstate* q);
+
#endif /* UTIL_MODULE_H */