From: Colin Vidal Date: Fri, 8 Nov 2024 17:18:30 +0000 (+0100) Subject: Add EDE 22 No reachable authority code X-Git-Tag: v9.21.4~64^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d13e94b930214736600079778c2644a384c32c13;p=thirdparty%2Fbind9.git 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. --- diff --git a/bin/named/server.c b/bin/named/server.c index 4b624d7ebf7..de1a4ba6e15 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -6893,7 +6893,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); diff --git a/bin/tests/system/resolver/tests.sh b/bin/tests/system/resolver/tests.sh index 222b76838e2..82cf1b731bd 100755 --- a/bin/tests/system/resolver/tests.sh +++ b/bin/tests/system/resolver/tests.sh @@ -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)) diff --git a/lib/dns/adb.c b/lib/dns/adb.c index 924f6404320..13e6bca9b8f 100644 --- a/lib/dns/adb.c +++ b/lib/dns/adb.c @@ -2941,7 +2941,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); } diff --git a/lib/dns/client.c b/lib/dns/client.c index c22c4d4b66b..96043e0266e 100644 --- a/lib/dns/client.c +++ b/lib/dns/client.c @@ -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); } /* diff --git a/lib/dns/include/dns/message.h b/lib/dns/include/dns/message.h index 62a00cedf64..624392fd060 100644 --- a/lib/dns/include/dns/message.h +++ b/lib/dns/include/dns/message.h @@ -158,6 +158,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 @@ -1526,4 +1535,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 diff --git a/lib/dns/include/dns/resolver.h b/lib/dns/include/dns/resolver.h index daa7ef2cff6..b65e2aa78c3 100644 --- a/lib/dns/include/dns/resolver.h +++ b/lib/dns/include/dns/resolver.h @@ -57,6 +57,7 @@ #include #include +#include #include /* 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; @@ -631,4 +633,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 diff --git a/lib/dns/message.c b/lib/dns/message.c index 096784d7531..01af5729292 100644 --- a/lib/dns/message.c +++ b/lib/dns/message.c @@ -5105,3 +5105,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_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)); +} diff --git a/lib/dns/nta.c b/lib/dns/nta.c index 51b99efdfd8..acd788795fa 100644 --- a/lib/dns/nta.c +++ b/lib/dns/nta.c @@ -178,7 +178,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: diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index cd5a780ad8c..e6f69c0fda4 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -352,6 +352,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; @@ -1328,6 +1329,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 @@ -1590,6 +1593,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); } @@ -4150,7 +4164,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)) { @@ -4296,6 +4311,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); @@ -4359,6 +4375,12 @@ fctx_expired(void *arg) { 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); } @@ -4422,6 +4444,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, @@ -4512,6 +4535,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)) { @@ -10081,7 +10106,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); } @@ -11014,3 +11039,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)); +} diff --git a/lib/dns/validator.c b/lib/dns/validator.c index aafe5147d06..a210c773a9a 100644 --- a/lib/dns/validator.c +++ b/lib/dns/validator.c @@ -449,7 +449,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); } @@ -579,7 +579,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); } diff --git a/lib/dns/zone.c b/lib/dns/zone.c index ddb7ae29274..01c670aa161 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -10851,9 +10851,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); @@ -21751,7 +21751,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); diff --git a/lib/ns/query.c b/lib/ns/query.c index ee7b83a4f78..b7f6e53b375 100644 --- a/lib/ns/query.c +++ b/lib/ns/query.c @@ -2558,8 +2558,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 @@ -6172,6 +6171,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 diff --git a/tests/dns/Makefile.am b/tests/dns/Makefile.am index 78b209b1298..8d33cc46f34 100644 --- a/tests/dns/Makefile.am +++ b/tests/dns/Makefile.am @@ -28,6 +28,7 @@ check_PROGRAMS = \ dispatch_test \ dns64_test \ dst_test \ + ede_test \ keytable_test \ name_test \ nametree_test \ diff --git a/tests/dns/ede_test.c b/tests/dns/ede_test.c new file mode 100644 index 00000000000..f7a8c190028 --- /dev/null +++ b/tests/dns/ede_test.c @@ -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 +#include /* IWYU pragma: keep */ +#include +#include +#include +#include +#include + +#define UNIT_TESTING +#include + +#include +#include +#include +#include +#include + +#include + +#include + +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