[dnssd-server] support SOA and NS query (#11413)

This commit updates the DNS-SD server to support responding to SOA and
NS record queries (including `ANY` record type queries) for the
default service domain. The recommended values for SOA record data
from RFC-8766 section 6.1 are used. The server name included in
SOA/NS answers is derived from the Extended Address of the device,
ensuring it remains fixed and consistent over reboots as long as the
device's Extended Address stays the same.

A new detailed test case is added in the `test_dns_client` unit test
to cover all the newly added behavior.
This commit is contained in:
Abtin Keshavarzian
2025-05-20 18:33:14 -07:00
committed by GitHub
parent 901d2d3bec
commit 4071e328fe
3 changed files with 502 additions and 11 deletions
+192 -10
View File
@@ -51,6 +51,10 @@ const char Server::kMdnsDomainName[] = "local.";
const char *Server::kBlockedDomains[] = {"ipv4only.arpa."};
#endif
#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE || OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE
const char Server::kSoaRnameLabel[] = "postmaster";
#endif
Server::Server(Instance &aInstance)
: InstanceLocator(aInstance)
, mSocket(aInstance, *this)
@@ -64,6 +68,10 @@ Server::Server(Instance &aInstance)
, mTestMode(kTestModeDisabled)
{
mCounters.Clear();
#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE || OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE
ClearAllBytes(mSoaServerName);
#endif
}
Error Server::Start(void)
@@ -79,6 +87,10 @@ Error Server::Start(void)
Get<Srp::Server>().HandleDnssdServerStateChange();
#endif
#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE || OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE
ConstructSoaServerName();
#endif
LogInfo("Started");
#if OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE
@@ -187,6 +199,13 @@ void Server::ProcessQuery(Request &aRequest)
response.Log();
#endif
#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE || OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE
// Try to resolve SOA/NS queries. If successfully
// resolved, the response will not be empty.
VerifyOrExit(response.ResolveSoaOrNsQuery() == kErrorNone, rcode = Header::kResponseServerFailure);
VerifyOrExit(response.IsEmpty());
#endif
#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
switch (response.ResolveBySrp())
{
@@ -408,6 +427,12 @@ exit:
return rcode;
}
bool Server::Response::IsEmpty(void) const
{
return (mHeader.GetAnswerCount() == 0) && (mHeader.GetAuthorityRecordCount() == 0) &&
(mHeader.GetAdditionalRecordCount() == 0);
}
Error Server::Response::ParseQueryName(void)
{
// Parses the query name, determines name compression
@@ -439,14 +464,6 @@ Error Server::Response::ParseQueryName(void)
uint8_t labelLength = sizeof(label);
uint16_t comapreOffset;
SuccessOrExit(error = Name::ReadLabel(*mMessage, offset, label, labelLength));
if ((mQuestions.IsFor(kRrTypePtr) || mQuestions.IsFor(kRrTypeAny)) &&
StringMatch(label, kSubLabel, kStringCaseInsensitiveMatch))
{
mOffsets.mServiceName = offset;
}
comapreOffset = offset;
if (Name::CompareName(*mMessage, comapreOffset, kDefaultDomainName) == kErrorNone)
@@ -454,9 +471,15 @@ Error Server::Response::ParseQueryName(void)
mOffsets.mDomainName = offset;
ExitNow();
}
}
error = kErrorParse;
SuccessOrExit(error = Name::ReadLabel(*mMessage, offset, label, labelLength));
if ((mQuestions.IsFor(kRrTypePtr) || mQuestions.IsFor(kRrTypeAny)) &&
StringMatch(label, kSubLabel, kStringCaseInsensitiveMatch))
{
mOffsets.mServiceName = offset;
}
}
exit:
return error;
@@ -466,6 +489,11 @@ void Server::Response::ReadQueryName(Name::Buffer &aName) const { Server::ReadQu
bool Server::Response::QueryNameMatches(const char *aName) const { return Server::QueryNameMatches(*mMessage, aName); }
bool Server::Response::QueryNameIsForDomain(const char *aDomainName) const
{
return Server::QueryNameIsForDomain(*mMessage, aDomainName);
}
Error Server::Response::AppendQueryName(void) { return Name::AppendPointerLabel(kQueryNameOffset, *mMessage); }
#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
@@ -786,6 +814,9 @@ void Server::Response::IncResourceRecordCount(void)
case kAnswerSection:
mHeader.SetAnswerCount(mHeader.GetAnswerCount() + 1);
break;
case kAuthoritySection:
mHeader.SetAuthorityRecordCount(mHeader.GetAuthorityRecordCount() + 1);
break;
case kAdditionalDataSection:
mHeader.SetAdditionalRecordCount(mHeader.GetAdditionalRecordCount() + 1);
break;
@@ -929,6 +960,149 @@ exit:
#endif // OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE || OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE
void Server::ConstructSoaServerName(void)
{
if (mSoaServerName[0] == kNullChar)
{
StringWriter writer(mSoaServerName, sizeof(mSoaServerName));
writer.Append("otDNS%s", Get<Mac::Mac>().GetExtAddress().ToString().AsCString());
}
}
Error Server::Response::ResolveSoaOrNsQuery(void)
{
Error error = kErrorNone;
VerifyOrExit(QueryNameIsForDomain(kDefaultDomainName));
mSection = kAnswerSection;
// Handle AAAA (or ANY) query for the server host name used for
// SOA/NS answer.
if (mQuestions.IsFor(kRrTypeAaaa) || mQuestions.IsFor(kRrTypeAny))
{
Name::Buffer serverFullName;
ConstructFullName(Get<Server>().mSoaServerName, serverFullName);
if (QueryNameMatches(serverFullName))
{
for (Ip6::Netif::UnicastAddress &unicastAddr : Get<ThreadNetif>().GetUnicastAddresses())
{
if (!unicastAddr.mValid || !unicastAddr.mPreferred || unicastAddr.GetAddress().IsLinkLocalUnicast() ||
Get<Mle::Mle>().IsMeshLocalAddress(unicastAddr.GetAddress()))
{
continue;
}
SuccessOrExit(error = AppendAaaaRecord(unicastAddr.GetAddress(), kServerAaaaTtl));
}
if (IsEmpty())
{
SuccessOrExit(error = AppendAaaaRecord(Get<Mle::Mle>().GetMeshLocalEid(), kServerAaaaTtl));
}
ExitNow();
}
}
// Handle SOA or NS (or ANY) queries. The SOA or NS query should
// be for the domain name (`default.service.arpa`). If it is for a
// name under the default domain, e.g. `host1.default.service.arpa`,
// we provide the SOA record in Authority section in the response.
if (!QueryNameMatches(kDefaultDomainName))
{
VerifyOrExit(mQuestions.IsFor(kRrTypeSoa) || mQuestions.IsFor(kRrTypeNs));
mSection = kAuthoritySection;
ExitNow(error = AppendSoaRecord());
}
if (mQuestions.IsFor(kRrTypeSoa) || mQuestions.IsFor(kRrTypeAny))
{
SuccessOrExit(error = AppendSoaRecord());
}
if (mQuestions.IsFor(kRrTypeNs) || mQuestions.IsFor(kRrTypeAny))
{
SuccessOrExit(error = AppendNsRecord());
}
exit:
return error;
}
Error Server::Response::AppendSoaRecord(void)
{
const uint32_t kFields[] = {
kSoaSerial, kSoaRefresh, kSoaRetry, kSoaExpire, kSoaMinimum,
};
Error error = kErrorNone;
ResourceRecord record;
uint16_t offset;
record.Init(kRrTypeSoa);
record.SetTtl(kSoaTtl);
SuccessOrExit(error = Name::AppendPointerLabel(mOffsets.mDomainName, *mMessage));
offset = mMessage->GetLength();
SuccessOrExit(error = mMessage->Append(record));
// MNAME
SuccessOrExit(error = Name::AppendLabel(Get<Server>().mSoaServerName, *mMessage));
SuccessOrExit(error = Name::AppendPointerLabel(mOffsets.mDomainName, *mMessage));
// RNAME: Use constant "postmaster.<domain>"
SuccessOrExit(error = Name::AppendLabel(kSoaRnameLabel, *mMessage));
SuccessOrExit(error = Name::AppendPointerLabel(mOffsets.mDomainName, *mMessage));
for (uint32_t fieldValue : kFields)
{
SuccessOrExit(error = mMessage->Append<uint32_t>(BigEndian::HostSwap32(fieldValue)));
}
ResourceRecord::UpdateRecordLengthInMessage(*mMessage, offset);
IncResourceRecordCount();
exit:
return error;
}
Error Server::Response::AppendNsRecord(void)
{
Error error = kErrorNone;
ResourceRecord record;
uint16_t offset;
record.Init(kRrTypeNs);
record.SetTtl(kNsTtl);
SuccessOrExit(error = Name::AppendPointerLabel(mOffsets.mDomainName, *mMessage));
offset = mMessage->GetLength();
SuccessOrExit(error = mMessage->Append(record));
SuccessOrExit(error = Name::AppendLabel(Get<Server>().mSoaServerName, *mMessage));
SuccessOrExit(error = Name::AppendPointerLabel(mOffsets.mDomainName, *mMessage));
ResourceRecord::UpdateRecordLengthInMessage(*mMessage, offset);
IncResourceRecordCount();
exit:
return error;
}
#endif // OPENTHREAD_CONFIG_SRP_SERVER_ENABLE || OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE
#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE
bool Server::ShouldForwardToUpstream(const Request &aRequest) const
{
@@ -1096,6 +1270,14 @@ bool Server::QueryNameMatches(const Message &aQuery, const char *aName)
return (Name::CompareName(aQuery, offset, aName) == kErrorNone);
}
bool Server::QueryNameIsForDomain(const Message &aQuery, const char *aDomainName)
{
Name::Buffer name;
ReadQueryName(aQuery, name);
return Name::IsSubDomainOf(name, aDomainName);
}
void Server::ReadQueryInstanceName(const ProxyQuery &aQuery, const ProxyQueryInfo &aInfo, Name::Buffer &aName)
{
uint16_t offset = aInfo.mOffsets.mInstanceName;
+31
View File
@@ -312,6 +312,7 @@ private:
static constexpr uint16_t kMaxConcurrentUpstreamQueries = 32;
static constexpr uint16_t kRrTypeA = ResourceRecord::kTypeA;
static constexpr uint16_t kRrTypeNs = ResourceRecord::kTypeNs;
static constexpr uint16_t kRrTypeSoa = ResourceRecord::kTypeSoa;
static constexpr uint16_t kRrTypeCname = ResourceRecord::kTypeCname;
static constexpr uint16_t kRrTypePtr = ResourceRecord::kTypePtr;
@@ -321,6 +322,16 @@ private:
static constexpr uint16_t kRrTypeSrv = ResourceRecord::kTypeSrv;
static constexpr uint16_t kRrTypeAny = ResourceRecord::kTypeAny;
// Recommended values for SOA record (RFC 8766 section 6.1).
static constexpr uint32_t kSoaSerial = 0;
static constexpr uint32_t kSoaRefresh = 7200;
static constexpr uint32_t kSoaRetry = 3600;
static constexpr uint32_t kSoaExpire = 86400;
static constexpr uint32_t kSoaMinimum = 10;
static constexpr uint32_t kSoaTtl = 7200;
static constexpr uint32_t kNsTtl = 7200;
static constexpr uint32_t kServerAaaaTtl = 3600;
typedef Header::Response ResponseCode;
typedef Message ProxyQuery;
@@ -329,6 +340,7 @@ private:
enum Section : uint8_t
{
kAnswerSection,
kAuthoritySection,
kAdditionalDataSection,
};
@@ -412,9 +424,11 @@ private:
Error AllocateAndInitFrom(const Request &aRequest);
void InitFrom(ProxyQuery &aQuery, const ProxyQueryInfo &aInfo);
void SetResponseCode(ResponseCode aResponseCode) { mHeader.SetResponseCode(aResponseCode); }
bool IsEmpty(void) const;
Error ParseQueryName(void);
void ReadQueryName(Name::Buffer &aName) const;
bool QueryNameMatches(const char *aName) const;
bool QueryNameIsForDomain(const char *aDomainName) const;
Error AppendQueryName(void);
Error AppendPtrRecord(const char *aInstanceLabel, uint32_t aTtl);
Error AppendSrvRecord(const ServiceInstanceInfo &aInstanceInfo);
@@ -455,6 +469,11 @@ private:
Error AppendHostIp6Addresses(const ProxyResult &aResult);
Error AppendHostIp4Addresses(const ProxyResult &aResult);
Error AppendGenericRecord(const ProxyResult &aResult);
#endif
#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE || OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE
Error ResolveSoaOrNsQuery(void);
Error AppendSoaRecord(void);
Error AppendNsRecord(void);
#endif
template <typename ServiceType> Error AppendServiceRecords(const ServiceType &aService);
@@ -564,6 +583,7 @@ private:
static void ReadQueryName(const Message &aQuery, Name::Buffer &aName);
static bool QueryNameMatches(const Message &aQuery, const char *aName);
static bool QueryNameIsForDomain(const Message &aQuery, const char *aDomainName);
static void ReadQueryInstanceName(const ProxyQuery &aQuery, const ProxyQueryInfo &aInfo, Name::Buffer &aName);
static void ReadQueryInstanceName(const ProxyQuery &aQuery,
const ProxyQueryInfo &aInfo,
@@ -596,6 +616,10 @@ private:
Error ResolveByUpstream(const Request &aRequest);
#endif
#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE || OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE
void ConstructSoaServerName(void);
#endif
void HandleTimer(void);
void ResetTimer(void);
@@ -610,6 +634,9 @@ private:
#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE
static const char *kBlockedDomains[];
#endif
#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE || OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE
static const char kSoaRnameLabel[];
#endif
ServerSocket mSocket;
@@ -621,6 +648,10 @@ private:
DiscoveryProxy mDiscoveryProxy;
#endif
#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE || OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE
Name::LabelBuffer mSoaServerName;
#endif
#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE
bool mEnableUpstreamQuery;
UpstreamQueryTransaction mUpstreamQueryTransactions[kMaxConcurrentUpstreamQueries];
+279 -1
View File
@@ -551,7 +551,7 @@ struct QueryRecordInfo
{
struct Record : public Dns::Client::RecordInfo
{
static constexpr uint16_t kMaxRecordDataSize = 200;
static constexpr uint16_t kMaxRecordDataSize = 500;
void Init(void)
{
@@ -670,6 +670,94 @@ void ValidatePtrRecordData(const QueryRecordInfo::Record &aRecord, const char *a
data->Free();
}
void ValidateSoaRecordData(const QueryRecordInfo::Record &aRecord, const char *aServerName)
{
static constexpr uint32_t kSoaSerial = 0;
static constexpr uint32_t kSoaRefresh = 7200;
static constexpr uint32_t kSoaRetry = 3600;
static constexpr uint32_t kSoaExpire = 86400;
static constexpr uint32_t kSoaMinimum = 10;
uint16_t offset;
Message *message;
Dns::Name::Buffer name;
VerifyOrQuit(StringMatch(aRecord.mNameBuffer, "default.service.arpa."));
VerifyOrQuit(aRecord.mRecordType == Dns::ResourceRecord::kTypeSoa);
VerifyOrQuit(aRecord.mTtl == 7200);
message = sInstance->Get<MessagePool>().Allocate(Message::kTypeOther);
VerifyOrQuit(message != nullptr);
VerifyOrQuit(aRecord.mRecordLength == aRecord.mDataBufferSize);
SuccessOrQuit(message->AppendBytes(aRecord.mDataBuffer, aRecord.mDataBufferSize));
// Validate the SOA record data.
offset = 0;
// MNAME field:
SuccessOrQuit(Dns::Name::ReadName(*message, offset, name));
VerifyOrQuit(StringMatch(name, aServerName, kStringCaseInsensitiveMatch));
// RNAME: must be "postmaster.default.service.arpa."
SuccessOrQuit(Dns::Name::ReadName(*message, offset, name));
SuccessOrQuit(Dns::Name::StripName(name, "default.service.arpa."));
VerifyOrQuit(StringMatch(name, "postmaster"));
// SERIAL
VerifyOrQuit(message->Compare<uint32_t>(offset, BigEndian::HostSwap32(kSoaSerial)));
offset += sizeof(uint32_t);
// REFRESH
VerifyOrQuit(message->Compare<uint32_t>(offset, BigEndian::HostSwap32(kSoaRefresh)));
offset += sizeof(uint32_t);
// RETRY
VerifyOrQuit(message->Compare<uint32_t>(offset, BigEndian::HostSwap32(kSoaRetry)));
offset += sizeof(uint32_t);
// EXPIRE
VerifyOrQuit(message->Compare<uint32_t>(offset, BigEndian::HostSwap32(kSoaExpire)));
offset += sizeof(uint32_t);
// MINIMUM
VerifyOrQuit(message->Compare<uint32_t>(offset, BigEndian::HostSwap32(kSoaMinimum)));
offset += sizeof(uint32_t);
VerifyOrQuit(offset == message->GetLength());
message->Free();
}
void ValidateNsRecordData(const QueryRecordInfo::Record &aRecord, const char *aServerName)
{
uint16_t offset;
Message *message;
Dns::Name::Buffer name;
VerifyOrQuit(StringMatch(aRecord.mNameBuffer, "default.service.arpa."));
VerifyOrQuit(aRecord.mRecordType == Dns::ResourceRecord::kTypeNs);
VerifyOrQuit(aRecord.mTtl == 7200);
message = sInstance->Get<MessagePool>().Allocate(Message::kTypeOther);
VerifyOrQuit(message != nullptr);
VerifyOrQuit(aRecord.mRecordLength == aRecord.mDataBufferSize);
SuccessOrQuit(message->AppendBytes(aRecord.mDataBuffer, aRecord.mDataBufferSize));
// Validate the NS data
offset = 0;
SuccessOrQuit(Dns::Name::ReadName(*message, offset, name));
VerifyOrQuit(StringMatch(name, aServerName, kStringCaseInsensitiveMatch));
VerifyOrQuit(offset == message->GetLength());
message->Free();
}
//----------------------------------------------------------------------------------------------------------------------
void TestDnsClient(void)
@@ -1734,6 +1822,195 @@ void TestDnssdServerProxyCallback(void)
Log("End of TestDnssdServerProxyCallback");
}
void TestDnssdSoaNsResponse(void)
{
Srp::Server *srpServer;
Srp::Client *srpClient;
Dns::Client *dnsClient;
Dns::Name::Buffer serverName;
StringWriter writer(serverName, sizeof(serverName));
Log("--------------------------------------------------------------------------------------------");
Log("TestDnssdSoaNsResponse");
InitTest();
srpServer = &sInstance->Get<Srp::Server>();
srpClient = &sInstance->Get<Srp::Client>();
dnsClient = &sInstance->Get<Dns::Client>();
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Start SRP server.
SuccessOrQuit(srpServer->SetAddressMode(Srp::Server::kAddressModeUnicast));
VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateDisabled);
srpServer->SetEnabled(true);
VerifyOrQuit(srpServer->GetState() != Srp::Server::kStateDisabled);
AdvanceTime(10000);
VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateRunning);
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Start SRP client.
srpClient->EnableAutoStartMode(nullptr, nullptr);
VerifyOrQuit(srpClient->IsAutoStartModeEnabled());
AdvanceTime(2000);
VerifyOrQuit(srpClient->IsRunning());
writer.Append("otDNS%s.default.service.arpa.", sInstance->Get<Mac::Mac>().GetExtAddress().ToString().AsCString());
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("SOA Query");
for (uint8_t iter = 0; iter < 2; iter++)
{
sQueryRecordInfo.Reset();
// First iteration: Query for `default.service.arpa.` directly, and
// validate that we see the SOA record in the Answer section.
// Second iteration: Query for `myhost.default.service.arpa.`, and
// validate that we see the SOA record in the Authority section.
if (iter == 0)
{
Log("QueryRecord(%s) for SOA RR", "default.service.arpa.");
SuccessOrQuit(dnsClient->QueryRecord(Dns::ResourceRecord::kTypeSoa, "default", "service.arpa.",
RecordCallback, sInstance));
}
else
{
Log("QueryRecord(%s) for SOA RR", "myhost.default.service.arpa.");
SuccessOrQuit(dnsClient->QueryRecord(Dns::ResourceRecord::kTypeSoa, "myhost", "default.service.arpa.",
RecordCallback, sInstance));
}
AdvanceTime(100);
VerifyOrQuit(sQueryRecordInfo.mCallbackCount == 1);
SuccessOrQuit(sQueryRecordInfo.mError);
VerifyOrQuit(sQueryRecordInfo.mNumRecords == 1);
if (iter == 0)
{
VerifyOrQuit(StringMatch(sQueryRecordInfo.mQueryName, "default.service.arpa."));
VerifyOrQuit(MapEnum(sQueryRecordInfo.mRecords[0].mSection) == Dns::Client::RecordInfo::kSectionAnswer);
}
else
{
VerifyOrQuit(StringMatch(sQueryRecordInfo.mQueryName, "myhost.default.service.arpa."));
VerifyOrQuit(MapEnum(sQueryRecordInfo.mRecords[0].mSection) == Dns::Client::RecordInfo::kSectionAuthority);
}
ValidateSoaRecordData(sQueryRecordInfo.mRecords[0], serverName);
AdvanceTime(1000);
}
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("NS Query");
for (uint8_t iter = 0; iter < 2; iter++)
{
sQueryRecordInfo.Reset();
// First iteration: Query for `default.service.arpa.` directly and
// validate that we see the NS response in the Answer section. Second
// iteration: Query for `myhost.default.service.arpa.` and validate
// that we see the SOA record instead in the Authority section in
// the response.
if (iter == 0)
{
Log("QueryRecord(%s) for NS RR", "default.service.arpa.");
SuccessOrQuit(dnsClient->QueryRecord(Dns::ResourceRecord::kTypeNs, "default", "service.arpa.",
RecordCallback, sInstance));
}
else
{
Log("QueryRecord(%s) for NS RR", "myhost.default.service.arpa.");
SuccessOrQuit(dnsClient->QueryRecord(Dns::ResourceRecord::kTypeNs, "myhost", "default.service.arpa.",
RecordCallback, sInstance));
}
AdvanceTime(100);
VerifyOrQuit(sQueryRecordInfo.mCallbackCount == 1);
SuccessOrQuit(sQueryRecordInfo.mError);
VerifyOrQuit(sQueryRecordInfo.mNumRecords == 1);
if (iter == 0)
{
VerifyOrQuit(StringMatch(sQueryRecordInfo.mQueryName, "default.service.arpa."));
VerifyOrQuit(MapEnum(sQueryRecordInfo.mRecords[0].mSection) == Dns::Client::RecordInfo::kSectionAnswer);
ValidateNsRecordData(sQueryRecordInfo.mRecords[0], serverName);
}
else
{
VerifyOrQuit(StringMatch(sQueryRecordInfo.mQueryName, "myhost.default.service.arpa."));
VerifyOrQuit(MapEnum(sQueryRecordInfo.mRecords[0].mSection) == Dns::Client::RecordInfo::kSectionAuthority);
ValidateSoaRecordData(sQueryRecordInfo.mRecords[0], serverName);
}
AdvanceTime(1000);
}
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
sQueryRecordInfo.Reset();
Log("QueryRecord(%s) for ANY RR", "default.service.arpa.");
SuccessOrQuit(
dnsClient->QueryRecord(Dns::ResourceRecord::kTypeAny, "default", "service.arpa.", RecordCallback, sInstance));
AdvanceTime(100);
VerifyOrQuit(sQueryRecordInfo.mCallbackCount == 1);
SuccessOrQuit(sQueryRecordInfo.mError);
VerifyOrQuit(sQueryRecordInfo.mNumRecords == 2);
VerifyOrQuit(StringMatch(sQueryRecordInfo.mQueryName, "default.service.arpa."));
for (uint8_t index = 0; index < 2; index++)
{
QueryRecordInfo::Record &record = sQueryRecordInfo.mRecords[index];
VerifyOrQuit(MapEnum(record.mSection) == Dns::Client::RecordInfo::kSectionAnswer);
switch (record.mRecordType)
{
case Dns::ResourceRecord::kTypeSoa:
ValidateSoaRecordData(record, serverName);
break;
case Dns::ResourceRecord::kTypeNs:
ValidateNsRecordData(record, serverName);
break;
default:
VerifyOrQuit(false);
break;
}
AdvanceTime(1000);
}
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
sAddressInfo.Reset();
Log("ResolveAddress(%s)", serverName);
SuccessOrQuit(dnsClient->ResolveAddress(serverName, AddressCallback, sInstance));
AdvanceTime(100);
VerifyOrQuit(sAddressInfo.mCallbackCount >= 1);
SuccessOrQuit(sAddressInfo.mError);
VerifyOrQuit(sAddressInfo.mHostAddresses[0] == sInstance->Get<Mle::Mle>().GetMeshLocalEid());
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Finalize OT instance and validate all heap allocations are freed.
Log("Finalizing OT instance");
FinalizeTest();
Log("End of TestDnssdSoaNsResponse");
}
#endif // ENABLE_DNS_TEST
int main(void)
@@ -1741,6 +2018,7 @@ int main(void)
#if ENABLE_DNS_TEST
TestDnsClient();
TestDnssdServerProxyCallback();
TestDnssdSoaNsResponse();
printf("All tests passed\n");
#else
printf("DNS_CLIENT or DSNSSD_SERVER feature is not enabled\n");