From 78274ec2b17bb16ee5414aaaf7f91d30ff43daba Mon Sep 17 00:00:00 2001 From: Colin Vidal Date: Thu, 23 Jan 2025 16:38:35 +0100 Subject: [PATCH 1/4] fix EDE 22 time out detection Extended DNS error 22 (No reachable authority) was previously detected when `fctx_expired` fired. It turns out this function is used as a "safety net" and the timeout detection should be caught earlier. It was working though, because of another issue fixed by !9927. Since this change, the recursive request timed out detection occurs before `fctx_expired` so EDE 22 is not added to the response message anymore. The fix of the problem is to add the EDE 22 code in two situations: - When the dispatch code timed out (rctx_timedout) the resolver code checks various properties to figure out if it needs to make another fetch attempt. One of the paramters if the fetch expiration time. If it expires, the whole recursion is canceled, so it now adds the EDE 22 code. - If the fetch expiration time doesn't expires in the case above (and other parameters allows it) a new fetch attempt is made (fctx_query). But before the new request is actually made, the fetch expiration time is re-checked. It might then has elapsed, and the whole recursion is canceled. So it now also adds the EDE 22 code here as well. --- lib/dns/resolver.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index 13fbed4215..d17bacd8d7 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -1987,6 +1987,10 @@ fctx_query(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, fctx_setretryinterval(fctx, srtt); if (isc_interval_iszero(&fctx->interval)) { FCTXTRACE("fetch expired"); + LOCK(&fctx->lock); + dns_ede_append(fctx->mctx, &fctx->edelist, + DNS_EDE_NOREACHABLEAUTH, NULL); + UNLOCK(&fctx->lock); return ISC_R_TIMEDOUT; } @@ -7948,6 +7952,10 @@ rctx_timedout(respctx_t *rctx) { if (isc_time_microdiff(&fctx->expires, &now) < US_PER_MS) { FCTXTRACE("query timed out; stopped trying to make " "fetch happen"); + LOCK(&fctx->lock); + dns_ede_append(fctx->mctx, &fctx->edelist, + DNS_EDE_NOREACHABLEAUTH, NULL); + UNLOCK(&fctx->lock); } else { FCTXTRACE("query timed out; trying next server"); /* try next server */ From 7cb8a028feb294f1c8bec04a03d582980a6a019b Mon Sep 17 00:00:00 2001 From: Colin Vidal Date: Thu, 23 Jan 2025 16:43:53 +0100 Subject: [PATCH 2/4] add new EDE 22 system tests This re-do a previously existing EDE 22 system test as well as add another one making sure the timed out flow detection works also on UDP when the resolver is contacting the authoritative server. (the existing test was using TCP to contact the authoritative servers). --- bin/tests/system/resolver/ans2/ans.pl | 5 +++++ bin/tests/system/resolver/tests.sh | 15 +++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/bin/tests/system/resolver/ans2/ans.pl b/bin/tests/system/resolver/ans2/ans.pl index b17fd6ec2e..079a7d9879 100644 --- a/bin/tests/system/resolver/ans2/ans.pl +++ b/bin/tests/system/resolver/ans2/ans.pl @@ -110,9 +110,14 @@ for (;;) { } elsif ($qname eq "net" && $qtype eq "NS") { $packet->header->aa(1); $packet->push("answer", new Net::DNS::RR("net 300 NS a.root-servers.nil.")); + } elsif ($qname eq "noresponse.exampleudp.net") { + next; } elsif ($qname =~ /example\.net/) { $packet->push("authority", new Net::DNS::RR("example.net 300 NS ns.example.net")); $packet->push("additional", new Net::DNS::RR("ns.example.net 300 A 10.53.0.3")); + } elsif ($qname =~ /exampleudp\.net/) { + $packet->push("authority", new Net::DNS::RR("exampleudp.net 300 NS ns.exampleudp.net")); + $packet->push("additional", new Net::DNS::RR("ns.exampleudp.net 300 A 10.53.0.2")); } elsif ($qname =~ /lame\.example\.org/) { $packet->header->ad(0); $packet->header->aa(0); diff --git a/bin/tests/system/resolver/tests.sh b/bin/tests/system/resolver/tests.sh index c2c2fd4d6f..066f42460f 100755 --- a/bin/tests/system/resolver/tests.sh +++ b/bin/tests/system/resolver/tests.sh @@ -50,6 +50,7 @@ echo_i "checking no response handling with a shorter than resolver-query-timeout ret=0 dig_with_opts +tcp +tries=1 +timeout=3 noresponse.example.net @10.53.0.1 a >dig.out.ns1.test${n} && ret=1 grep -F "no servers could be reached" 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)) @@ -61,6 +62,20 @@ 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)) + +# 'resolver-query-timeout' is set to 5 seconds in ns1, so named should +# interrupt the non-responsive query and send a SERVFAIL answer before dig's +# own timeout fires, which is set to 7 seconds. This time, exampleudp.net is +# contacted using UDP transport by the resolver. +n=$((n + 1)) +echo_i "checking no response handling with a longer than resolver-query-timeout timeout (UDP recursion) ($n)" +ret=0 +dig_with_opts +tcp +tries=1 +timeout=7 noresponse.exampleudp.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)) From 27f3b8950a675afe875759027a241da7c5244511 Mon Sep 17 00:00:00 2001 From: Colin Vidal Date: Fri, 24 Jan 2025 11:23:43 +0100 Subject: [PATCH 3/4] update serve-stale test to support EDE 22 When EDE 3 (stale answer) was added the serve-stale tests were checking for those exclusively, i.e. grepping for no "EDE" in the dig output when no stale answer was expected. However, some stale tests disable stale answers and make the authoritative server unresponsive, effectively triggering a timed out request thus an EDE 22. Update those tests so they still tests the absence of EDE 3 error, but also the presence of EDE 22. --- bin/tests/system/serve-stale/tests.sh | 79 ++++++++++++++++++++------- 1 file changed, 58 insertions(+), 21 deletions(-) diff --git a/bin/tests/system/serve-stale/tests.sh b/bin/tests/system/serve-stale/tests.sh index 507081b02e..dc5e7d9d9f 100755 --- a/bin/tests/system/serve-stale/tests.sh +++ b/bin/tests/system/serve-stale/tests.sh @@ -340,11 +340,15 @@ $DIG -p ${PORT} @10.53.0.1 nxdomain.example TXT >dig.out.test$((n + 4)) & wait +# no stale answers are used and the authoritative queries timed out. So no EDE 3 +# is not sent but EDE 22 is sent. + n=$((n + 1)) echo_i "check stale data.example TXT (serve-stale off) ($n)" ret=0 grep "status: SERVFAIL" dig.out.test$n >/dev/null || ret=1 -grep "EDE" dig.out.test$n >/dev/null && ret=1 +grep "EDE: 22 (No Reachable Authority)" dig.out.test$n >/dev/null || ret=1 +grep "EDE: 3 (Stale Answer)" dig.out.test$n >/dev/null && ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status + ret)) @@ -352,7 +356,8 @@ n=$((n + 1)) echo_i "check stale othertype.example CAA (serve-stale off) ($n)" ret=0 grep "status: SERVFAIL" dig.out.test$n >/dev/null || ret=1 -grep "EDE" dig.out.test$n >/dev/null && ret=1 +grep "EDE: 22 (No Reachable Authority)" dig.out.test$n >/dev/null || ret=1 +grep "EDE: 3 (Stale Answer)" dig.out.test$n >/dev/null && ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status + ret)) @@ -360,7 +365,8 @@ n=$((n + 1)) echo_i "check stale nodata.example TXT (serve-stale off) ($n)" ret=0 grep "status: SERVFAIL" dig.out.test$n >/dev/null || ret=1 -grep "EDE" dig.out.test$n >/dev/null && ret=1 +grep "EDE: 22 (No Reachable Authority)" dig.out.test$n >/dev/null || ret=1 +grep "EDE: 3 (Stale Answer)" dig.out.test$n >/dev/null && ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status + ret)) @@ -368,7 +374,8 @@ n=$((n + 1)) echo_i "check stale nxdomain.example TXT (serve-stale off) ($n)" ret=0 grep "status: SERVFAIL" dig.out.test$n >/dev/null || ret=1 -grep "EDE" dig.out.test$n >/dev/null && ret=1 +grep "EDE: 22 (No Reachable Authority)" dig.out.test$n >/dev/null || ret=1 +grep "EDE: 3 (Stale Answer)" dig.out.test$n >/dev/null && ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status + ret)) @@ -739,11 +746,15 @@ $DIG -p ${PORT} @10.53.0.1 nxdomain.example TXT >dig.out.test$((n + 4)) & wait +# stale-answer is enabled, but with a very low TTL so the following answer have +# been removed from the stale cache. Hence, no EDE 3 anymore, but EDE 22. + n=$((n + 1)) echo_i "check ancient data.example TXT (low max-stale-ttl) ($n)" ret=0 grep "status: SERVFAIL" dig.out.test$n >/dev/null || ret=1 -grep "EDE" dig.out.test$n >/dev/null && ret=1 +grep "EDE: 22 (No Reachable Authority)" dig.out.test$n >/dev/null || ret=1 +grep "EDE: 3 (Stale Answer)" dig.out.test$n >/dev/null && ret=1 grep "ANSWER: 0," dig.out.test$n >/dev/null || ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status + ret)) @@ -752,7 +763,8 @@ n=$((n + 1)) echo_i "check ancient othertype.example CAA (low max-stale-ttl) ($n)" ret=0 grep "status: SERVFAIL" dig.out.test$n >/dev/null || ret=1 -grep "EDE" dig.out.test$n >/dev/null && ret=1 +grep "EDE: 22 (No Reachable Authority)" dig.out.test$n >/dev/null || ret=1 +grep "EDE: 3 (Stale Answer)" dig.out.test$n >/dev/null && ret=1 grep "ANSWER: 0," dig.out.test$n >/dev/null || ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status + ret)) @@ -761,7 +773,8 @@ n=$((n + 1)) echo_i "check ancient nodata.example TXT (low max-stale-ttl) ($n)" ret=0 grep "status: SERVFAIL" dig.out.test$n >/dev/null || ret=1 -grep "EDE" dig.out.test$n >/dev/null && ret=1 +grep "EDE: 22 (No Reachable Authority)" dig.out.test$n >/dev/null || ret=1 +grep "EDE: 3 (Stale Answer)" dig.out.test$n >/dev/null && ret=1 grep "ANSWER: 0," dig.out.test$n >/dev/null || ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status + ret)) @@ -770,7 +783,8 @@ n=$((n + 1)) echo_i "check ancient nxdomain.example TXT (low max-stale-ttl) ($n)" ret=0 grep "status: SERVFAIL" dig.out.test$n >/dev/null || ret=1 -grep "EDE" dig.out.test$n >/dev/null && ret=1 +grep "EDE: 22 (No Reachable Authority)" dig.out.test$n >/dev/null || ret=1 +grep "EDE: 3 (Stale Answer)" dig.out.test$n >/dev/null && ret=1 grep "ANSWER: 0," dig.out.test$n >/dev/null || ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status + ret)) @@ -1093,11 +1107,15 @@ $DIG -p ${PORT} @10.53.0.3 nxdomain.example TXT >dig.out.test$((n + 4)) & wait +# no stale answers are used and the authoritative queries timed out. So no EDE 3 +# is not sent but EDE 22 is sent. + n=$((n + 1)) echo_i "check fail of data.example TXT (max-stale-ttl default) ($n)" ret=0 grep "status: SERVFAIL" dig.out.test$n >/dev/null || ret=1 -grep "EDE" dig.out.test$n >/dev/null && ret=1 +grep "EDE: 22 (No Reachable Authority)" dig.out.test$n >/dev/null || ret=1 +grep "EDE: 3 (Stale Answer)" dig.out.test$n >/dev/null && ret=1 grep "ANSWER: 0," dig.out.test$n >/dev/null || ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status + ret)) @@ -1106,7 +1124,8 @@ n=$((n + 1)) echo_i "check fail of othertype.example CAA (max-stale-ttl default) ($n)" ret=0 grep "status: SERVFAIL" dig.out.test$n >/dev/null || ret=1 -grep "EDE" dig.out.test$n >/dev/null && ret=1 +grep "EDE: 22 (No Reachable Authority)" dig.out.test$n >/dev/null || ret=1 +grep "EDE: 3 (Stale Answer)" dig.out.test$n >/dev/null && ret=1 grep "ANSWER: 0," dig.out.test$n >/dev/null || ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status + ret)) @@ -1115,7 +1134,8 @@ n=$((n + 1)) echo_i "check fail of nodata.example TXT (max-stale-ttl default) ($n)" ret=0 grep "status: SERVFAIL" dig.out.test$n >/dev/null || ret=1 -grep "EDE" dig.out.test$n >/dev/null && ret=1 +grep "EDE: 22 (No Reachable Authority)" dig.out.test$n >/dev/null || ret=1 +grep "EDE: 3 (Stale Answer)" dig.out.test$n >/dev/null && ret=1 grep "ANSWER: 0," dig.out.test$n >/dev/null || ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status + ret)) @@ -1124,7 +1144,8 @@ n=$((n + 1)) echo_i "check fail of nxdomain.example TXT (max-stale-ttl default) ($n)" ret=0 grep "status: SERVFAIL" dig.out.test$n >/dev/null || ret=1 -grep "EDE" dig.out.test$n >/dev/null && ret=1 +grep "EDE: 22 (No Reachable Authority)" dig.out.test$n >/dev/null || ret=1 +grep "EDE: 3 (Stale Answer)" dig.out.test$n >/dev/null && ret=1 grep "ANSWER: 0," dig.out.test$n >/dev/null || ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status + ret)) @@ -1225,11 +1246,13 @@ status=$((status + ret)) # The notfound.example check is different than nxdomain.example because # we didn't send a prime query to add notfound.example to the cache. +# Independently, EDE 22 is sent as the authoritative server doesn't respond. n=$((n + 1)) echo_i "check notfound.example TXT (max-stale-ttl default) ($n)" ret=0 grep "status: SERVFAIL" dig.out.test$n >/dev/null || ret=1 -grep "EDE" dig.out.test$n >/dev/null && ret=1 +grep "EDE: 22 (No Reachable Authority)" dig.out.test$n >/dev/null || ret=1 +grep "EDE: 3 (Stale Answer)" dig.out.test$n >/dev/null && ret=1 grep "ANSWER: 0," dig.out.test$n >/dev/null || ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status + ret)) @@ -1341,11 +1364,15 @@ $DIG -p ${PORT} @10.53.0.4 nxdomain.example TXT >dig.out.test$((n + 4)) & wait +# no stale answers are used and the authoritative queries timed out. So no EDE 3 +# is not sent but EDE 22 is sent. + n=$((n + 1)) echo_i "check fail of data.example TXT (serve-stale answers disabled) ($n)" ret=0 grep "status: SERVFAIL" dig.out.test$n >/dev/null || ret=1 -grep "EDE" dig.out.test$n >/dev/null && ret=1 +grep "EDE: 22 (No Reachable Authority)" dig.out.test$n >/dev/null || ret=1 +grep "EDE: 3 (Stale Answer)" dig.out.test$n >/dev/null && ret=1 grep "ANSWER: 0," dig.out.test$n >/dev/null || ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status + ret)) @@ -1354,7 +1381,8 @@ n=$((n + 1)) echo_i "check fail of othertype.example TXT (serve-stale answers disabled) ($n)" ret=0 grep "status: SERVFAIL" dig.out.test$n >/dev/null || ret=1 -grep "EDE" dig.out.test$n >/dev/null && ret=1 +grep "EDE: 22 (No Reachable Authority)" dig.out.test$n >/dev/null || ret=1 +grep "EDE: 3 (Stale Answer)" dig.out.test$n >/dev/null && ret=1 grep "ANSWER: 0," dig.out.test$n >/dev/null || ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status + ret)) @@ -1363,7 +1391,8 @@ n=$((n + 1)) echo_i "check fail of nodata.example TXT (serve-stale answers disabled) ($n)" ret=0 grep "status: SERVFAIL" dig.out.test$n >/dev/null || ret=1 -grep "EDE" dig.out.test$n >/dev/null && ret=1 +grep "EDE: 22 (No Reachable Authority)" dig.out.test$n >/dev/null || ret=1 +grep "EDE: 3 (Stale Answer)" dig.out.test$n >/dev/null && ret=1 grep "ANSWER: 0," dig.out.test$n >/dev/null || ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status + ret)) @@ -1372,7 +1401,8 @@ n=$((n + 1)) echo_i "check fail of nxdomain.example TXT (serve-stale answers disabled) ($n)" ret=0 grep "status: SERVFAIL" dig.out.test$n >/dev/null || ret=1 -grep "EDE" dig.out.test$n >/dev/null && ret=1 +grep "EDE: 22 (No Reachable Authority)" dig.out.test$n >/dev/null || ret=1 +grep "EDE: 3 (Stale Answer)" dig.out.test$n >/dev/null && ret=1 grep "ANSWER: 0," dig.out.test$n >/dev/null || ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status + ret)) @@ -1549,11 +1579,15 @@ $DIG -p ${PORT} @10.53.0.5 nxdomain.example TXT >dig.out.test$((n + 4)) & wait +# no stale answers are used and the authoritative queries timed out. So no EDE 3 +# is not sent but EDE 22 is sent. + n=$((n + 1)) echo_i "check fail of data.example TXT (serve-stale cache disabled) ($n)" ret=0 grep "status: SERVFAIL" dig.out.test$n >/dev/null || ret=1 -grep "EDE" dig.out.test$n >/dev/null && ret=1 +grep "EDE: 22 (No Reachable Authority)" dig.out.test$n >/dev/null || ret=1 +grep "EDE: 3 (Stale Answer)" dig.out.test$n >/dev/null && ret=1 grep "ANSWER: 0," dig.out.test$n >/dev/null || ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status + ret)) @@ -1562,7 +1596,8 @@ n=$((n + 1)) echo_i "check fail of othertype.example CAA (serve-stale cache disabled) ($n)" ret=0 grep "status: SERVFAIL" dig.out.test$n >/dev/null || ret=1 -grep "EDE" dig.out.test$n >/dev/null && ret=1 +grep "EDE: 22 (No Reachable Authority)" dig.out.test$n >/dev/null || ret=1 +grep "EDE: 3 (Stale Answer)" dig.out.test$n >/dev/null && ret=1 grep "ANSWER: 0," dig.out.test$n >/dev/null || ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status + ret)) @@ -1571,7 +1606,8 @@ n=$((n + 1)) echo_i "check fail of nodata.example TXT (serve-stale cache disabled) ($n)" ret=0 grep "status: SERVFAIL" dig.out.test$n >/dev/null || ret=1 -grep "EDE" dig.out.test$n >/dev/null && ret=1 +grep "EDE: 22 (No Reachable Authority)" dig.out.test$n >/dev/null || ret=1 +grep "EDE: 3 (Stale Answer)" dig.out.test$n >/dev/null && ret=1 grep "ANSWER: 0," dig.out.test$n >/dev/null || ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status + ret)) @@ -1580,7 +1616,8 @@ n=$((n + 1)) echo_i "check fail of nxdomain.example TXT (serve-stale cache disabled) ($n)" ret=0 grep "status: SERVFAIL" dig.out.test$n >/dev/null || ret=1 -grep "EDE" dig.out.test$n >/dev/null && ret=1 +grep "EDE: 22 (No Reachable Authority)" dig.out.test$n >/dev/null || ret=1 +grep "EDE: 3 (Stale Answer)" dig.out.test$n >/dev/null && ret=1 grep "ANSWER: 0," dig.out.test$n >/dev/null || ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status + ret)) From 39c2fc4670ee3dd10693cb8a9007d60442157553 Mon Sep 17 00:00:00 2001 From: Colin Vidal Date: Fri, 24 Jan 2025 17:27:09 +0100 Subject: [PATCH 4/4] fix byte order in EDE logging When an EDE code is added to a message, the code is converted early in a big-endian order so it can be memcpy-ed directly in the EDE buffer that will go on the wire. This previous change forget to update debug logs which still assume the EDE code was in host byte order. Add a separate variable to differentiate both and avoid ambiguities --- lib/ns/client.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/ns/client.c b/lib/ns/client.c index 4ff16aa6a4..eb74cdd961 100644 --- a/lib/ns/client.c +++ b/lib/ns/client.c @@ -234,7 +234,8 @@ client_extendederror_reset(ns_client_t *client) { void ns_client_extendederror(ns_client_t *client, uint16_t code, const char *text) { - const uint16_t codelen = sizeof(code); + uint16_t becode; + const uint16_t becodelen = sizeof(becode); uint16_t textlen = 0; size_t pos = 0; unsigned char *ede = NULL; @@ -246,7 +247,7 @@ ns_client_extendederror(ns_client_t *client, uint16_t code, const char *text) { * As ede will be directly put in the DNS message we need to make sure * the code is in big-endian format */ - code = htobe16(code); + becode = htobe16(code); for (pos = 0; pos < DNS_EDE_MAX_ERRORS; pos++) { edns = client->ede[pos]; @@ -255,7 +256,7 @@ ns_client_extendederror(ns_client_t *client, uint16_t code, const char *text) { break; } - if (memcmp(&code, edns->value, sizeof(code)) == 0) { + if (memcmp(&becode, edns->value, becodelen) == 0) { ns_client_log(client, NS_LOGCATEGORY_CLIENT, NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(1), "ignoring duplicate ede %u %s", code, @@ -288,16 +289,16 @@ ns_client_extendederror(ns_client_t *client, uint16_t code, const char *text) { } } - ede = isc_mem_get(client->manager->mctx, codelen + textlen); + ede = isc_mem_get(client->manager->mctx, becodelen + textlen); - memcpy(ede, &code, sizeof(code)); + memcpy(ede, &becode, sizeof(code)); if (textlen > 0) { - memcpy(ede + codelen, text, textlen); + memcpy(ede + becodelen, text, textlen); } edns = isc_mem_get(client->manager->mctx, sizeof(*edns)); *edns = (dns_ednsopt_t){ .code = DNS_OPT_EDE, - .length = codelen + textlen, + .length = becodelen + textlen, .value = ede }; client->ede[pos] = edns;