[dnssd] support ANY record type queries (#11447)

This commit updates the DNS-SD `Server` implementation to support
queries for the `ANY` record type. This is supported whether a query
is resolved using the SRP server or the OpenThread native Discovery
Proxy.

When a query is resolved using the SRP server database, all known
records that match the query name and type are included in the
response (e.g., AAAA and KEY records for a hostname; SRV,  TXT and
KEY records for a service instance name; and PTR records for service
type or sub-type query names).

Note that unlike mDNS, where an `ANY` query is expected to elicit all
known matching records, in the case of a unicast DNS query for `ANY`,
the response is only required to contain at least one matching
record, not necessarily all of them. This will be the behavior when
the Discovery Proxy is used to resolve a unicast DNS `ANY` query
(i.e., once the first answer is received from the Discovery Proxy
(mDNS), a response is prepared and sent to the client).

The unit tests `test_dns_client` and `test_dnssd_discovery_proxy` are
updated to validate the new `ANY` query behavior.
This commit is contained in:
Abtin Keshavarzian
2025-04-28 12:55:47 -07:00
committed by GitHub
parent 6b5493c9f1
commit b3c4a7e583
4 changed files with 412 additions and 87 deletions
+117 -80
View File
@@ -290,6 +290,21 @@ exit:
return;
}
bool Server::Questions::IsFor(uint16_t aRrType) const
{
// Check if any of questions is for `aRrType`.
return (mFirstRrType == aRrType) || (mSecondRrType == aRrType);
}
Server::Section Server::Questions::SectionFor(uint16_t aRrType) const
{
// Determine section to append `aRrType` record based on the
// query questions.
return (IsFor(aRrType) || IsFor(kRrTypeAny)) ? kAnswerSection : kAdditionalDataSection;
}
Server::ResponseCode Server::Request::ParseQuestions(uint8_t aTestMode, bool &aShouldRespond)
{
// Parse header and questions from a `Request` query message and
@@ -349,7 +364,7 @@ Server::ResponseCode Server::Response::AddQuestionsFrom(const Request &aRequest)
// service instance names with dot characters in the instance
// label, are appended correctly.
SuccessOrExit(Name(*aRequest.mMessage, sizeof(Header)).AppendTo(*mMessage));
SuccessOrExit(Name(*aRequest.mMessage, kQueryNameOffset).AppendTo(*mMessage));
// Check the name to include the correct domain name and determine
// the domain name offset (for DNS name compression).
@@ -398,26 +413,17 @@ Error Server::Response::ParseQueryName(void)
offset = sizeof(Header);
SuccessOrExit(error = Name::ReadName(*mMessage, offset, name));
if (mQuestions.IsFor(kRrTypePtr))
{
// `mOffsets.mServiceName` may be updated as we read labels and if we
// determine that the query name is a sub-type service.
mOffsets.mServiceName = sizeof(Header);
}
else if (mQuestions.IsFor(kRrTypeSrv) || mQuestions.IsFor(kRrTypeTxt))
{
mOffsets.mInstanceName = sizeof(Header);
}
else
{
mOffsets.mHostName = sizeof(Header);
}
// `mOffsets.mServiceName` may be updated as we read labels and if we
// determine that the query name is a sub-type service.
mOffsets.mServiceName = kQueryNameOffset;
mOffsets.mInstanceName = kQueryNameOffset;
mOffsets.mHostName = kQueryNameOffset;
// Read the query name labels one by one to check if the name is
// service sub-type and also check that it is sub-domain of the
// default domain name and determine its offset
offset = sizeof(Header);
offset = kQueryNameOffset;
while (true)
{
@@ -427,7 +433,8 @@ Error Server::Response::ParseQueryName(void)
SuccessOrExit(error = Name::ReadLabel(*mMessage, offset, label, labelLength));
if (mQuestions.IsFor(kRrTypePtr) && StringMatch(label, kSubLabel, kStringCaseInsensitiveMatch))
if ((mQuestions.IsFor(kRrTypePtr) || mQuestions.IsFor(kRrTypeAny)) &&
StringMatch(label, kSubLabel, kStringCaseInsensitiveMatch))
{
mOffsets.mServiceName = offset;
}
@@ -451,7 +458,7 @@ void Server::Response::ReadQueryName(Name::Buffer &aName) const { Server::ReadQu
bool Server::Response::QueryNameMatches(const char *aName) const { return Server::QueryNameMatches(*mMessage, aName); }
Error Server::Response::AppendQueryName(void) { return Name::AppendPointerLabel(sizeof(Header), *mMessage); }
Error Server::Response::AppendQueryName(void) { return Name::AppendPointerLabel(kQueryNameOffset, *mMessage); }
#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
Error Server::Response::AppendPtrRecord(const Srp::Server::Service &aService)
@@ -514,6 +521,7 @@ Error Server::Response::AppendSrvRecord(const char *aHostName,
SrvRecord srvRecord;
uint16_t recordOffset;
Name::Buffer hostLabels;
uint16_t nameOffset;
SuccessOrExit(error = Name::ExtractLabels(aHostName, kDefaultDomainName, hostLabels));
@@ -523,7 +531,8 @@ Error Server::Response::AppendSrvRecord(const char *aHostName,
srvRecord.SetWeight(aWeight);
srvRecord.SetPort(aPort);
SuccessOrExit(error = Name::AppendPointerLabel(mOffsets.mInstanceName, *mMessage));
nameOffset = mQuestions.IsFor(kRrTypeAny) ? kQueryNameOffset : mOffsets.mInstanceName;
SuccessOrExit(error = Name::AppendPointerLabel(nameOffset, *mMessage));
recordOffset = mMessage->GetLength();
SuccessOrExit(error = mMessage->Append(srvRecord));
@@ -601,6 +610,7 @@ Error Server::Response::AppendAaaaRecord(const Ip6::Address &aAddress, uint32_t
{
Error error = kErrorNone;
AaaaRecord aaaaRecord;
uint16_t nameOffset;
VerifyOrExit(!aAddress.IsIp4Mapped());
@@ -608,7 +618,8 @@ Error Server::Response::AppendAaaaRecord(const Ip6::Address &aAddress, uint32_t
aaaaRecord.SetTtl(aTtl);
aaaaRecord.SetAddress(aAddress);
SuccessOrExit(error = Name::AppendPointerLabel(mOffsets.mHostName, *mMessage));
nameOffset = mQuestions.IsFor(kRrTypeAny) ? kQueryNameOffset : mOffsets.mHostName;
SuccessOrExit(error = Name::AppendPointerLabel(nameOffset, *mMessage));
SuccessOrExit(error = mMessage->Append(aaaaRecord));
IncResourceRecordCount();
@@ -621,6 +632,7 @@ Error Server::Response::AppendARecord(const Ip6::Address &aAddress, uint32_t aTt
Error error = kErrorNone;
ARecord aRecord;
Ip4::Address ip4Address;
uint16_t nameOffset;
SuccessOrExit(ip4Address.ExtractFromIp4MappedIp6Address(aAddress));
@@ -628,7 +640,8 @@ Error Server::Response::AppendARecord(const Ip6::Address &aAddress, uint32_t aTt
aRecord.SetTtl(aTtl);
aRecord.SetAddress(ip4Address);
SuccessOrExit(error = Name::AppendPointerLabel(mOffsets.mHostName, *mMessage));
nameOffset = mQuestions.IsFor(kRrTypeAny) ? kQueryNameOffset : mOffsets.mHostName;
SuccessOrExit(error = Name::AppendPointerLabel(nameOffset, *mMessage));
SuccessOrExit(error = mMessage->Append(aRecord));
IncResourceRecordCount();
@@ -653,6 +666,7 @@ Error Server::Response::AppendTxtRecord(const void *aTxtData, uint16_t aTxtLengt
{
Error error = kErrorNone;
TxtRecord txtRecord;
uint16_t nameOffset;
uint8_t emptyTxt = 0;
if (aTxtLength == 0)
@@ -665,7 +679,8 @@ Error Server::Response::AppendTxtRecord(const void *aTxtData, uint16_t aTxtLengt
txtRecord.SetTtl(aTtl);
txtRecord.SetLength(aTxtLength);
SuccessOrExit(error = Name::AppendPointerLabel(mOffsets.mInstanceName, *mMessage));
nameOffset = mQuestions.IsFor(kRrTypeAny) ? kQueryNameOffset : mOffsets.mInstanceName;
SuccessOrExit(error = Name::AppendPointerLabel(nameOffset, *mMessage));
SuccessOrExit(error = mMessage->Append(txtRecord));
SuccessOrExit(error = mMessage->AppendBytes(aTxtData, aTxtLength));
@@ -705,7 +720,7 @@ Error Server::Response::AppendGenericRecord(uint16_t aRrType, const RecordData &
record.Init(aRrType);
record.SetTtl(aTtl);
SuccessOrExit(error = Name::AppendPointerLabel(mOffsets.mHostName, *mMessage));
SuccessOrExit(error = Name::AppendPointerLabel(kQueryNameOffset, *mMessage));
recordOffset = mMessage->GetLength();
SuccessOrExit(error = mMessage->Append(record));
@@ -735,6 +750,7 @@ template <typename ServiceType> Error Server::Response::AppendServiceRecords(con
if (mSection == kAdditionalDataSection)
{
VerifyOrExit(!mQuestions.IsFor(kRrTypeAny));
VerifyOrExit(!(Get<Server>().mTestMode & kTestModeEmptyAdditionalSection));
}
@@ -788,7 +804,6 @@ Error Server::Response::ResolveBySrp(void)
{
Error error = kErrorNone;
const Srp::Server::Service *matchedService = nullptr;
bool found = false;
mSection = kAnswerSection;
@@ -801,18 +816,7 @@ Error Server::Response::ResolveBySrp(void)
if (QueryNameMatches(host.GetFullName()))
{
if (mQuestions.IsFor(kRrTypeAaaa) || mQuestions.IsFor(kRrTypeA))
{
mSection = mQuestions.SectionFor(kRrTypeAaaa);
SuccessOrExit(error = AppendHostAddresses(host));
}
if (mQuestions.IsFor(kRrTypeKey))
{
mSection = kAnswerSection;
SuccessOrExit(error = AppendKeyRecord(host));
}
error = ResolveUsingSrpHost(host);
ExitNow();
}
@@ -823,52 +827,74 @@ Error Server::Response::ResolveBySrp(void)
continue;
}
if (mQuestions.IsFor(kRrTypePtr))
if (QueryNameMatches(service.GetInstanceName()))
{
if (QueryNameMatchesService(service))
{
SuccessOrExit(error = AppendPtrRecord(service));
matchedService = &service;
}
error = ResolveUsingSrpService(service);
ExitNow();
}
else if (QueryNameMatches(service.GetInstanceName()))
{
matchedService = &service;
found = true;
break;
}
}
if (found)
{
break;
if ((mQuestions.IsFor(kRrTypePtr) || mQuestions.IsFor(kRrTypeAny)) && QueryNameMatchesService(service))
{
SuccessOrExit(error = AppendPtrRecord(service));
matchedService = &service;
}
}
}
VerifyOrExit(matchedService != nullptr, error = kErrorNotFound);
if (mQuestions.IsFor(kRrTypePtr))
{
// Skip adding additional records, when answering a
// PTR query with more than one answer. This is the
// recommended behavior to keep the size of the
// response small.
// We append SRV/TXT/AAAA records in additional section for a PTR
// query when there is only a single matched service. This is the
// recommended behavior to keep the size of the response small.
VerifyOrExit(mHeader.GetAnswerCount() == 1);
}
else
if (mQuestions.IsFor(kRrTypePtr) && (mHeader.GetAnswerCount() == 1))
{
if (mQuestions.IsFor(kRrTypeKey))
{
mSection = kAnswerSection;
error = AppendKeyRecord(matchedService->GetHost());
ExitNow();
}
VerifyOrExit(mQuestions.IsFor(kRrTypeSrv) || mQuestions.IsFor(kRrTypeTxt));
error = AppendServiceRecords(*matchedService);
}
error = AppendServiceRecords(*matchedService);
exit:
return error;
}
Error Server::Response::ResolveUsingSrpHost(const Srp::Server::Host &aHost)
{
// The query name is already checked to match the `aHost` name.
Error error = kErrorNone;
if (mQuestions.IsFor(kRrTypeAaaa) || mQuestions.IsFor(kRrTypeA) || mQuestions.IsFor(kRrTypeAny))
{
mSection = mQuestions.SectionFor(kRrTypeAaaa);
SuccessOrExit(error = AppendHostAddresses(aHost));
}
if (mQuestions.IsFor(kRrTypeKey) || mQuestions.IsFor(kRrTypeAny))
{
mSection = kAnswerSection;
SuccessOrExit(error = AppendKeyRecord(aHost));
}
exit:
return error;
}
Error Server::Response::ResolveUsingSrpService(const Srp::Server::Service &aService)
{
// The query name is already checked to match the
// `aService` instance name.
Error error = kErrorNone;
if (mQuestions.IsFor(kRrTypeKey) || mQuestions.IsFor(kRrTypeAny))
{
mSection = kAnswerSection;
SuccessOrExit(error = AppendKeyRecord(aService.GetHost()));
}
if (mQuestions.IsFor(kRrTypeSrv) || mQuestions.IsFor(kRrTypeTxt) || mQuestions.IsFor(kRrTypeAny))
{
SuccessOrExit(error = AppendServiceRecords(aService));
}
exit:
return error;
@@ -1573,6 +1599,7 @@ void Server::DiscoveryProxy::ReadNameFor(ProxyAction aAction,
case kNoAction:
break;
case kBrowsing:
case kQueryingRecord:
ReadQueryName(aQuery, aName);
break;
case kResolvingSrv:
@@ -1581,7 +1608,6 @@ void Server::DiscoveryProxy::ReadNameFor(ProxyAction aAction,
break;
case kResolvingIp6Address:
case kResolvingIp4Address:
case kQueryingRecord:
ReadQueryHostName(aQuery, aInfo, aName);
break;
}
@@ -1819,7 +1845,7 @@ void Server::DiscoveryProxy::StartOrStopRecordQuerier(Command aCom
Dnssd::RecordQuerier querier;
Name::LabelBuffer firstLabel;
Name::Buffer nextLabels;
uint16_t offset = aInfo.mOffsets.mHostName;
uint16_t offset = sizeof(Header);
uint8_t labelLength = sizeof(firstLabel);
IgnoreError(Dns::Name::ReadLabel(aQuery, offset, firstLabel, labelLength));
@@ -1847,12 +1873,14 @@ bool Server::DiscoveryProxy::QueryMatches(const ProxyQuery &aQuery,
const ProxyQueryInfo &aInfo,
ProxyAction aAction,
const Name::Buffer &aName,
uint16_t aQuerierRrType) const
uint16_t aQuerierRrType,
RrTypeMatchMode aRrTypeMatchMode) const
{
// Check whether `aQuery` is performing `aAction` and
// its name matches `aName`. The `aQuerierRrType` is only
// used when the action is `kQueryingRecord` to indicate
// which record is being queried.
// its name matches `aName`. The `aQuerierRrType` and
// `aRrTypeMatchMode` are only used when the action is
// `kQueryingRecord` to indicate queried record type and
// how to determine a match.
bool matches = false;
@@ -1860,12 +1888,22 @@ bool Server::DiscoveryProxy::QueryMatches(const ProxyQuery &aQuery,
if (aAction == kQueryingRecord)
{
VerifyOrExit(aInfo.mQuestions.IsFor(aQuerierRrType));
switch (aRrTypeMatchMode)
{
case kRequireExactMatch:
VerifyOrExit(aInfo.mQuestions.IsFor(aQuerierRrType));
break;
case kPermitAnyOrExactMatch:
VerifyOrExit(aInfo.mQuestions.IsFor(aQuerierRrType) || aInfo.mQuestions.IsFor(kRrTypeAny));
break;
}
}
switch (aAction)
{
case kBrowsing:
case kQueryingRecord:
VerifyOrExit(QueryNameMatches(aQuery, aName));
break;
case kResolvingSrv:
@@ -1874,7 +1912,6 @@ bool Server::DiscoveryProxy::QueryMatches(const ProxyQuery &aQuery,
break;
case kResolvingIp6Address:
case kResolvingIp4Address:
case kQueryingRecord:
VerifyOrExit(QueryHostNameMatches(aQuery, aInfo, aName));
break;
case kNoAction:
@@ -1902,7 +1939,7 @@ bool Server::DiscoveryProxy::HasActive(ProxyAction aAction, const Name::Buffer &
info.ReadFrom(query);
if (QueryMatches(query, info, aAction, aName, aQuerierRrType))
if (QueryMatches(query, info, aAction, aName, aQuerierRrType, kRequireExactMatch))
{
has = true;
break;
@@ -2105,7 +2142,7 @@ void Server::DiscoveryProxy::HandleResult(ProxyAction aAction,
info.ReadFrom(query);
if (!QueryMatches(query, info, aAction, aName, querierRrType))
if (!QueryMatches(query, info, aAction, aName, querierRrType, kPermitAnyOrExactMatch))
{
continue;
}
+15 -3
View File
@@ -319,6 +319,7 @@ private:
static constexpr uint16_t kRrTypeKey = ResourceRecord::kTypeKey;
static constexpr uint16_t kRrTypeAaaa = ResourceRecord::kTypeAaaa;
static constexpr uint16_t kRrTypeSrv = ResourceRecord::kTypeSrv;
static constexpr uint16_t kRrTypeAny = ResourceRecord::kTypeAny;
typedef Header::Response ResponseCode;
@@ -356,8 +357,8 @@ private:
{
Questions(void) { mFirstRrType = 0, mSecondRrType = 0; }
bool IsFor(uint16_t aRrType) const { return (mFirstRrType == aRrType) || (mSecondRrType == aRrType); }
Section SectionFor(uint16_t aRrType) const { return IsFor(aRrType) ? kAnswerSection : kAdditionalDataSection; }
bool IsFor(uint16_t aRrType) const;
Section SectionFor(uint16_t aRrType) const;
uint16_t mFirstRrType;
uint16_t mSecondRrType;
@@ -403,6 +404,8 @@ private:
class Response : public InstanceLocator, private NonCopyable
{
public:
static constexpr uint16_t kQueryNameOffset = sizeof(Header);
explicit Response(Instance &aInstance);
ResponseCode AddQuestionsFrom(const Request &aRequest);
@@ -435,6 +438,8 @@ private:
Error ExtractServiceInstanceLabel(const char *aInstanceName, Name::LabelBuffer &aLabel);
#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
Error ResolveBySrp(void);
Error ResolveUsingSrpHost(const Srp::Server::Host &aHost);
Error ResolveUsingSrpService(const Srp::Server::Service &aService);
bool QueryNameMatchesService(const Srp::Server::Service &aService) const;
Error AppendPtrRecord(const Srp::Server::Service &aService);
Error AppendSrvRecord(const Srp::Server::Service &aService);
@@ -497,6 +502,12 @@ private:
kStop,
};
enum RrTypeMatchMode : uint8_t
{
kRequireExactMatch, // Require record type to match exactly.
kPermitAnyOrExactMatch, // Permit ANY record type in addition to exact match.
};
typedef Error (Response::*ResponseAppender)(const ProxyResult &aResult);
void Perform(ProxyAction aAction, ProxyQuery &aQuery, ProxyQueryInfo &aInfo);
@@ -506,7 +517,8 @@ private:
const ProxyQueryInfo &aInfo,
ProxyAction aAction,
const Name::Buffer &aName,
uint16_t aQuerierRrType) const;
uint16_t aQuerierRrType,
RrTypeMatchMode aRrTypeMatchMode) const;
void UpdateProxy(Command aCommand,
ProxyAction aAction,
const ProxyQuery &aQuery,
+114 -1
View File
@@ -823,7 +823,7 @@ void TestDnsClient(void)
VerifyOrQuit(sAddressInfo.mError != kErrorNone);
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Validate DNS Client `QueryRecord()` for host name
// Validate DNS Client `QueryRecord()` for host name and KEY record
sQueryRecordInfo.Reset();
Log("QueryRecord(%s) for KEY RR", kHostFullName);
@@ -850,6 +850,43 @@ void TestDnsClient(void)
SuccessOrQuit(sQueryRecordInfo.mError);
VerifyOrQuit(sQueryRecordInfo.mNumRecords == 0);
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Validate DNS Client `QueryRecord()` for host name and ANY record
sQueryRecordInfo.Reset();
Log("QueryRecord(%s) for ANY RR", kHostFullName);
SuccessOrQuit(dnsClient->QueryRecord(Dns::ResourceRecord::kTypeAny, kHostName, "default.service.arpa.",
RecordCallback, sInstance));
AdvanceTime(100);
VerifyOrQuit(sQueryRecordInfo.mCallbackCount == 1);
SuccessOrQuit(sQueryRecordInfo.mError);
VerifyOrQuit(sQueryRecordInfo.mNumRecords == 3);
for (uint8_t index = 0; index < 3; index++)
{
const QueryRecordInfo::Record &record = sQueryRecordInfo.mRecords[index];
VerifyOrQuit(StringMatch(record.mNameBuffer, kHostFullName));
VerifyOrQuit(MapEnum(record.mSection) == Dns::Client::RecordInfo::kSectionAnswer);
VerifyOrQuit(record.mTtl > 0);
if (record.mRecordType == Dns::ResourceRecord::kTypeKey)
{
VerifyOrQuit(record.mRecordLength == sizeof(Dns::Ecdsa256KeyRecord));
VerifyOrQuit(record.mDataBufferSize == sizeof(Dns::Ecdsa256KeyRecord));
}
else if (record.mRecordType == Dns::ResourceRecord::kTypeAaaa)
{
VerifyOrQuit(record.mRecordLength == sizeof(Ip6::Address));
VerifyOrQuit(addresses.Contains(*reinterpret_cast<const Ip6::Address *>(record.mDataBuffer)));
}
else
{
// Unexpected record type.
VerifyOrQuit(false);
}
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Validate DNS Client `QueryRecord()` for service instance name and KEY record
@@ -925,6 +962,41 @@ void TestDnsClient(void)
}
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Validate DNS Client `QueryRecord()` for service instance name and ANY record
sQueryRecordInfo.Reset();
Log("QueryRecord(%s) for ANY record", kInstance1FullName);
SuccessOrQuit(dnsClient->QueryRecord(Dns::ResourceRecord::kTypeAny, kInstance1Label, kService1FullName,
RecordCallback, sInstance));
AdvanceTime(100);
VerifyOrQuit(sQueryRecordInfo.mCallbackCount == 1);
SuccessOrQuit(sQueryRecordInfo.mError);
VerifyOrQuit(sQueryRecordInfo.mNumRecords == 3);
for (uint8_t index = 1; index < 3; index++)
{
const QueryRecordInfo::Record &record = sQueryRecordInfo.mRecords[index];
VerifyOrQuit(StringMatch(record.mNameBuffer, kInstance1FullName));
VerifyOrQuit(record.mRecordLength > 0);
VerifyOrQuit(record.mTtl > 0);
VerifyOrQuit(record.mDataBufferSize == record.mRecordLength);
VerifyOrQuit(MapEnum(record.mSection) == Dns::Client::RecordInfo::kSectionAnswer);
switch (record.mRecordType)
{
case Dns::ResourceRecord::kTypeKey:
case Dns::ResourceRecord::kTypeTxt:
case Dns::ResourceRecord::kTypeSrv:
break;
default:
VerifyOrQuit(false);
break;
}
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Validate DNS Client `QueryRecord()` for PTR record
@@ -976,6 +1048,47 @@ void TestDnsClient(void)
}
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Validate DNS Client `QueryRecord()` for service name and ANY record
sQueryRecordInfo.Reset();
Log("QueryRecord(%s) for ANY record", kInstance1FullName);
SuccessOrQuit(dnsClient->QueryRecord(Dns::ResourceRecord::kTypeAny, "_srv", "_udp.default.service.arpa.",
RecordCallback, sInstance));
AdvanceTime(100);
VerifyOrQuit(sQueryRecordInfo.mCallbackCount == 1);
SuccessOrQuit(sQueryRecordInfo.mError);
VerifyOrQuit(sQueryRecordInfo.mNumRecords == 1);
VerifyOrQuit(StringMatch(sQueryRecordInfo.mRecords[0].mNameBuffer, kService1FullName));
VerifyOrQuit(sQueryRecordInfo.mRecords[0].mRecordType == Dns::ResourceRecord::kTypePtr);
VerifyOrQuit(sQueryRecordInfo.mRecords[0].mRecordLength > 0);
VerifyOrQuit(sQueryRecordInfo.mRecords[0].mTtl > 0);
VerifyOrQuit(sQueryRecordInfo.mRecords[0].mDataBufferSize == sQueryRecordInfo.mRecords[0].mRecordLength);
VerifyOrQuit(MapEnum(sQueryRecordInfo.mRecords[0].mSection) == Dns::Client::RecordInfo::kSectionAnswer);
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Validate DNS Client `QueryRecord()` for sub-type service name and ANY record
sQueryRecordInfo.Reset();
Log("QueryRecord(%s) for ANY record", kService2SubTypeFullName);
SuccessOrQuit(dnsClient->QueryRecord(Dns::ResourceRecord::kTypeAny, "_best",
"_sub._game._udp.default.service.arpa.", RecordCallback, sInstance));
AdvanceTime(100);
VerifyOrQuit(sQueryRecordInfo.mCallbackCount == 1);
SuccessOrQuit(sQueryRecordInfo.mError);
VerifyOrQuit(sQueryRecordInfo.mNumRecords == 1);
VerifyOrQuit(StringMatch(sQueryRecordInfo.mRecords[0].mNameBuffer, kService2SubTypeFullName));
VerifyOrQuit(sQueryRecordInfo.mRecords[0].mRecordType == Dns::ResourceRecord::kTypePtr);
VerifyOrQuit(sQueryRecordInfo.mRecords[0].mRecordLength > 0);
VerifyOrQuit(sQueryRecordInfo.mRecords[0].mTtl > 0);
VerifyOrQuit(sQueryRecordInfo.mRecords[0].mDataBufferSize == sQueryRecordInfo.mRecords[0].mRecordLength);
VerifyOrQuit(MapEnum(sQueryRecordInfo.mRecords[0].mSection) == Dns::Client::RecordInfo::kSectionAnswer);
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Validate DNS Client `Browse()`
+166 -3
View File
@@ -687,7 +687,8 @@ struct RecordQuerierInfo : public Clearable<RecordQuerierInfo>
mCallCount++;
CopyString(mFirstLabel, aQuerier->mFirstLabel);
CopyString(mNextLabels, aQuerier->mNextLabels);
mCallback = aQuerier->mCallback;
mRecordType = aQuerier->mRecordType;
mCallback = aQuerier->mCallback;
}
uint16_t mCallCount;
@@ -1621,7 +1622,7 @@ void TestProxyBasic(void)
ResetPlatDnssdApiInfo();
sQueryRecordInfo.Reset();
Log("QueryRecord()");
Log("QueryRecord() for KEY and hostname");
SuccessOrQuit(dnsClient->QueryRecord(Dns::ResourceRecord::kTypeKey, "shield", "default.service.arpa.",
RecordCallback, sInstance));
AdvanceTime(10);
@@ -1642,6 +1643,7 @@ void TestProxyBasic(void)
VerifyOrQuit(sStopRecordQuerierInfo.mCallCount == 0);
VerifyOrQuit(sStartRecordQuerierInfo.NameMatches("shield", nullptr));
VerifyOrQuit(sStartRecordQuerierInfo.mRecordType == Dns::ResourceRecord::kTypeKey);
VerifyOrQuit(sQueryRecordInfo.mCallbackCount == 0);
@@ -1676,6 +1678,7 @@ void TestProxyBasic(void)
VerifyOrQuit(sStopRecordQuerierInfo.mCallCount == 1);
VerifyOrQuit(sStopRecordQuerierInfo.NameMatches("shield", nullptr));
VerifyOrQuit(sStopRecordQuerierInfo.mRecordType == Dns::ResourceRecord::kTypeKey);
VerifyOrQuit(sStopRecordQuerierInfo.mCallback == sStartRecordQuerierInfo.mCallback);
// Check that response is sent to client and validate it
@@ -1699,7 +1702,7 @@ void TestProxyBasic(void)
ResetPlatDnssdApiInfo();
sQueryRecordInfo.Reset();
Log("QueryRecord()");
Log("QueryRecord() for KEY and service instance name");
SuccessOrQuit(dnsClient->QueryRecord(Dns::ResourceRecord::kTypeKey, "iron.man",
"_avenger._udp.default.service.arpa.", RecordCallback, sInstance));
AdvanceTime(10);
@@ -1720,6 +1723,7 @@ void TestProxyBasic(void)
VerifyOrQuit(sStopRecordQuerierInfo.mCallCount == 0);
VerifyOrQuit(sStartRecordQuerierInfo.NameMatches("iron.man", "_avenger._udp"));
VerifyOrQuit(sStartRecordQuerierInfo.mRecordType == Dns::ResourceRecord::kTypeKey);
VerifyOrQuit(sQueryRecordInfo.mCallbackCount == 0);
@@ -1755,6 +1759,7 @@ void TestProxyBasic(void)
VerifyOrQuit(sStopRecordQuerierInfo.NameMatches("iron.man", "_avenger._udp"));
VerifyOrQuit(sStopRecordQuerierInfo.mCallback == sStartRecordQuerierInfo.mCallback);
VerifyOrQuit(sStopRecordQuerierInfo.mRecordType == Dns::ResourceRecord::kTypeKey);
// Check that response is sent to client and validate it
@@ -1851,6 +1856,164 @@ void TestProxyBasic(void)
VerifyOrQuit(!memcmp(sQueryRecordInfo.mRecords[0].mDataBuffer, kTranslatedCnameData, sizeof(kTranslatedCnameData)));
VerifyOrQuit(MapEnum(sQueryRecordInfo.mRecords[0].mSection) == Dns::Client::RecordInfo::kSectionAnswer);
Log("--------------------------------------------------------------------------------------------");
ResetPlatDnssdApiInfo();
sQueryRecordInfo.Reset();
Log("QueryRecord() for ANY and host name");
SuccessOrQuit(dnsClient->QueryRecord(Dns::ResourceRecord::kTypeAny, "shield", "default.service.arpa.",
RecordCallback, sInstance));
AdvanceTime(10);
// Check that a record querier is started
VerifyOrQuit(sStartBrowserInfo.mCallCount == 0);
VerifyOrQuit(sStopBrowserInfo.mCallCount == 0);
VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 0);
VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 0);
VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 0);
VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0);
VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 0);
VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0);
VerifyOrQuit(sStartIp4AddrResolverInfo.mCallCount == 0);
VerifyOrQuit(sStopIp4AddrResolverInfo.mCallCount == 0);
VerifyOrQuit(sStartRecordQuerierInfo.mCallCount == 1);
VerifyOrQuit(sStopRecordQuerierInfo.mCallCount == 0);
VerifyOrQuit(sStartRecordQuerierInfo.NameMatches("shield", nullptr));
VerifyOrQuit(sStartRecordQuerierInfo.mRecordType == Dns::ResourceRecord::kTypeAny);
VerifyOrQuit(sQueryRecordInfo.mCallbackCount == 0);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Invoke Record Querier callback");
recordResult.mFirstLabel = "shield";
recordResult.mNextLabels = nullptr;
recordResult.mRecordType = Dns::ResourceRecord::kTypeKey;
recordResult.mRecordData = kKeyData;
recordResult.mRecordDataLength = sizeof(kKeyData);
recordResult.mTtl = kTtl;
recordResult.mInfraIfIndex = kInfraIfIndex;
InvokeRecordQuerierCallback(sStartRecordQuerierInfo.mCallback, recordResult);
AdvanceTime(10);
// Check that the record querier is stopped
VerifyOrQuit(sStartBrowserInfo.mCallCount == 0);
VerifyOrQuit(sStopBrowserInfo.mCallCount == 0);
VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 0);
VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 0);
VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 0);
VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0);
VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 0);
VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0);
VerifyOrQuit(sStartIp4AddrResolverInfo.mCallCount == 0);
VerifyOrQuit(sStopIp4AddrResolverInfo.mCallCount == 0);
VerifyOrQuit(sStartRecordQuerierInfo.mCallCount == 1);
VerifyOrQuit(sStopRecordQuerierInfo.mCallCount == 1);
VerifyOrQuit(sStopRecordQuerierInfo.NameMatches("shield", nullptr));
VerifyOrQuit(sStopRecordQuerierInfo.mCallback == sStartRecordQuerierInfo.mCallback);
// Check that response is sent to client and validate it
VerifyOrQuit(sQueryRecordInfo.mCallbackCount == 1);
SuccessOrQuit(sQueryRecordInfo.mError);
VerifyOrQuit(!strcmp(sQueryRecordInfo.mQueryName, "shield.default.service.arpa."));
VerifyOrQuit(sQueryRecordInfo.mNumRecords == 1);
VerifyOrQuit(!strcmp(sQueryRecordInfo.mRecords[0].mNameBuffer, "shield.default.service.arpa."));
VerifyOrQuit(sQueryRecordInfo.mRecords[0].mRecordType == Dns::ResourceRecord::kTypeKey);
VerifyOrQuit(sQueryRecordInfo.mRecords[0].mRecordLength == sizeof(kKeyData));
VerifyOrQuit(sQueryRecordInfo.mRecords[0].mTtl == kTtl);
VerifyOrQuit(sQueryRecordInfo.mRecords[0].mDataBufferSize == sizeof(kKeyData));
VerifyOrQuit(!memcmp(sQueryRecordInfo.mRecords[0].mDataBuffer, kKeyData, sizeof(kKeyData)));
VerifyOrQuit(MapEnum(sQueryRecordInfo.mRecords[0].mSection) == Dns::Client::RecordInfo::kSectionAnswer);
Log("--------------------------------------------------------------------------------------------");
ResetPlatDnssdApiInfo();
sQueryRecordInfo.Reset();
Log("QueryRecord() for ANY and service instance name");
SuccessOrQuit(dnsClient->QueryRecord(Dns::ResourceRecord::kTypeAny, "iron.man",
"_avenger._udp.default.service.arpa.", RecordCallback, sInstance));
AdvanceTime(10);
// Check that a record querier is started
VerifyOrQuit(sStartBrowserInfo.mCallCount == 0);
VerifyOrQuit(sStopBrowserInfo.mCallCount == 0);
VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 0);
VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 0);
VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 0);
VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0);
VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 0);
VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0);
VerifyOrQuit(sStartIp4AddrResolverInfo.mCallCount == 0);
VerifyOrQuit(sStopIp4AddrResolverInfo.mCallCount == 0);
VerifyOrQuit(sStartRecordQuerierInfo.mCallCount == 1);
VerifyOrQuit(sStopRecordQuerierInfo.mCallCount == 0);
VerifyOrQuit(sStartRecordQuerierInfo.NameMatches("iron.man", "_avenger._udp"));
VerifyOrQuit(sStartRecordQuerierInfo.mRecordType == Dns::ResourceRecord::kTypeAny);
VerifyOrQuit(sQueryRecordInfo.mCallbackCount == 0);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Invoke Record Querier callback");
recordResult.mFirstLabel = "iron.man";
recordResult.mNextLabels = "_avenger._udp";
recordResult.mRecordType = Dns::ResourceRecord::kTypeKey;
recordResult.mRecordData = kKeyData;
recordResult.mRecordDataLength = sizeof(kKeyData);
recordResult.mTtl = kTtl;
recordResult.mInfraIfIndex = kInfraIfIndex;
InvokeRecordQuerierCallback(sStartRecordQuerierInfo.mCallback, recordResult);
AdvanceTime(10);
// Check that the record querier is stopped
VerifyOrQuit(sStartBrowserInfo.mCallCount == 0);
VerifyOrQuit(sStopBrowserInfo.mCallCount == 0);
VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 0);
VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 0);
VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 0);
VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0);
VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 0);
VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0);
VerifyOrQuit(sStartIp4AddrResolverInfo.mCallCount == 0);
VerifyOrQuit(sStopIp4AddrResolverInfo.mCallCount == 0);
VerifyOrQuit(sStartRecordQuerierInfo.mCallCount == 1);
VerifyOrQuit(sStopRecordQuerierInfo.mCallCount == 1);
VerifyOrQuit(sStopRecordQuerierInfo.NameMatches("iron.man", "_avenger._udp"));
VerifyOrQuit(sStopRecordQuerierInfo.mCallback == sStartRecordQuerierInfo.mCallback);
// Check that response is sent to client and validate it
VerifyOrQuit(sQueryRecordInfo.mCallbackCount == 1);
SuccessOrQuit(sQueryRecordInfo.mError);
VerifyOrQuit(!strcmp(sQueryRecordInfo.mQueryName, "iron.man._avenger._udp.default.service.arpa."));
VerifyOrQuit(sQueryRecordInfo.mNumRecords == 1);
VerifyOrQuit(!strcmp(sQueryRecordInfo.mRecords[0].mNameBuffer, "iron.man._avenger._udp.default.service.arpa."));
VerifyOrQuit(sQueryRecordInfo.mRecords[0].mRecordType == Dns::ResourceRecord::kTypeKey);
VerifyOrQuit(sQueryRecordInfo.mRecords[0].mRecordLength == sizeof(kKeyData));
VerifyOrQuit(sQueryRecordInfo.mRecords[0].mTtl == kTtl);
VerifyOrQuit(sQueryRecordInfo.mRecords[0].mDataBufferSize == sizeof(kKeyData));
VerifyOrQuit(!memcmp(sQueryRecordInfo.mRecords[0].mDataBuffer, kKeyData, sizeof(kKeyData)));
VerifyOrQuit(MapEnum(sQueryRecordInfo.mRecords[0].mSection) == Dns::Client::RecordInfo::kSectionAnswer);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Stop DNS-SD server");