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);
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))
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);
}
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);
}
/*
#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
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
#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 */
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;
*
*\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
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));
+}
dns_db_detach(&resp->db);
}
- isc_mem_putanddetach(&resp->mctx, resp, sizeof(*resp));
+ dns_resolver_freefresp(&resp);
switch (eresult) {
case ISC_R_SUCCESS:
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;
ISC_LIST_UNLINK(fctx->altaddrs, addr, publink);
dns_adb_freeaddrinfo(fctx->adb, &addr);
}
+
+ dns_ede_unlinkall(fctx->mctx, &fctx->edelist);
}
static void
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);
}
}
result = resp->result;
- isc_mem_putanddetach(&resp->mctx, resp, sizeof(*resp));
+
+ dns_resolver_freefresp(&resp);
LOCK(&fctx->lock);
if (SHUTTINGDOWN(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);
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);
}
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,
.loop = loop,
.nvalidations = atomic_load_relaxed(&res->maxvalidations),
.nfails = atomic_load_relaxed(&res->maxvalidationfails),
+ .edelist = ISC_LIST_INITIALIZER,
};
isc_mem_attach(mctx, &fctx->mctx);
/* 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)) {
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);
}
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));
+}
}
cleanup:
- isc_mem_putanddetach(&resp->mctx, resp, sizeof(*resp));
+ dns_resolver_freefresp(&resp);
validate_async_done(val, result);
dns_validator_detach(&val);
}
}
cleanup:
- isc_mem_putanddetach(&resp->mctx, resp, sizeof(*resp));
+ dns_resolver_freefresp(&resp);
validate_async_done(val, result);
dns_validator_detach(&val);
}
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);
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);
ns_client_putrdataset(client, &fresp->sigrdataset);
}
- *frespp = NULL;
- isc_mem_putanddetach(&fresp->mctx, fresp, sizeof(*fresp));
+ dns_resolver_freefresp(frespp);
}
static isc_result_t
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
dispatch_test \
dns64_test \
dst_test \
+ ede_test \
keytable_test \
name_test \
nametree_test \
--- /dev/null
+/*
+ * 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