fix: usr: Fix the spurious timeouts while resolving names

Sometimes the loops in the resolving (e.g. to resolve or validate ns1.example.com we need to resolve ns1.example.com) were not properly detected leading to spurious 10 seconds delay.  This has been fixed and such loops are properly detected.

Closes #3033, #5578

Merge branch '5578-tracker-parent-fetch' into 'main'

See merge request isc-projects/bind9!11138
This commit is contained in:
Ondřej Surý 2025-11-27 17:34:42 +01:00
commit 908b7c1f34
15 changed files with 103 additions and 48 deletions

View file

@ -6745,7 +6745,7 @@ tat_send(void *arg) {
result = dns_resolver_createfetch(
tat->view->resolver, tatname, dns_rdatatype_null,
domain, &nameservers, NULL, NULL, 0, 0, 0, NULL, NULL,
tat->loop, tat_done, tat, NULL, &tat->rdataset,
NULL, tat->loop, tat_done, tat, NULL, &tat->rdataset,
&tat->sigrdataset, &tat->fetch);
}

View file

@ -356,7 +356,7 @@ static isc_result_t
dbfind_name(dns_adbname_t *, isc_stdtime_t, dns_rdatatype_t);
static isc_result_t
fetch_name(dns_adbname_t *, bool, bool, unsigned int, isc_counter_t *qc,
isc_counter_t *gqc, dns_rdatatype_t);
isc_counter_t *gqc, fetchctx_t *parent, dns_rdatatype_t);
static void
shutdown_names(dns_adb_t *);
static void
@ -1717,7 +1717,7 @@ isc_result_t
dns_adb_createfind(dns_adb_t *adb, isc_loop_t *loop, isc_job_cb cb, void *cbarg,
const dns_name_t *name, unsigned int options,
isc_stdtime_t now, in_port_t port, unsigned int depth,
isc_counter_t *qc, isc_counter_t *gqc,
isc_counter_t *qc, isc_counter_t *gqc, fetchctx_t *parent,
dns_adbfind_t **findp) {
isc_result_t result = ISC_R_UNEXPECTED;
dns_adbfind_t *find = NULL;
@ -1922,7 +1922,7 @@ fetch:
*/
if (WANT_INET(wanted_fetches) &&
fetch_name(adbname, start_at_zone, no_validate, depth, qc,
gqc, dns_rdatatype_a) == ISC_R_SUCCESS)
gqc, parent, dns_rdatatype_a) == ISC_R_SUCCESS)
{
DP(DEF_LEVEL,
"dns_adb_createfind: "
@ -1935,7 +1935,8 @@ fetch:
*/
if (WANT_INET6(wanted_fetches) &&
fetch_name(adbname, start_at_zone, no_validate, depth, qc,
gqc, dns_rdatatype_aaaa) == ISC_R_SUCCESS)
gqc, parent,
dns_rdatatype_aaaa) == ISC_R_SUCCESS)
{
DP(DEF_LEVEL,
"dns_adb_createfind: "
@ -2719,7 +2720,7 @@ out:
static isc_result_t
fetch_name(dns_adbname_t *adbname, bool start_at_zone, bool no_validation,
unsigned int depth, isc_counter_t *qc, isc_counter_t *gqc,
dns_rdatatype_t type) {
fetchctx_t *parent, dns_rdatatype_t type) {
isc_result_t result;
dns_adbfetch_t *fetch = NULL;
dns_adb_t *adb = NULL;
@ -2774,8 +2775,8 @@ fetch_name(dns_adbname_t *adbname, bool start_at_zone, bool no_validation,
dns_adbname_ref(adbname);
result = dns_resolver_createfetch(
adb->res, adbname->name, type, name, nameservers, NULL, NULL, 0,
options, depth, qc, gqc, isc_loop(), fetch_callback, adbname,
NULL, &fetch->rdataset, NULL, &fetch->fetch);
options, depth, qc, gqc, parent, isc_loop(), fetch_callback,
adbname, NULL, &fetch->rdataset, NULL, &fetch->fetch);
if (result != ISC_R_SUCCESS) {
DP(ENTER_LEVEL, "fetch_name: createfetch failed with %s",
isc_result_totext(result));

View file

@ -455,8 +455,8 @@ start_fetch(resctx_t *rctx) {
result = dns_resolver_createfetch(
rctx->view->resolver, dns_fixedname_name(&rctx->name),
rctx->type, NULL, NULL, NULL, NULL, 0, fopts, 0, NULL, rctx->qc,
rctx->client->loop, fetch_done, rctx, NULL, rctx->rdataset,
rctx->sigrdataset, &rctx->fetch);
NULL, rctx->client->loop, fetch_done, rctx, NULL,
rctx->rdataset, rctx->sigrdataset, &rctx->fetch);
return result;
}

View file

@ -278,7 +278,8 @@ isc_result_t
dns_adb_createfind(dns_adb_t *adb, isc_loop_t *loop, isc_job_cb cb, void *cbarg,
const dns_name_t *name, unsigned int options,
isc_stdtime_t now, in_port_t port, unsigned int depth,
isc_counter_t *qc, isc_counter_t *gqc, dns_adbfind_t **find);
isc_counter_t *qc, isc_counter_t *gqc, fetchctx_t *parent,
dns_adbfind_t **find);
/*%<
* Main interface for clients. The adb will look up the name given in
* "name" and will build up a list of found addresses, and perhaps start

View file

@ -280,9 +280,10 @@ dns_resolver_createfetch(dns_resolver_t *res, const dns_name_t *name,
const isc_sockaddr_t *client, dns_messageid_t id,
unsigned int options, unsigned int depth,
isc_counter_t *qc, isc_counter_t *gqc,
isc_loop_t *loop, isc_job_cb cb, void *arg,
dns_edectx_t *edectx, dns_rdataset_t *rdataset,
dns_rdataset_t *sigrdataset, dns_fetch_t **fetchp);
fetchctx_t *parent, isc_loop_t *loop, isc_job_cb cb,
void *arg, dns_edectx_t *edectx,
dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset,
dns_fetch_t **fetchp);
/*%<
* Recurse to answer a question.
*

View file

@ -156,6 +156,7 @@ struct dns_validator {
isc_counter_t *nfails;
isc_counter_t *qc;
isc_counter_t *gqc;
fetchctx_t *parent_fetch;
dns_edectx_t edectx;
@ -176,7 +177,7 @@ dns_validator_create(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type,
dns_message_t *message, unsigned int options,
isc_loop_t *loop, isc_job_cb cb, void *arg,
isc_counter_t *nvalidations, isc_counter_t *nfails,
isc_counter_t *qc, isc_counter_t *gqc,
isc_counter_t *qc, isc_counter_t *gqc, fetchctx_t *parent,
dns_edectx_t *edectx, dns_validator_t **validatorp);
/*%<
* Start a DNSSEC validation.

View file

@ -730,7 +730,7 @@ dns_notify_find_address(dns_notify_t *notify) {
result = dns_adb_createfind(adb, loop, process_notify_adb_event, notify,
&notify->ns, options, 0, view->dstport, 0,
NULL, NULL, &notify->find);
NULL, NULL, NULL, &notify->find);
dns_adb_detach(&adb);
/* Something failed? */

View file

@ -237,7 +237,7 @@ checkbogus(void *arg) {
dns__nta_ref(nta); /* for dns_resolver_createfetch */
result = dns_resolver_createfetch(
resolver, &nta->name, dns_rdatatype_nsec, NULL, NULL, NULL,
NULL, 0, DNS_FETCHOPT_NONTA, 0, NULL, NULL, nta->loop,
NULL, 0, DNS_FETCHOPT_NONTA, 0, NULL, NULL, NULL, nta->loop,
fetch_done, nta, NULL, &nta->rdataset, &nta->sigrdataset,
&nta->fetch);
if (result != ISC_R_SUCCESS) {

View file

@ -473,6 +473,7 @@ struct fetchctx {
isc_counter_t *nvalidations;
isc_counter_t *nfails;
fetchctx_t *parent;
struct cds_lfht_node ht_node;
struct rcu_head rcu_head;
@ -813,7 +814,8 @@ get_attached_fctx(dns_resolver_t *res, isc_loop_t *loop, const dns_name_t *name,
dns_rdatatype_t type, const dns_name_t *domain,
dns_rdataset_t *nameservers, const isc_sockaddr_t *client,
unsigned int options, unsigned int depth, isc_counter_t *qc,
isc_counter_t *gqc, fetchctx_t **fctxp, bool *new_fctx);
isc_counter_t *gqc, fetchctx_t *parent, fetchctx_t **fctxp,
bool *new_fctx);
/*%
* The structure and functions defined below implement the resolver
@ -1093,7 +1095,8 @@ valcreate(fetchctx_t *fctx, dns_message_t *message, dns_adbaddrinfo_t *addrinfo,
result = dns_validator_create(
fctx->res->view, name, type, rdataset, sigrdataset, message,
valoptions, fctx->loop, validated, valarg, fctx->nvalidations,
fctx->nfails, fctx->qc, fctx->gqc, &fctx->edectx, &validator);
fctx->nfails, fctx->qc, fctx->gqc, fctx, &fctx->edectx,
&validator);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
inc_stats(fctx->res, dns_resstatscounter_val);
ISC_LIST_APPEND(fctx->validators, validator, link);
@ -3349,7 +3352,7 @@ findname(fetchctx_t *fctx, const dns_name_t *name, in_port_t port,
fetchctx_ref(fctx);
result = dns_adb_createfind(fctx->adb, fctx->loop, fctx_finddone, fctx,
name, options, now, res->view->dstport,
fctx->depth + 1, fctx->qc, fctx->gqc,
fctx->depth + 1, fctx->qc, fctx->gqc, fctx,
&find);
isc_log_write(DNS_LOGCATEGORY_RESOLVER, DNS_LOGMODULE_RESOLVER,
@ -4231,8 +4234,8 @@ fctx_try(fetchctx_t *fctx, bool retrying) {
fctx->res, fctx->qminname, fctx->qmintype, fctx->domain,
&fctx->nameservers, NULL, NULL, 0,
options | DNS_FETCHOPT_QMINFETCH, 0, fctx->qc,
fctx->gqc, fctx->loop, resume_qmin, fctx, &fctx->edectx,
&fctx->qminrrset, &fctx->qminsigrrset,
fctx->gqc, fctx, fctx->loop, resume_qmin, fctx,
&fctx->edectx, &fctx->qminrrset, &fctx->qminsigrrset,
&fctx->qminfetch);
if (result != ISC_R_SUCCESS) {
fetchctx_unref(fctx);
@ -4629,6 +4632,9 @@ fctx__destroy(fetchctx_t *fctx, const char *func, const char *file,
if (fctx->gqc != NULL) {
isc_counter_detach(&fctx->gqc);
}
if (fctx->parent != NULL) {
fetchctx_detach(&fctx->parent);
}
fcount_decr(fctx);
dns_message_detach(&fctx->qmessage);
if (dns_rdataset_isassociated(&fctx->nameservers)) {
@ -4780,17 +4786,17 @@ log_ns_ttl(fetchctx_t *fctx, const char *where) {
}
#define fctx_create(res, loop, name, type, domain, nameservers, client, \
options, depth, qc, gqp, fctxp) \
options, depth, qc, gqp, parent, fctxp) \
fctx__create(res, loop, name, type, domain, nameservers, client, \
options, depth, qc, gqp, fctxp, __func__, __FILE__, \
__LINE__)
options, depth, qc, gqp, parent, fctxp, __func__, \
__FILE__, __LINE__)
static isc_result_t
fctx__create(dns_resolver_t *res, isc_loop_t *loop, const dns_name_t *name,
dns_rdatatype_t type, const dns_name_t *domain,
dns_rdataset_t *nameservers, const isc_sockaddr_t *client,
unsigned int options, unsigned int depth, isc_counter_t *qc,
isc_counter_t *gqc, fetchctx_t **fctxp, const char *func,
const char *file, const unsigned int line) {
isc_counter_t *gqc, fetchctx_t *parent, fetchctx_t **fctxp,
const char *func, const char *file, const unsigned int line) {
fetchctx_t *fctx = NULL;
isc_result_t result;
isc_result_t iresult;
@ -4897,6 +4903,10 @@ fctx__create(dns_resolver_t *res, isc_loop_t *loop, const dns_name_t *name,
isc_counter_used(fctx->gqc));
}
if (parent != NULL) {
fetchctx_attach(parent, &fctx->parent);
}
urcu_ref_set(&fctx->ref, 1);
if (client != NULL) {
@ -5104,6 +5114,9 @@ cleanup_nameservers:
if (fctx->gqc != NULL) {
isc_counter_detach(&fctx->gqc);
}
if (fctx->parent != NULL) {
fetchctx_detach(&fctx->parent);
}
dns_resolver_detach(&fctx->res);
isc_mem_putanddetach(&fctx->mctx, fctx, sizeof(*fctx));
@ -7021,7 +7034,7 @@ resume_dslookup(void *arg) {
result = dns_resolver_createfetch(
res, fctx->nsname, dns_rdatatype_ns, domain, nsrdataset,
NULL, NULL, 0, fctx->options, 0, fctx->qc, fctx->gqc,
loop, resume_dslookup, fctx, &fctx->edectx,
fctx, loop, resume_dslookup, fctx, &fctx->edectx,
&fctx->nsrrset, NULL, &fctx->nsfetch);
if (result != ISC_R_SUCCESS) {
fetchctx_unref(fctx);
@ -9370,9 +9383,9 @@ rctx_chaseds(respctx_t *rctx, dns_message_t *message,
fetchctx_ref(fctx);
result = dns_resolver_createfetch(
fctx->res, fctx->nsname, dns_rdatatype_ns, NULL, NULL, NULL,
NULL, 0, fctx->options, 0, fctx->qc, fctx->gqc, fctx->loop,
resume_dslookup, fctx, &fctx->edectx, &fctx->nsrrset, NULL,
&fctx->nsfetch);
NULL, 0, fctx->options, 0, fctx->qc, fctx->gqc, fctx,
fctx->loop, resume_dslookup, fctx, &fctx->edectx,
&fctx->nsrrset, NULL, &fctx->nsfetch);
if (result != ISC_R_SUCCESS) {
if (result == DNS_R_DUPLICATE) {
result = DNS_R_SERVFAIL;
@ -9953,7 +9966,7 @@ dns_resolver_prime(dns_resolver_t *res) {
LOCK(&res->primelock);
result = dns_resolver_createfetch(
res, dns_rootname, dns_rdatatype_ns, NULL, NULL, NULL,
NULL, 0, DNS_FETCHOPT_NOFORWARD, 0, NULL, NULL,
NULL, 0, DNS_FETCHOPT_NOFORWARD, 0, NULL, NULL, NULL,
isc_loop(), prime_done, res, NULL, rdataset, NULL,
&res->primefetch);
UNLOCK(&res->primelock);
@ -10140,7 +10153,8 @@ get_attached_fctx(dns_resolver_t *res, isc_loop_t *loop, const dns_name_t *name,
dns_rdatatype_t type, const dns_name_t *domain,
dns_rdataset_t *nameservers, const isc_sockaddr_t *client,
unsigned int options, unsigned int depth, isc_counter_t *qc,
isc_counter_t *gqc, fetchctx_t **fctxp, bool *new_fctx) {
isc_counter_t *gqc, fetchctx_t *parent, fetchctx_t **fctxp,
bool *new_fctx) {
isc_result_t result;
fetchctx_t key = {
.name = UNCONST(name),
@ -10161,7 +10175,8 @@ get_attached_fctx(dns_resolver_t *res, isc_loop_t *loop, const dns_name_t *name,
if (fctx == NULL) {
create:
result = fctx_create(res, loop, name, type, domain, nameservers,
client, options, depth, qc, gqc, &fctx);
client, options, depth, qc, gqc, parent,
&fctx);
if (result != ISC_R_SUCCESS) {
rcu_read_unlock();
return result;
@ -10230,6 +10245,18 @@ get_attached_fctx(dns_resolver_t *res, isc_loop_t *loop, const dns_name_t *name,
return ISC_R_SUCCESS;
}
static bool
waiting_for_fetch(fetchctx_t *fctx, const dns_name_t *name,
dns_rdatatype_t type) {
while (fctx != NULL) {
if (type == fctx->type && !dns_name_compare(name, fctx->name)) {
return true;
}
fctx = fctx->parent;
}
return false;
}
isc_result_t
dns_resolver_createfetch(dns_resolver_t *res, const dns_name_t *name,
dns_rdatatype_t type, const dns_name_t *domain,
@ -10238,9 +10265,10 @@ dns_resolver_createfetch(dns_resolver_t *res, const dns_name_t *name,
const isc_sockaddr_t *client, dns_messageid_t id,
unsigned int options, unsigned int depth,
isc_counter_t *qc, isc_counter_t *gqc,
isc_loop_t *loop, isc_job_cb cb, void *arg,
dns_edectx_t *edectx, dns_rdataset_t *rdataset,
dns_rdataset_t *sigrdataset, dns_fetch_t **fetchp) {
fetchctx_t *parent, isc_loop_t *loop, isc_job_cb cb,
void *arg, dns_edectx_t *edectx,
dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset,
dns_fetch_t **fetchp) {
dns_fetch_t *fetch = NULL;
fetchctx_t *fctx = NULL;
isc_result_t result = ISC_R_SUCCESS;
@ -10272,6 +10300,22 @@ dns_resolver_createfetch(dns_resolver_t *res, const dns_name_t *name,
log_fetch(name, type);
if (waiting_for_fetch(parent, name, type)) {
if (isc_log_wouldlog(ISC_LOG_INFO)) {
char namebuf[DNS_NAME_FORMATSIZE + 1];
char typebuf[DNS_RDATATYPE_FORMATSIZE];
dns_name_format(name, namebuf, sizeof(namebuf));
dns_rdatatype_format(type, typebuf, sizeof(typebuf));
isc_log_write(DNS_LOGCATEGORY_RESOLVER,
DNS_LOGMODULE_RESOLVER, ISC_LOG_DEBUG(2),
"fetch loop detected resolving '%s/%s'",
namebuf, typebuf);
}
return DNS_R_LOOPDETECTED;
}
fetch = isc_mem_get(mctx, sizeof(*fetch));
*fetch = (dns_fetch_t){ 0 };
@ -10291,7 +10335,7 @@ dns_resolver_createfetch(dns_resolver_t *res, const dns_name_t *name,
result = get_attached_fctx(res, loop, name, type, domain,
nameservers, client, options, depth,
qc, gqc, &fctx, &new_fctx);
qc, gqc, parent, &fctx, &new_fctx);
if (result != ISC_R_SUCCESS) {
goto fail;
}
@ -10325,7 +10369,8 @@ dns_resolver_createfetch(dns_resolver_t *res, const dns_name_t *name,
}
} else {
result = fctx_create(res, loop, name, type, domain, nameservers,
client, options, depth, qc, gqc, &fctx);
client, options, depth, qc, gqc, parent,
&fctx);
if (result != ISC_R_SUCCESS) {
goto fail;
}

View file

@ -1001,8 +1001,9 @@ create_fetch(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type,
dns_validator_ref(val);
result = dns_resolver_createfetch(
val->view->resolver, name, type, NULL, NULL, NULL, NULL, 0,
fopts, 0, val->qc, val->gqc, val->loop, callback, val,
&val->edectx, &val->frdataset, &val->fsigrdataset, &val->fetch);
fopts, 0, val->qc, val->gqc, val->parent_fetch, val->loop,
callback, val, &val->edectx, &val->frdataset,
&val->fsigrdataset, &val->fetch);
if (result != ISC_R_SUCCESS) {
dns_validator_detach(&val);
}
@ -1039,7 +1040,7 @@ create_validator(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type,
result = dns_validator_create(
val->view, name, type, rdataset, sig, NULL, vopts, val->loop,
cb, val, val->nvalidations, val->nfails, val->qc, val->gqc,
&val->edectx, &val->subvalidator);
val->parent_fetch, &val->edectx, &val->subvalidator);
if (result == ISC_R_SUCCESS) {
dns_validator_attach(val, &val->subvalidator->parent);
val->subvalidator->depth = val->depth + 1;
@ -3677,7 +3678,7 @@ dns_validator_create(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type,
dns_message_t *message, unsigned int options,
isc_loop_t *loop, isc_job_cb cb, void *arg,
isc_counter_t *nvalidations, isc_counter_t *nfails,
isc_counter_t *qc, isc_counter_t *gqc,
isc_counter_t *qc, isc_counter_t *gqc, fetchctx_t *parent,
dns_edectx_t *edectx, dns_validator_t **validatorp) {
isc_result_t result = ISC_R_FAILURE;
dns_validator_t *val = NULL;
@ -3710,6 +3711,7 @@ dns_validator_create(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type,
.arg = arg,
.rdata = DNS_RDATA_INIT,
.cb_edectx = edectx,
.parent_fetch = parent,
};
dns_ede_init(view->mctx, &val->edectx);

View file

@ -20707,7 +20707,7 @@ checkds_find_address(dns_checkds_t *checkds) {
result = dns_adb_createfind(
adb, checkds->zone->loop, process_checkds_adb_event, checkds,
&checkds->ns, options, 0, checkds->zone->view->dstport, 0, NULL,
NULL, &checkds->find);
NULL, NULL, &checkds->find);
dns_adb_detach(&adb);
/* Something failed? */

View file

@ -72,8 +72,8 @@ dns_zonefetch_run(void *arg) {
result = dns_resolver_createfetch(
resolver, fetch->qname, fetch->qtype, NULL, NULL, NULL, NULL, 0,
fetch->options, 0, NULL, NULL, loop, dns_zonefetch_done, fetch,
NULL, &fetch->rrset, &fetch->sigset, &fetch->fetch);
fetch->options, 0, NULL, NULL, NULL, loop, dns_zonefetch_done,
fetch, NULL, &fetch->rrset, &fetch->sigset, &fetch->fetch);
dns_resolver_detach(&resolver);

View file

@ -208,6 +208,7 @@ typedef enum isc_result {
DNS_R_NODOHPATH,
DNS_R_NOSKRFILE,
DNS_R_NOSKRBUNDLE,
DNS_R_LOOPDETECTED,
DST_R_UNSUPPORTEDALG,
DST_R_CRYPTOFAILURE,

View file

@ -17,6 +17,7 @@
#include <stdlib.h>
#include <isc/once.h>
#include <isc/result.h>
#include <isc/util.h>
static const char *description[ISC_R_NRESULTS] = {
@ -210,6 +211,7 @@ static const char *description[ISC_R_NRESULTS] = {
[DNS_R_NODOHPATH] = "no DOHPATH",
[DNS_R_NOSKRFILE] = "no SKR file",
[DNS_R_NOSKRBUNDLE] = "no available SKR bundle",
[DNS_R_LOOPDETECTED] = "fetch loop detected",
[DST_R_UNSUPPORTEDALG] = "algorithm is unsupported",
[DST_R_CRYPTOFAILURE] = "crypto failure",
@ -442,6 +444,7 @@ static const char *identifier[ISC_R_NRESULTS] = {
[DNS_R_NODOHPATH] = "DNS_R_NODOHPATH",
[DNS_R_NOSKRFILE] = "DNS_R_NOSKRFILE",
[DNS_R_NOSKRBUNDLE] = "DNS_R_NOSKRBUNDLE",
[DNS_R_LOOPDETECTED] = "DNS_R_LOOPDETECTED",
[DST_R_UNSUPPORTEDALG] = "DST_R_UNSUPPORTEDALG",
[DST_R_CRYPTOFAILURE] = "DST_R_CRYPTOFAILURE",

View file

@ -2876,7 +2876,7 @@ fetch_and_forget(ns_client_t *client, dns_name_t *qname, dns_rdatatype_t qtype,
result = dns_resolver_createfetch(
client->inner.view->resolver, qname, qtype, NULL, NULL, NULL,
peeraddr, client->message->id, options, 0, NULL,
client->query.qc, client->manager->loop, cb, client, NULL,
client->query.qc, NULL, client->manager->loop, cb, client, NULL,
tmprdataset, NULL, fetchp);
if (result != ISC_R_SUCCESS) {
ns_client_putrdataset(client, &tmprdataset);
@ -6403,7 +6403,7 @@ ns_query_recurse(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname,
result = dns_resolver_createfetch(
client->inner.view->resolver, qname, qtype, qdomain,
nameservers, NULL, peeraddr, client->message->id,
client->query.fetchoptions, 0, NULL, client->query.qc,
client->query.fetchoptions, 0, NULL, client->query.qc, NULL,
client->manager->loop, fetch_callback, client, &client->edectx,
rdataset, sigrdataset, &FETCH_RECTYPE_NORMAL(client));
if (result != ISC_R_SUCCESS) {