]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Add EDE 22 No reachable authority code
authorColin Vidal <colin@isc.org>
Fri, 8 Nov 2024 17:18:30 +0000 (18:18 +0100)
committerColin Vidal <colin@isc.org>
Wed, 4 Dec 2024 15:19:30 +0000 (16:19 +0100)
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.

14 files changed:
bin/named/server.c
bin/tests/system/resolver/tests.sh
lib/dns/adb.c
lib/dns/client.c
lib/dns/include/dns/message.h
lib/dns/include/dns/resolver.h
lib/dns/message.c
lib/dns/nta.c
lib/dns/resolver.c
lib/dns/validator.c
lib/dns/zone.c
lib/ns/query.c
tests/dns/Makefile.am
tests/dns/ede_test.c [new file with mode: 0644]

index 4b624d7ebf7ed702196de15c4c6ff3f7f00a988d..de1a4ba6e157f615ec6537c3e4db7016b9c32e30 100644 (file)
@@ -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);
index 222b76838e2a3576a1974e7454ff90b46e2be216..82cf1b731bdd90ea70f131250a65e5d97cc1cfdd 100755 (executable)
@@ -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))
 
index 924f640432030ccecdff6f91494f40151648cf26..13e6bca9b8fa515c9622e9bd28c6e37beae90c4d 100644 (file)
@@ -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);
        }
index c22c4d4b66b6ff1339a9cced32b0f2d056c858ab..96043e0266ea0adc2975756bc9cb5b1388fc70f0 100644 (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);
                }
 
                /*
index 62a00cedf6415e5dca463683492189a01c2825c6..624392fd060acf878e03a892785cf6fb68903687 100644 (file)
 #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
index daa7ef2cff68a04624cc9f0580b5372f173df911..b65e2aa78c315814139eb67b5783971a4276740d 100644 (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;
@@ -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
index 096784d75312cf17738aac23ced1208681099aa3..01af572929286e65da2a03ac0fcbb67dff552531 100644 (file)
@@ -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));
+}
index 51b99efdfd82f166ecee7963b73044130abf4727..acd788795faec1ec9d4642b96736e599419434ba 100644 (file)
@@ -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:
index cd5a780ad8c8d465f6a67ce84eb84971662218da..e6f69c0fda41efa72b9b2f7cae7f4e8931564413 100644 (file)
@@ -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));
+}
index aafe5147d0612bd90cf99355c6a92dc0fa855521..a210c773a9ab76bfc29ef5b2e61611c7fb4a2c1c 100644 (file)
@@ -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);
 }
index ddb7ae292742a09b0b7c68b81fdf66683682aa58..01c670aa1614072b4f583f9a4318a22e4f5ac326 100644 (file)
@@ -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);
index ee7b83a4f78a562d6d10eaeb6e8465b839959532..b7f6e53b375cc518dc3d36393e1897ff4569bfc1 100644 (file)
@@ -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
index 78b209b12989e0d28b6b0ecb4cec91518747e031..8d33cc46f349f60a52f6e59b61b6d6acf857f7c1 100644 (file)
@@ -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 (file)
index 0000000..f7a8c19
--- /dev/null
@@ -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