[dns-client] switch to separate SRV/TXT queries on response timeout (#10444)

This commit updates `Dns::Client` so that when resolving a service
using `kServiceModeSrvTxtOptimize`, it switches to single-question
query mode and sends separate parallel SRV and TXT queries upon the
first response timeout. This is in addition to the existing behavior
of switching to separate queries upon receiving a response with an
error rcode from the server.

The `test_dns_client` unit test is also updated to validate this
scenario. New `TestMode` configurations are added to server to
control its behavior, allowing it to either reject multi-question
queries (by sending a "FormatError" rcode) or ignore them (sending
no response).
This commit is contained in:
Abtin Keshavarzian
2024-06-26 21:35:30 -04:00
committed by GitHub
parent 7252cadb5a
commit f2ed3e9c0d
4 changed files with 73 additions and 13 deletions
+12 -2
View File
@@ -1520,7 +1520,17 @@ void Client::HandleTimer(void)
break;
}
IgnoreError(SendQuery(*query, info, /* aUpdateTimer */ false));
#if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
if (ReplaceWithSeparateSrvTxtQueries(*query) == kErrorNone)
{
LogInfo("Switching to separate SRV/TXT on response timeout");
info.ReadFrom(*query);
}
else
#endif
{
IgnoreError(SendQuery(*query, info, /* aUpdateTimer */ false));
}
}
nextTime.UpdateIfEarlier(info.mRetransmissionTime);
@@ -1534,7 +1544,7 @@ void Client::HandleTimer(void)
}
}
mTimer.FireAt(nextTime);
mTimer.FireAtIfEarlier(nextTime);
#if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE
if (!hasTcpQuery && mTcpState != kTcpUninitialized)
+12 -5
View File
@@ -163,7 +163,8 @@ exit:
void Server::ProcessQuery(Request &aRequest)
{
ResponseCode rcode = Header::kResponseSuccess;
ResponseCode rcode = Header::kResponseSuccess;
bool shouldRespond = true;
Response response(GetInstance());
#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE
@@ -193,7 +194,7 @@ void Server::ProcessQuery(Request &aRequest)
SuccessOrExit(rcode);
#endif
SuccessOrExit(rcode = aRequest.ParseQuestions(mTestMode));
SuccessOrExit(rcode = aRequest.ParseQuestions(mTestMode, shouldRespond));
SuccessOrExit(rcode = response.AddQuestionsFrom(aRequest));
#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
@@ -225,7 +226,10 @@ exit:
response.SetResponseCode(rcode);
}
response.Send(*aRequest.mMessageInfo);
if (shouldRespond)
{
response.Send(*aRequest.mMessageInfo);
}
}
Server::Response::Response(Instance &aInstance)
@@ -296,7 +300,7 @@ exit:
return;
}
Server::ResponseCode Server::Request::ParseQuestions(uint8_t aTestMode)
Server::ResponseCode Server::Request::ParseQuestions(uint8_t aTestMode, bool &aShouldRespond)
{
// Parse header and questions from a `Request` query message and
// determine the `QueryType`.
@@ -306,6 +310,8 @@ Server::ResponseCode Server::Request::ParseQuestions(uint8_t aTestMode)
uint16_t questionCount = mHeader.GetQuestionCount();
Question question;
aShouldRespond = true;
VerifyOrExit(mHeader.GetQueryType() == Header::kQueryTypeStandard, rcode = Header::kResponseNotImplemented);
VerifyOrExit(!mHeader.IsTruncationFlagSet());
@@ -335,7 +341,8 @@ Server::ResponseCode Server::Request::ParseQuestions(uint8_t aTestMode)
if (questionCount > 1)
{
VerifyOrExit(!(aTestMode & kTestModeSingleQuestionOnly));
VerifyOrExit(!(aTestMode & kTestModeRejectMultiQuestionQuery));
VerifyOrExit(!(aTestMode & kTestModeIgnoreMultiQuestionQuery), aShouldRespond = false);
VerifyOrExit(questionCount == 2);
+4 -3
View File
@@ -291,8 +291,9 @@ public:
*/
enum TestModeFlags : uint8_t
{
kTestModeSingleQuestionOnly = 1 << 0, ///< Allow single question in query, send `FormatError` otherwise.
kTestModeEmptyAdditionalSection = 1 << 1, ///< Do not include any RR in additional section.
kTestModeRejectMultiQuestionQuery = 1 << 0, ///< Send `FormatError` for a query with multiple questions.
kTestModeIgnoreMultiQuestionQuery = 1 << 1, ///< Ignore a query with multiple questions (send no response).
kTestModeEmptyAdditionalSection = 1 << 2, ///< Do not include any RR in additional section.
};
static constexpr uint8_t kTestModeDisabled = 0; ///< Test mode is disabled (no flags).
@@ -346,7 +347,7 @@ private:
struct Request
{
ResponseCode ParseQuestions(uint8_t aTestMode);
ResponseCode ParseQuestions(uint8_t aTestMode, bool &aShouldRespond);
const Message *mMessage;
const Ip6::MessageInfo *mMessageInfo;
+45 -3
View File
@@ -671,8 +671,8 @@ void TestDnsClient(void)
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Set TestMode on server to only accept single question");
dnsServer->SetTestMode(Dns::ServiceDiscovery::Server::kTestModeSingleQuestionOnly);
Log("Set TestMode on server to reject multi-question queries and send error");
dnsServer->SetTestMode(Dns::ServiceDiscovery::Server::kTestModeRejectMultiQuestionQuery);
Log("ResolveService(%s,%s) with ServiceMode %s", kInstance1Label, kService1FullName,
ServiceModeToString(Dns::Client::QueryConfig::kServiceModeSrvTxtOptimize));
@@ -706,6 +706,48 @@ void TestDnsClient(void)
dnsServer->SetTestMode(Dns::ServiceDiscovery::Server::kTestModeDisabled);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Set TestMode on server to ignore multi-question queries (send no response)");
dnsServer->SetTestMode(Dns::ServiceDiscovery::Server::kTestModeIgnoreMultiQuestionQuery);
Log("ResolveService(%s,%s) with ServiceMode %s", kInstance1Label, kService1FullName,
ServiceModeToString(Dns::Client::QueryConfig::kServiceModeSrvTxtOptimize));
queryConfig.Clear();
queryConfig.mServiceMode = static_cast<otDnsServiceMode>(Dns::Client::QueryConfig::kServiceModeSrvTxtOptimize);
sResolveServiceInfo.Reset();
SuccessOrQuit(
dnsClient->ResolveService(kInstance1Label, kService1FullName, ServiceCallback, sInstance, &queryConfig));
AdvanceTime(10 * 1000); // Wait longer than client response timeout.
VerifyOrQuit(sResolveServiceInfo.mCallbackCount == 1);
SuccessOrQuit(sResolveServiceInfo.mError);
// Use `kServiceModeSrvTxt` and check that server does ignore two questions.
Log("ResolveService(%s,%s) with ServiceMode %s", kInstance1Label, kService1FullName,
ServiceModeToString(Dns::Client::QueryConfig::kServiceModeSrvTxt));
queryConfig.Clear();
queryConfig.mServiceMode = static_cast<otDnsServiceMode>(Dns::Client::QueryConfig::kServiceModeSrvTxt);
sResolveServiceInfo.Reset();
SuccessOrQuit(
dnsClient->ResolveService(kInstance1Label, kService1FullName, ServiceCallback, sInstance, &queryConfig));
// Wait for the client to time out after exhausting all retry attempts, and
// ensure that a `kErrorResponseTimeout` error is reported.
AdvanceTime(45 * 1000);
VerifyOrQuit(sResolveServiceInfo.mCallbackCount == 1);
VerifyOrQuit(sResolveServiceInfo.mError == kErrorResponseTimeout);
dnsServer->SetTestMode(Dns::ServiceDiscovery::Server::kTestModeDisabled);
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Validate DNS Client `ResolveService()` using all service modes
// when sever does not provide any RR in the addition data section.
@@ -832,7 +874,7 @@ void TestDnsClient(void)
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Set TestMode on server to not include any RR in additional section AND to only accept single question");
dnsServer->SetTestMode(Dns::ServiceDiscovery::Server::kTestModeEmptyAdditionalSection +
Dns::ServiceDiscovery::Server::kTestModeSingleQuestionOnly);
Dns::ServiceDiscovery::Server::kTestModeRejectMultiQuestionQuery);
Log("ResolveServiceAndHostAddress(%s,%s) with ServiceMode: %s", kInstance1Label, kService1FullName,
ServiceModeToString(Dns::Client::QueryConfig::kServiceModeSrvTxtOptimize));