diff --git a/lib/ns/query.c b/lib/ns/query.c index 132a151bed..866ea9d509 100644 --- a/lib/ns/query.c +++ b/lib/ns/query.c @@ -11910,7 +11910,7 @@ isc_result_t ns_query_done(query_ctx_t *qctx) { isc_result_t result = ISC_R_UNSET; const dns_namelist_t *secs = qctx->client->message->sections; - bool nodetach; + bool nodetach, partial_result_with_servfail = false; CCTRACE(ISC_LOG_DEBUG(3), "ns_query_done"); @@ -11944,13 +11944,36 @@ ns_query_done(query_ctx_t *qctx) { /* * Do we need to restart the query (e.g. for CNAME chaining)? */ - if (qctx->want_restart && qctx->client->query.restarts < MAX_RESTARTS) { - qctx->client->query.restarts++; - return (ns__query_start(qctx)); + if (qctx->want_restart) { + if (qctx->client->query.restarts < MAX_RESTARTS) { + qctx->client->query.restarts++; + return (ns__query_start(qctx)); + } else { + /* + * This is e.g. a long CNAME chain which we cut short. + */ + qctx->client->query.attributes |= + NS_QUERYATTR_PARTIALANSWER; + qctx->client->message->rcode = dns_rcode_servfail; + qctx->result = DNS_R_SERVFAIL; + + /* + * Send the answer back with a SERVFAIL result even + * if recursion was requested. + */ + partial_result_with_servfail = true; + + ns_client_extendederror(qctx->client, 0, + "max. restarts reached"); + ns_client_log(qctx->client, NS_LOGCATEGORY_CLIENT, + NS_LOGMODULE_QUERY, ISC_LOG_INFO, + "query iterations limit reached"); + } } if (qctx->result != ISC_R_SUCCESS && - (!PARTIALANSWER(qctx->client) || WANTRECURSION(qctx->client) || + (!PARTIALANSWER(qctx->client) || + (WANTRECURSION(qctx->client) && !partial_result_with_servfail) || qctx->result == DNS_R_DROP)) { if (qctx->result == DNS_R_DUPLICATE ||