diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c index 5517db149d..2a806e48d3 100644 --- a/src/resolve/resolved-dns-query.c +++ b/src/resolve/resolved-dns-query.c @@ -42,6 +42,8 @@ static void dns_query_candidate_stop(DnsQueryCandidate *c) { assert(c); + /* Detach all the DnsTransactions attached to this query */ + while ((t = set_steal_first(c->transactions))) { set_remove(t->notify_query_candidates, c); set_remove(t->notify_query_candidates_done, c); @@ -49,21 +51,34 @@ static void dns_query_candidate_stop(DnsQueryCandidate *c) { } } +static DnsQueryCandidate* dns_query_candidate_unlink(DnsQueryCandidate *c) { + assert(c); + + /* Detach this DnsQueryCandidate from the Query and Scope objects */ + + if (c->query) { + LIST_REMOVE(candidates_by_query, c->query->candidates, c); + c->query = NULL; + } + + if (c->scope) { + LIST_REMOVE(candidates_by_scope, c->scope->query_candidates, c); + c->scope = NULL; + } + + return c; +} + static DnsQueryCandidate* dns_query_candidate_free(DnsQueryCandidate *c) { if (!c) return NULL; dns_query_candidate_stop(c); + dns_query_candidate_unlink(c); set_free(c->transactions); dns_search_domain_unref(c->search_domain); - if (c->query) - LIST_REMOVE(candidates_by_query, c->query->candidates, c); - - if (c->scope) - LIST_REMOVE(candidates_by_scope, c->scope->query_candidates, c); - return mfree(c); } @@ -105,6 +120,7 @@ static int dns_query_candidate_add_transaction( int r; assert(c); + assert(c->query); /* We shan't add transactions to a candidate that has been detached already */ if (key) { /* Regular lookup with a resource key */ @@ -223,6 +239,7 @@ static int dns_query_candidate_setup_transactions(DnsQueryCandidate *c) { int n = 0, r; assert(c); + assert(c->query); /* We shan't add transactions to a candidate that has been detached already */ dns_query_candidate_stop(c); @@ -280,6 +297,9 @@ void dns_query_candidate_notify(DnsQueryCandidate *c) { assert(c); + if (!c->query) /* This candidate has been abandoned, do nothing. */ + return; + state = dns_query_candidate_state(c); if (DNS_TRANSACTION_IS_LIVE(state)) @@ -330,11 +350,13 @@ static void dns_query_stop(DnsQuery *q) { dns_query_candidate_stop(c); } -static void dns_query_unref_candidates(DnsQuery *q) { +static void dns_query_unlink_candidates(DnsQuery *q) { assert(q); while (q->candidates) - dns_query_candidate_unref(q->candidates); + /* Here we drop *our* references to each of the candidates. If we had the only reference, the + * DnsQueryCandidate object will be freed. */ + dns_query_candidate_unref(dns_query_candidate_unlink(q->candidates)); } static void dns_query_reset_answer(DnsQuery *q) { @@ -364,7 +386,7 @@ DnsQuery *dns_query_free(DnsQuery *q) { LIST_REMOVE(auxiliary_queries, q->auxiliary_for->auxiliary_queries, q); } - dns_query_unref_candidates(q); + dns_query_unlink_candidates(q); dns_question_unref(q->question_idna); dns_question_unref(q->question_utf8); @@ -1025,7 +1047,7 @@ static int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname) dns_question_unref(q->question_utf8); q->question_utf8 = TAKE_PTR(nq_utf8); - dns_query_unref_candidates(q); + dns_query_unlink_candidates(q); /* Note that we do *not* reset the answer here, because the answer we previously got might already * include everything we need, let's check that first */