diff --git a/src/dnssec.c b/src/dnssec.c index 3e26f7b7..7674636c 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -736,6 +736,8 @@ typedef struct chain_node chain_node; struct chain_head { struct mem_funcs my_mf; + size_t lock; + chain_head *next; chain_node *parent; size_t node_count; /* Number of nodes attached directly @@ -857,6 +859,7 @@ static chain_head *add_rrset2val_chain(struct mem_funcs *mf, head = *chain_p = (chain_head *)region; head->my_mf = *mf; + head->lock = 1; head->next = NULL; head->rrset.name = head->name_spc; memcpy(head->name_spc, rrset->name, dname_len); @@ -1319,6 +1322,7 @@ static void val_chain_node_cb(getdns_dns_req *dnsreq) default : check_chain_complete(node->chains); return; } + node->lock++; n_signers = 0; for ( i = rrset_iter_init(&i_spc,netreq->response,netreq->response_len) ; i @@ -1344,6 +1348,7 @@ static void val_chain_node_cb(getdns_dns_req *dnsreq) */ val_chain_sched_soa_node(node->parent); + node->lock--; check_chain_complete(node->chains); } @@ -1363,17 +1368,21 @@ static void val_chain_node_soa_cb(getdns_dns_req *dnsreq) ! _dname_equal(node->ds.name, rrset->name)) node = node->parent; - if (node) + if (node) { + node->lock++; val_chain_sched_ds_node(node); - else { + } else { /* SOA for a different name */ node = (chain_node *)dnsreq->user_pointer; + node->lock++; val_chain_sched_soa_node(node->parent); } - } else if (node->parent) + } else if (node->parent) { + node->lock++; val_chain_sched_soa_node(node->parent); - + } + node->lock--; check_chain_complete(node->chains); } @@ -3003,7 +3012,7 @@ static size_t count_outstanding_requests(chain_head *head) if (!head) return 0; - for ( node = head->parent, count = 0 + for ( node = head->parent, count = head->lock ; node ; node = node->parent) { @@ -3245,6 +3254,7 @@ static void check_chain_complete(chain_head *chain) } /* Final user callback */ + dnsreq->validating = 0; _getdns_call_user_callback(dnsreq, response_dict); } @@ -3252,7 +3262,11 @@ static void check_chain_complete(chain_head *chain) void _getdns_get_validation_chain(getdns_dns_req *dnsreq) { getdns_network_req *netreq, **netreq_p; - chain_head *chain = NULL; + chain_head *chain = NULL, *chain_p; + + if (dnsreq->validating) + return; + dnsreq->validating = 1; for (netreq_p = dnsreq->netreqs; (netreq = *netreq_p) ; netreq_p++) { if (! netreq->response @@ -3277,11 +3291,15 @@ void _getdns_get_validation_chain(getdns_dns_req *dnsreq) , netreq ); } - if (chain) + if (chain) { + for (chain_p = chain; chain_p; chain_p = chain_p->next) + chain_p->lock--; check_chain_complete(chain); - else + } else { + dnsreq->validating = 0; _getdns_call_user_callback(dnsreq, _getdns_create_getdns_response(dnsreq)); + } } diff --git a/src/request-internal.c b/src/request-internal.c index b209a971..52f91d89 100644 --- a/src/request-internal.c +++ b/src/request-internal.c @@ -903,6 +903,7 @@ _getdns_dns_req_new(getdns_context *context, getdns_eventloop *loop, result->finished_next = NULL; result->freed = NULL; + result->validating = 0; network_req_init(result->netreqs[0], result, request_type, dnssec_extension_set, with_opt, diff --git a/src/types-internal.h b/src/types-internal.h index b70b59f6..1a49b8e5 100644 --- a/src/types-internal.h +++ b/src/types-internal.h @@ -304,10 +304,15 @@ typedef struct getdns_dns_req { int dnssec_ok_checking_disabled; int is_sync_request; - /* Integer pointed to by pointer will be set to 1 (if set), - * before the request is freed. - * To be used by _getdns_submit_netreq only! + /* The validating and freed variables are used to make sure a single + * code path is followed while processing a DNS request, even when + * callbacks are already fired whilst the registering/scheduling call + * (i.e. ub_resolve_event) has not returned yet. + * + * validating is touched by _getdns_get_validation_chain only and + * freed is touched by _getdns_submit_netreq only */ + int validating; int *freed; /* internally scheduled request */