[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
+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");