mirror of
https://github.com/espressif/openthread.git
synced 2026-06-05 21:14:49 +00:00
[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:
committed by
GitHub
parent
901d2d3bec
commit
4071e328fe
+192
-10
@@ -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;
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user