mirror of
https://github.com/NLnetLabs/unbound.git
synced 2025-12-20 23:00:56 -05:00
- subrequests without parent store in cache and stop.
- worker slumber list for ongoing promiscuous queries.
- subrequest error handling.
git-svn-id: file:///svn/unbound/trunk@368 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
parent
7a882802b6
commit
e98df72b44
6 changed files with 205 additions and 49 deletions
|
|
@ -79,16 +79,11 @@ worker_send_cmd(struct worker* worker, ldns_buffer* buffer,
|
|||
|
||||
/** 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);
|
||||
|
|
@ -96,12 +91,55 @@ qstate_free(struct worker* worker, struct module_qstate* qstate)
|
|||
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.
|
||||
*/
|
||||
|
|
@ -237,7 +275,7 @@ worker_process_query(struct worker* worker, struct work_query* w,
|
|||
qstate_free(worker, qstate);
|
||||
qstate = up;
|
||||
entry = NULL;
|
||||
event = module_event_error;
|
||||
event = module_event_subq_error;
|
||||
continue;
|
||||
}
|
||||
if(s == module_finished && qstate->parent) {
|
||||
|
|
@ -261,16 +299,21 @@ worker_process_query(struct worker* worker, struct work_query* w,
|
|||
}
|
||||
/* 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 */
|
||||
|
|
@ -312,7 +355,7 @@ worker_handle_service_reply(struct comm_point* c, void* arg, int error,
|
|||
{
|
||||
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) {
|
||||
|
|
@ -656,7 +699,8 @@ reqs_delete(struct worker* worker)
|
|||
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;
|
||||
|
|
@ -751,6 +795,7 @@ worker_init(struct worker* worker, struct config_file *cfg,
|
|||
worker_delete(worker);
|
||||
return 0;
|
||||
}
|
||||
worker->slumber_list = NULL;
|
||||
|
||||
server_stats_init(&worker->stats);
|
||||
alloc_init(&worker->alloc, &worker->daemon->superalloc,
|
||||
|
|
@ -775,6 +820,7 @@ worker_delete(struct worker* worker)
|
|||
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);
|
||||
|
|
|
|||
|
|
@ -116,6 +116,8 @@ struct worker {
|
|||
struct work_query* free_queries;
|
||||
/** list of all working queries */
|
||||
struct work_query* all_queries;
|
||||
/** list of slumbering states, with promiscuous queries */
|
||||
struct module_qstate* slumber_list;
|
||||
|
||||
/** random() table for this worker. */
|
||||
struct ub_randstate* rndstate;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
5 June 2007: Wouter
|
||||
- iterator state finished.
|
||||
- subrequests without parent store in cache and stop.
|
||||
- worker slumber list for ongoing promiscuous queries.
|
||||
- subrequest error handling.
|
||||
|
||||
4 June 2007: Wouter
|
||||
- random selection of equally preferred nameserver targets.
|
||||
|
|
|
|||
|
|
@ -253,11 +253,30 @@ final_state(struct module_qstate* qstate, struct iter_qstate* iq)
|
|||
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;
|
||||
}
|
||||
|
|
@ -468,6 +487,7 @@ generate_sub_request(uint8_t* qname, size_t qnamelen, uint16_t qtype,
|
|||
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];
|
||||
|
|
@ -571,7 +591,7 @@ prime_stub(struct module_qstate* qstate, struct iter_qstate* iq,
|
|||
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];
|
||||
|
|
@ -844,7 +864,10 @@ generate_target_query(struct module_qstate* qstate, struct iter_qstate* iq,
|
|||
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;
|
||||
|
|
@ -1082,7 +1105,6 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
|
|||
{
|
||||
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);
|
||||
|
|
@ -1214,7 +1236,14 @@ processPrimeResponse(struct module_qstate* qstate, struct iter_qstate* iq,
|
|||
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);
|
||||
|
|
@ -1224,23 +1253,17 @@ processPrimeResponse(struct module_qstate* qstate, struct iter_qstate* iq,
|
|||
* 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
|
||||
|
|
@ -1274,9 +1297,14 @@ processTargetResponse(struct module_qstate* qstate, struct iter_qstate* iq,
|
|||
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)
|
||||
|
|
@ -1460,7 +1488,7 @@ process_response(struct module_qstate* qstate, struct iter_qstate* iq,
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
@ -1496,6 +1524,48 @@ handle_it:
|
|||
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
|
||||
|
|
@ -1514,7 +1584,7 @@ iter_operate(struct module_qstate* qstate, enum module_ev event, int id,
|
|||
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];
|
||||
|
|
@ -1529,14 +1599,24 @@ iter_operate(struct module_qstate* qstate, enum module_ev event, int 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 */
|
||||
|
|
|
|||
|
|
@ -64,28 +64,26 @@ strmodulevent(enum module_ev e)
|
|||
case module_event_timeout: return "module_event_timeout";
|
||||
case module_event_mod_done: return "module_event_mod_done";
|
||||
case module_event_subq_done: return "module_event_subq_done";
|
||||
case module_event_subq_error: return "module_event_subq_error";
|
||||
case module_event_error: return "module_event_error";
|
||||
}
|
||||
return "bad_event_value";
|
||||
}
|
||||
|
||||
void
|
||||
module_subreq_remove(struct module_qstate* sub)
|
||||
module_subreq_remove(struct module_qstate** head, struct module_qstate* sub)
|
||||
{
|
||||
struct module_qstate* p, *prev = NULL;
|
||||
if(!sub || !sub->parent)
|
||||
if(!sub || !head)
|
||||
return;
|
||||
p = sub->parent->subquery_first;
|
||||
while(p) {
|
||||
if(p == sub) {
|
||||
/* snip it off */
|
||||
if(prev) prev->subquery_next = p->subquery_next;
|
||||
else sub->parent->subquery_first = p->subquery_next;
|
||||
return;
|
||||
}
|
||||
prev = p;
|
||||
p = p->subquery_next;
|
||||
}
|
||||
/* snip off double linked list */
|
||||
if(sub->subquery_prev)
|
||||
sub->subquery_prev->subquery_next = sub->subquery_next;
|
||||
else *head = sub->subquery_next;
|
||||
if(sub->subquery_next)
|
||||
sub->subquery_next->subquery_prev = sub->subquery_prev;
|
||||
/* cleanup values for robustness */
|
||||
sub->subquery_next = NULL;
|
||||
sub->subquery_prev = NULL;
|
||||
}
|
||||
|
||||
int
|
||||
|
|
@ -98,3 +96,14 @@ module_subreq_depth(struct module_qstate* sub)
|
|||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
int
|
||||
module_subreq_num(struct module_qstate* q)
|
||||
{
|
||||
int n = 0;
|
||||
while(q) {
|
||||
n++;
|
||||
q = q->subquery_next;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -163,6 +163,8 @@ enum module_ev {
|
|||
module_event_mod_done,
|
||||
/** subquery finished */
|
||||
module_event_subq_done,
|
||||
/** subquery finished with error */
|
||||
module_event_subq_error,
|
||||
/** error */
|
||||
module_event_error
|
||||
};
|
||||
|
|
@ -198,15 +200,19 @@ struct module_qstate {
|
|||
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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -271,10 +277,13 @@ const char* strmodulevent(enum module_ev e);
|
|||
|
||||
/**
|
||||
* 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
|
||||
|
|
@ -283,4 +292,11 @@ void module_subreq_remove(struct module_qstate* sub);
|
|||
*/
|
||||
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 */
|
||||
|
|
|
|||
Loading…
Reference in a new issue