Add EDE 22 No reachable authority code

Add support for Extended DNS Errors (EDE) error 22: No reachable
authority. This occurs when after a timeout delay when the resolver is
trying to query an authority server.

(cherry picked from commit d13e94b930)

Commit amended in order to fix usage of isc_log_write (adding dns_lctx
parameter)
This commit is contained in:
Colin Vidal 2024-11-08 18:18:30 +01:00
parent fc14b62e92
commit c586d9a658
14 changed files with 243 additions and 13 deletions

View file

@ -7222,7 +7222,8 @@ tat_done(void *arg) {
if (resp->db != NULL) {
dns_db_detach(&resp->db);
}
isc_mem_putanddetach(&resp->mctx, resp, sizeof(*resp));
dns_resolver_freefresp(&resp);
dns_resolver_destroyfetch(&tat->fetch);
if (dns_rdataset_isassociated(&tat->rdataset)) {
dns_rdataset_disassociate(&tat->rdataset);

View file

@ -62,6 +62,7 @@ echo_i "checking no response handling with a longer than resolver-query-timeout
ret=0
dig_with_opts +tcp +tries=1 +timeout=7 noresponse.example.net @10.53.0.1 a >dig.out.ns1.test${n} || ret=1
grep -F "status: SERVFAIL" dig.out.ns1.test${n} >/dev/null || ret=1
grep -F "EDE: 22 (No Reachable Authority)" dig.out.ns1.test${n} >/dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))

View file

@ -2944,7 +2944,7 @@ check_result:
out:
dns_resolver_destroyfetch(&fetch->fetch);
free_adbfetch(adb, &fetch);
isc_mem_putanddetach(&resp->mctx, resp, sizeof(*resp));
dns_resolver_freefresp(&resp);
if (astat != DNS_ADB_CANCELED) {
clean_finds_at_name(name, astat, address_type);
}

View file

@ -555,7 +555,7 @@ client_resfind(resctx_t *rctx, dns_fetchresponse_t *resp) {
fname = resp->foundname;
INSIST(resp->rdataset == rctx->rdataset);
INSIST(resp->sigrdataset == rctx->sigrdataset);
isc_mem_putanddetach(&resp->mctx, resp, sizeof(*resp));
dns_resolver_freefresp(&resp);
}
/*

View file

@ -151,6 +151,15 @@
#define DNS_EDE_NETWORKERROR 23 /*%< Network Error */
#define DNS_EDE_INVALIDDATA 24 /*%< Invalid Data */
typedef struct dns_ede dns_ede_t;
struct dns_ede {
uint16_t info_code;
char *extra_text;
ISC_LINK(dns_ede_t) link;
};
typedef ISC_LIST(dns_ede_t) dns_edelist_t;
/*
* From RFC 8914:
* Because long EXTRA-TEXT fields may trigger truncation (which is undesirable
@ -1520,4 +1529,32 @@ dns_message_createpools(isc_mem_t *mctx, isc_mempool_t **namepoolp,
void
dns_message_destroypools(isc_mempool_t **namepoolp, isc_mempool_t **rdspoolp);
void
dns_ede_append(isc_mem_t *mctx, dns_edelist_t *list, uint16_t info_code,
const char *extra_text);
/*%<
* Adds a new EDE message at the end of 'list'. If 'extra_text' is non
* NULL, the string is synchronously copied internally, so the called
* doesn't have to keep it alive once this call returns. RFC8914
* section 4 define the valid range of possible numbers for
* 'info_code'.
*
* Requires:
* \li mctx to be non NULL;
* \li list to be non NULL;
* \li info_code to be valid;
* \li extra_text can be NULL or non NULL, do not take ownership.
*/
void
dns_ede_unlinkall(isc_mem_t *mctx, dns_edelist_t *list);
/*%<
* Unlink all elements from from 'list' and free it from
* memory. Optional text owned by elements is also freed.
*
* Requires:
* \li mctx to be non NULL;
* \li list to be non NULL;
*/
ISC_LANG_ENDDECLS

View file

@ -57,6 +57,7 @@
#include <isc/types.h>
#include <dns/fixedname.h>
#include <dns/message.h>
#include <dns/types.h>
/* Add -DDNS_RESOLVER_TRACE=1 to CFLAGS for detailed reference tracing */
@ -81,6 +82,7 @@ struct dns_fetchresponse {
isc_mem_t *mctx;
isc_result_t result;
isc_result_t vresult;
dns_edelist_t edelist;
dns_rdatatype_t qtype;
dns_db_t *db;
dns_dbnode_t *node;
@ -632,4 +634,15 @@ dns_resolver_getquerystats(dns_resolver_t *res, dns_stats_t **statsp);
*
*\li 'statsp' != NULL && '*statsp' != NULL
*/
void
dns_resolver_freefresp(dns_fetchresponse_t **frespp);
/*%<
* Free a dns_fetchresponse_t object and internal owned object as
* well.
*
* Requires:
* \li 'frespp' is valid. No-op if *frespp == NULL
*/
ISC_LANG_ENDDECLS

View file

@ -5015,3 +5015,57 @@ dns_message_destroypools(isc_mempool_t **namepoolp, isc_mempool_t **rdspoolp) {
isc_mempool_destroy(rdspoolp);
isc_mempool_destroy(namepoolp);
}
void
dns_ede_append(isc_mem_t *mctx, dns_edelist_t *list, uint16_t info_code,
const char *extra_text) {
REQUIRE(mctx);
REQUIRE(list);
REQUIRE(info_code <= 24);
dns_ede_t *ede = isc_mem_get(mctx, sizeof(*ede));
*ede = (dns_ede_t){
.info_code = info_code,
.extra_text = NULL,
.link = ISC_LINK_INITIALIZER,
};
if (extra_text) {
size_t len = strlen(extra_text);
if (len >= DNS_EDE_EXTRATEXT_LEN) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
DNS_LOGMODULE_MESSAGE, ISC_LOG_PRINTTIME,
"truncate EDE code %hu text: %s",
info_code, extra_text);
len = DNS_EDE_EXTRATEXT_LEN - 1;
}
ede->extra_text = isc_mem_allocate(mctx, len + 1);
strncpy(ede->extra_text, extra_text, len);
ede->extra_text[len] = '\0';
}
ISC_LIST_APPEND(*list, ede, link);
}
void
dns_ede_unlinkall(isc_mem_t *mctx, dns_edelist_t *list) {
dns_ede_t *ede, *next;
REQUIRE(mctx);
REQUIRE(list);
for (ede = ISC_LIST_HEAD(*list); ede != NULL; ede = next) {
next = ISC_LIST_NEXT(ede, link);
ISC_LIST_UNLINK(*list, ede, link);
if (ede->extra_text) {
isc_mem_free(mctx, ede->extra_text);
ede->extra_text = NULL;
}
isc_mem_put(mctx, ede, sizeof(*ede));
}
INSIST(ISC_LIST_EMPTY(*list));
}

View file

@ -179,7 +179,7 @@ fetch_done(void *arg) {
dns_db_detach(&resp->db);
}
isc_mem_putanddetach(&resp->mctx, resp, sizeof(*resp));
dns_resolver_freefresp(&resp);
switch (eresult) {
case ISC_R_SUCCESS:

View file

@ -359,6 +359,7 @@ struct fetchctx {
bool spilled;
ISC_LINK(struct fetchctx) link;
ISC_LIST(dns_fetchresponse_t) resps;
dns_edelist_t edelist;
/*% Locked by loop event serialization. */
dns_fixedname_t dfname;
@ -1335,6 +1336,8 @@ fctx_cleanup(fetchctx_t *fctx) {
ISC_LIST_UNLINK(fctx->altaddrs, addr, publink);
dns_adb_freeaddrinfo(fctx->adb, &addr);
}
dns_ede_unlinkall(fctx->mctx, &fctx->edelist);
}
static void
@ -1597,6 +1600,17 @@ fctx_sendevents(fetchctx_t *fctx, isc_result_t result) {
resp->result == DNS_R_NCACHENXRRSET);
}
/*
* Copy EDE that occured during the resolution to all
* clients
*/
for (dns_ede_t *ede = ISC_LIST_HEAD(fctx->edelist); ede != NULL;
ede = ISC_LIST_NEXT(ede, link))
{
dns_ede_append(resp->mctx, &resp->edelist,
ede->info_code, ede->extra_text);
}
FCTXTRACE("post response event");
isc_async_run(resp->loop, resp->cb, resp);
}
@ -4147,7 +4161,8 @@ resume_qmin(void *arg) {
}
result = resp->result;
isc_mem_putanddetach(&resp->mctx, resp, sizeof(*resp));
dns_resolver_freefresp(&resp);
LOCK(&fctx->lock);
if (SHUTTINGDOWN(fctx)) {
@ -4293,6 +4308,7 @@ fctx_destroy(fetchctx_t *fctx) {
REQUIRE(ISC_LIST_EMPTY(fctx->queries));
REQUIRE(ISC_LIST_EMPTY(fctx->finds));
REQUIRE(ISC_LIST_EMPTY(fctx->altfinds));
REQUIRE(ISC_LIST_EMPTY(fctx->edelist));
REQUIRE(atomic_load_acquire(&fctx->pending) == 0);
REQUIRE(ISC_LIST_EMPTY(fctx->validators));
REQUIRE(fctx->state != fetchstate_active);
@ -4356,6 +4372,12 @@ fctx_expired(void *arg) {
DNS_LOGMODULE_RESOLVER, ISC_LOG_INFO,
"shut down hung fetch while resolving %p(%s)", fctx,
fctx->info);
LOCK(&fctx->lock);
dns_ede_append(fctx->mctx, &fctx->edelist, DNS_EDE_NOREACHABLEAUTH,
NULL);
UNLOCK(&fctx->lock);
fctx_done_detach(&fctx, DNS_R_SERVFAIL);
}
@ -4419,6 +4441,7 @@ fctx_add_event(fetchctx_t *fctx, isc_loop_t *loop, const isc_sockaddr_t *client,
resp = isc_mem_get(fctx->mctx, sizeof(*resp));
*resp = (dns_fetchresponse_t){
.result = DNS_R_SERVFAIL,
.edelist = ISC_LIST_INITIALIZER,
.qtype = fctx->type,
.rdataset = rdataset,
.sigrdataset = sigrdataset,
@ -4509,6 +4532,7 @@ fctx_create(dns_resolver_t *res, isc_loop_t *loop, const dns_name_t *name,
.loop = loop,
.nvalidations = atomic_load_relaxed(&res->maxvalidations),
.nfails = atomic_load_relaxed(&res->maxvalidationfails),
.edelist = ISC_LIST_INITIALIZER,
};
isc_mem_attach(mctx, &fctx->mctx);
@ -7029,7 +7053,8 @@ resume_dslookup(void *arg) {
/* Preserve data from resp before freeing it. */
frdataset = resp->rdataset; /* a.k.a. fctx->nsrrset */
result = resp->result;
isc_mem_putanddetach(&resp->mctx, resp, sizeof(*resp));
dns_resolver_freefresp(&resp);
LOCK(&fctx->lock);
if (SHUTTINGDOWN(fctx)) {
@ -10086,7 +10111,7 @@ prime_done(void *arg) {
INSIST(resp->sigrdataset == NULL);
isc_mem_put(res->mctx, resp->rdataset, sizeof(*resp->rdataset));
isc_mem_putanddetach(&resp->mctx, resp, sizeof(*resp));
dns_resolver_freefresp(&resp);
dns_resolver_destroyfetch(&fetch);
}
@ -11021,3 +11046,18 @@ dns_resolver_getquerystats(dns_resolver_t *res, dns_stats_t **statsp) {
dns_stats_attach(res->querystats, statsp);
}
}
void
dns_resolver_freefresp(dns_fetchresponse_t **frespp) {
REQUIRE(frespp);
if (*frespp == NULL) {
return;
}
dns_fetchresponse_t *fresp = *frespp;
*frespp = NULL;
dns_ede_unlinkall(fresp->mctx, &fresp->edelist);
isc_mem_putanddetach(&fresp->mctx, fresp, sizeof(*fresp));
}

View file

@ -448,7 +448,7 @@ fetch_callback_dnskey(void *arg) {
}
cleanup:
isc_mem_putanddetach(&resp->mctx, resp, sizeof(*resp));
dns_resolver_freefresp(&resp);
validate_async_done(val, result);
dns_validator_detach(&val);
}
@ -578,7 +578,7 @@ fetch_callback_ds(void *arg) {
}
cleanup:
isc_mem_putanddetach(&resp->mctx, resp, sizeof(*resp));
dns_resolver_freefresp(&resp);
validate_async_done(val, result);
dns_validator_detach(&val);
}

View file

@ -10801,9 +10801,9 @@ cleanup:
dns_rdataset_disassociate(dnskeysigs);
}
dns_resolver_freefresp(&resp);
dns_name_free(keyname, mctx);
isc_mem_putanddetach(&kfetch->mctx, kfetch, sizeof(dns_keyfetch_t));
isc_mem_putanddetach(&resp->mctx, resp, sizeof(*resp));
if (secroots != NULL) {
dns_keytable_detach(&secroots);
@ -21781,7 +21781,8 @@ cleanup:
if (dns_rdataset_isassociated(nssigset)) {
dns_rdataset_disassociate(nssigset);
}
isc_mem_putanddetach(&resp->mctx, resp, sizeof(*resp));
dns_resolver_freefresp(&resp);
if (levelup) {
UNLOCK_ZONE(zone);

View file

@ -2560,8 +2560,7 @@ free_fresp(ns_client_t *client, dns_fetchresponse_t **frespp) {
ns_client_putrdataset(client, &fresp->sigrdataset);
}
*frespp = NULL;
isc_mem_putanddetach(&fresp->mctx, fresp, sizeof(*fresp));
dns_resolver_freefresp(frespp);
}
static isc_result_t
@ -6364,6 +6363,13 @@ fetch_callback(void *arg) {
client->query.attributes &= ~NS_QUERYATTR_RECURSING;
client->state = NS_CLIENTSTATE_WORKING;
for (dns_ede_t *ede = ISC_LIST_HEAD(resp->edelist); ede != NULL;
ede = ISC_LIST_NEXT(ede, link))
{
ns_client_extendederror(client, ede->info_code,
ede->extra_text);
}
/*
* Initialize a new qctx and use it to either resume from
* recursion or clean up after cancelation. Transfer

View file

@ -28,6 +28,7 @@ check_PROGRAMS = \
dispatch_test \
dns64_test \
dst_test \
ede_test \
keytable_test \
name_test \
nametree_test \

76
tests/dns/ede_test.c Normal file
View file

@ -0,0 +1,76 @@
/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* SPDX-License-Identifier: MPL-2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
#include <inttypes.h>
#include <sched.h> /* IWYU pragma: keep */
#include <setjmp.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <unistd.h>
#define UNIT_TESTING
#include <cmocka.h>
#include <isc/buffer.h>
#include <isc/net.h>
#include <isc/timer.h>
#include <isc/tls.h>
#include <isc/util.h>
#include <dns/message.h>
#include <tests/isc.h>
ISC_RUN_TEST_IMPL(ede_enqueue_unlink) {
dns_edelist_t list;
dns_ede_t *ede = NULL;
const char *msg1 = "abcd";
const char *msg2 = "abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabc"
"dabcdabcdadcdabcd";
ISC_LIST_INIT(list);
dns_ede_append(mctx, &list, 22, NULL);
dns_ede_append(mctx, &list, 12, msg1);
dns_ede_append(mctx, &list, 4, msg2);
ede = ISC_LIST_HEAD(list);
assert_non_null(ede);
assert_int_equal(ede->info_code, 22);
assert_null(ede->extra_text);
ede = ISC_LIST_NEXT(ede, link);
assert_non_null(ede);
assert_int_equal(ede->info_code, 12);
assert_string_equal(ede->extra_text, msg1);
assert_ptr_not_equal(ede->extra_text, msg1);
ede = ISC_LIST_NEXT(ede, link);
assert_non_null(ede);
assert_int_equal(ede->info_code, 4);
assert_string_not_equal(ede->extra_text, msg2);
assert_ptr_not_equal(ede->extra_text, msg2);
assert_int_equal(strlen(ede->extra_text), 63);
dns_ede_unlinkall(mctx, &list);
assert_true(ISC_LIST_EMPTY(list));
}
ISC_TEST_LIST_START
ISC_TEST_ENTRY(ede_enqueue_unlink)
ISC_TEST_LIST_END
ISC_TEST_MAIN