Repository: openthread/openthread (language: python)

Issue:
CLI command `dns resolve4` causes infinite loop of DNS queries, in NxDomain cases
**Describe the bug** A clear and concise description of what the bug is.
The command `dns resolve4 somename.org` locks the CLI and leads to an infinite loop of DNS queries to the BR. In case the DNS response RCode back to the client is 'NxDomain'.

**To Reproduce** Information to reproduce the behavior, including:

1. Git commit id: latest commit (tried in simulation) and ot-reference-release (tried on Nordic)
2. IEEE 802.15.4 hardware platform: both simulation (OTNS2) and Nordic NRF52840
3. Build steps: standard for OTNS2 / ot-reference-release respectively
4. Network topology: one BR with one Router attached

**Expected behavior** A clear and concise description of what you expected to happen.
No retry if the DNS query response is received by the client, even if the RCode indicates an error.

**Console/log output** If applicable, add console/log output to help explain your problem.
(can be added if needed)

**Additional context** Add any other context about the problem here.
Problem comes up in cert tests while issuing `dns resolve4 ipv4only.arpa` from the reference device client.



Discussion / hints:
Will take a look at it later when I get a chance. I will be less available today (Friday) and next week.



Fix patch:
diff --git a/src/core/net/dns_client.cpp b/src/core/net/dns_client.cpp
index 82b89d697fc..bdf7302961e 100644
--- a/src/core/net/dns_client.cpp
+++ b/src/core/net/dns_client.cpp
@@ -1563,7 +1563,7 @@ Error Client::ReplaceWithIp4Query(Query &aQuery)
 
     info.ReadFrom(aQuery);
 
-    VerifyOrExit(info.mQueryType == kIp4AddressQuery);
+    VerifyOrExit(info.mQueryType == kIp6AddressQuery);
     VerifyOrExit(info.mConfig.GetNat64Mode() == QueryConfig::kNat64Allow);
 
     // We send a new query for IPv4 address resolution


Test patch:
diff --git a/tests/unit/test_dns_client.cpp b/tests/unit/test_dns_client.cpp
index b4947f58561..244b04bf638 100644
--- a/tests/unit/test_dns_client.cpp
+++ b/tests/unit/test_dns_client.cpp
@@ -39,6 +39,7 @@
 
 #include "common/arg_macros.hpp"
 #include "common/array.hpp"
+#include "common/clearable.hpp"
 #include "common/string.hpp"
 #include "common/time.hpp"
 #include "instance/instance.hpp"
@@ -353,6 +354,64 @@ const char *ServiceModeToString(Dns::Client::QueryConfig::ServiceMode aMode)
 
 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
+static constexpr uint16_t kMaxAddresses = 10;
+
+struct AddressInfo
+{
+    void Reset(void) { ClearAllBytes(*this); }
+
+    uint16_t          mCallbackCount;
+    Error             mError;
+    Dns::Name::Buffer mHostName;
+    Ip6::Address      mHostAddresses[kMaxAddresses];
+    uint8_t           mNumHostAddresses;
+};
+
+static AddressInfo sAddressInfo;
+
+void AddressCallback(otError aError, const otDnsAddressResponse *aResponse, void *aContext)
+{
+    const Dns::Client::AddressResponse &response = AsCoreType(aResponse);
+
+    Log("AddressCallback");
+    Log("   Error: %s", ErrorToString(aError));
+
+    VerifyOrQuit(aContext == sInstance);
+
+    sAddressInfo.mCallbackCount++;
+    sAddressInfo.mError = aError;
+
+    SuccessOrExit(aError);
+
+    SuccessOrQuit(response.GetHostName(sAddressInfo.mHostName, sizeof(sAddressInfo.mHostName)));
+    Log("   HostName: %s", sAddressInfo.mHostName);
+
+    for (uint16_t index = 0;; index++)
+    {
+        Error    error;
+        uint32_t ttl;
+
+        VerifyOrQuit(index < kMaxAddresses);
+
+        error = response.GetAddress(index, sAddressInfo.mHostAddresses[index], ttl);
+
+        if (error == kErrorNotFound)
+        {
+            sAddressInfo.mNumHostAddresses = index;
+            break;
+        }
+
+        SuccessOrQuit(error);
+
+        Log("  %2u) %s ttl:%lu", index + 1, sAddressInfo.mHostAddresses[index].ToString().AsCString(), ToUlong(ttl));
+    }
+
+exit:
+    return;
+}
+
+//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
 struct BrowseInfo
 {
     void Reset(void) { mCallbackCount = 0; }
@@ -498,6 +557,7 @@ void TestDnsClient(void)
     };
 
     Array<Ip6::Address, kNumAddresses>      addresses;
+    NetworkData::ExternalRouteConfig        routeConfig;
     Srp::Server                            *srpServer;
     Srp::Client                            *srpClient;
     Srp::Client::Service                    service1;
@@ -514,6 +574,22 @@ void TestDnsClient(void)
 
     InitTest();
 
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Add a route prefix (with NAT64 flag) to network data");
+
+    routeConfig.Clear();
+    SuccessOrQuit(AsCoreType(&routeConfig.mPrefix.mPrefix).FromString("64:ff9b::"));
+    routeConfig.mPrefix.mLength = 96;
+    routeConfig.mPreference     = NetworkData::kRoutePreferenceMedium;
+    routeConfig.mNat64          = true;
+    routeConfig.mStable         = true;
+
+    SuccessOrQuit(otBorderRouterAddRoute(sInstance, &routeConfig));
+    SuccessOrQuit(otBorderRouterRegister(sInstance));
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Add addresses on Thread netif");
+
     for (const char *addrString : kAddresses)
     {
         otNetifAddress netifAddr;
@@ -583,6 +659,40 @@ void TestDnsClient(void)
     VerifyOrQuit(dnsClient->GetDefaultConfig().GetServiceMode() ==
                  Dns::Client::QueryConfig::kServiceModeSrvTxtOptimize);
 
+    //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+    // Validate DNS Client `ResolveAddress()`
+
+    sAddressInfo.Reset();
+    Log("ResolveAddress(%s)", kHostFullName);
+    SuccessOrQuit(dnsClient->ResolveAddress(kHostFullName, AddressCallback, sInstance));
+    AdvanceTime(100);
+    VerifyOrQuit(sAddressInfo.mCallbackCount == 1);
+    SuccessOrQuit(sAddressInfo.mError);
+    VerifyOrQuit(sAddressInfo.mNumHostAddresses == GetArrayLength(kAddresses));
+
+    for (uint8_t index = 0; index < sAddressInfo.mNumHostAddresses; index++)
+    {
+        VerifyOrQuit(addresses.Contains(sAddressInfo.mHostAddresses[index]));
+    }
+
+    //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+    // Validate DNS Client `ResolveIp4Address()`
+
+    sAddressInfo.Reset();
+    Log("ResolveIp4Address(%s)", kHostFullName);
+    SuccessOrQuit(dnsClient->ResolveIp4Address(kHostFullName, AddressCallback, sInstance));
+    AdvanceTime(100);
+    VerifyOrQuit(sAddressInfo.mCallbackCount == 1);
+    SuccessOrQuit(sAddressInfo.mError);
+    VerifyOrQuit(sAddressInfo.mNumHostAddresses == 0);
+
+    sAddressInfo.Reset();
+    Log("ResolveIp4Address(%s)", "badname");
+    SuccessOrQuit(dnsClient->ResolveIp4Address("badname", AddressCallback, sInstance));
+    AdvanceTime(100);
+    VerifyOrQuit(sAddressInfo.mCallbackCount == 1);
+    VerifyOrQuit(sAddressInfo.mError != kErrorNone);
+
     //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
     // Validate DNS Client `Browse()`