Skip to content

Commit b3c4a7e

Browse files
authored
[dnssd] support ANY record type queries (openthread#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.
1 parent 6b5493c commit b3c4a7e

4 files changed

Lines changed: 407 additions & 82 deletions

File tree

src/core/net/dnssd_server.cpp

Lines changed: 112 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,21 @@ void Server::Response::Send(const Ip6::MessageInfo &aMessageInfo)
290290
return;
291291
}
292292

293+
bool Server::Questions::IsFor(uint16_t aRrType) const
294+
{
295+
// Check if any of questions is for `aRrType`.
296+
297+
return (mFirstRrType == aRrType) || (mSecondRrType == aRrType);
298+
}
299+
300+
Server::Section Server::Questions::SectionFor(uint16_t aRrType) const
301+
{
302+
// Determine section to append `aRrType` record based on the
303+
// query questions.
304+
305+
return (IsFor(aRrType) || IsFor(kRrTypeAny)) ? kAnswerSection : kAdditionalDataSection;
306+
}
307+
293308
Server::ResponseCode Server::Request::ParseQuestions(uint8_t aTestMode, bool &aShouldRespond)
294309
{
295310
// Parse header and questions from a `Request` query message and
@@ -349,7 +364,7 @@ Server::ResponseCode Server::Response::AddQuestionsFrom(const Request &aRequest)
349364
// service instance names with dot characters in the instance
350365
// label, are appended correctly.
351366

352-
SuccessOrExit(Name(*aRequest.mMessage, sizeof(Header)).AppendTo(*mMessage));
367+
SuccessOrExit(Name(*aRequest.mMessage, kQueryNameOffset).AppendTo(*mMessage));
353368

354369
// Check the name to include the correct domain name and determine
355370
// the domain name offset (for DNS name compression).
@@ -398,26 +413,17 @@ Error Server::Response::ParseQueryName(void)
398413
offset = sizeof(Header);
399414
SuccessOrExit(error = Name::ReadName(*mMessage, offset, name));
400415

401-
if (mQuestions.IsFor(kRrTypePtr))
402-
{
403-
// `mOffsets.mServiceName` may be updated as we read labels and if we
404-
// determine that the query name is a sub-type service.
405-
mOffsets.mServiceName = sizeof(Header);
406-
}
407-
else if (mQuestions.IsFor(kRrTypeSrv) || mQuestions.IsFor(kRrTypeTxt))
408-
{
409-
mOffsets.mInstanceName = sizeof(Header);
410-
}
411-
else
412-
{
413-
mOffsets.mHostName = sizeof(Header);
414-
}
416+
// `mOffsets.mServiceName` may be updated as we read labels and if we
417+
// determine that the query name is a sub-type service.
418+
mOffsets.mServiceName = kQueryNameOffset;
419+
mOffsets.mInstanceName = kQueryNameOffset;
420+
mOffsets.mHostName = kQueryNameOffset;
415421

416422
// Read the query name labels one by one to check if the name is
417423
// service sub-type and also check that it is sub-domain of the
418424
// default domain name and determine its offset
419425

420-
offset = sizeof(Header);
426+
offset = kQueryNameOffset;
421427

422428
while (true)
423429
{
@@ -427,7 +433,8 @@ Error Server::Response::ParseQueryName(void)
427433

428434
SuccessOrExit(error = Name::ReadLabel(*mMessage, offset, label, labelLength));
429435

430-
if (mQuestions.IsFor(kRrTypePtr) && StringMatch(label, kSubLabel, kStringCaseInsensitiveMatch))
436+
if ((mQuestions.IsFor(kRrTypePtr) || mQuestions.IsFor(kRrTypeAny)) &&
437+
StringMatch(label, kSubLabel, kStringCaseInsensitiveMatch))
431438
{
432439
mOffsets.mServiceName = offset;
433440
}
@@ -451,7 +458,7 @@ void Server::Response::ReadQueryName(Name::Buffer &aName) const { Server::ReadQu
451458

452459
bool Server::Response::QueryNameMatches(const char *aName) const { return Server::QueryNameMatches(*mMessage, aName); }
453460

454-
Error Server::Response::AppendQueryName(void) { return Name::AppendPointerLabel(sizeof(Header), *mMessage); }
461+
Error Server::Response::AppendQueryName(void) { return Name::AppendPointerLabel(kQueryNameOffset, *mMessage); }
455462

456463
#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
457464
Error Server::Response::AppendPtrRecord(const Srp::Server::Service &aService)
@@ -514,6 +521,7 @@ Error Server::Response::AppendSrvRecord(const char *aHostName,
514521
SrvRecord srvRecord;
515522
uint16_t recordOffset;
516523
Name::Buffer hostLabels;
524+
uint16_t nameOffset;
517525

518526
SuccessOrExit(error = Name::ExtractLabels(aHostName, kDefaultDomainName, hostLabels));
519527

@@ -523,7 +531,8 @@ Error Server::Response::AppendSrvRecord(const char *aHostName,
523531
srvRecord.SetWeight(aWeight);
524532
srvRecord.SetPort(aPort);
525533

526-
SuccessOrExit(error = Name::AppendPointerLabel(mOffsets.mInstanceName, *mMessage));
534+
nameOffset = mQuestions.IsFor(kRrTypeAny) ? kQueryNameOffset : mOffsets.mInstanceName;
535+
SuccessOrExit(error = Name::AppendPointerLabel(nameOffset, *mMessage));
527536

528537
recordOffset = mMessage->GetLength();
529538
SuccessOrExit(error = mMessage->Append(srvRecord));
@@ -601,14 +610,16 @@ Error Server::Response::AppendAaaaRecord(const Ip6::Address &aAddress, uint32_t
601610
{
602611
Error error = kErrorNone;
603612
AaaaRecord aaaaRecord;
613+
uint16_t nameOffset;
604614

605615
VerifyOrExit(!aAddress.IsIp4Mapped());
606616

607617
aaaaRecord.Init();
608618
aaaaRecord.SetTtl(aTtl);
609619
aaaaRecord.SetAddress(aAddress);
610620

611-
SuccessOrExit(error = Name::AppendPointerLabel(mOffsets.mHostName, *mMessage));
621+
nameOffset = mQuestions.IsFor(kRrTypeAny) ? kQueryNameOffset : mOffsets.mHostName;
622+
SuccessOrExit(error = Name::AppendPointerLabel(nameOffset, *mMessage));
612623
SuccessOrExit(error = mMessage->Append(aaaaRecord));
613624
IncResourceRecordCount();
614625

@@ -621,14 +632,16 @@ Error Server::Response::AppendARecord(const Ip6::Address &aAddress, uint32_t aTt
621632
Error error = kErrorNone;
622633
ARecord aRecord;
623634
Ip4::Address ip4Address;
635+
uint16_t nameOffset;
624636

625637
SuccessOrExit(ip4Address.ExtractFromIp4MappedIp6Address(aAddress));
626638

627639
aRecord.Init();
628640
aRecord.SetTtl(aTtl);
629641
aRecord.SetAddress(ip4Address);
630642

631-
SuccessOrExit(error = Name::AppendPointerLabel(mOffsets.mHostName, *mMessage));
643+
nameOffset = mQuestions.IsFor(kRrTypeAny) ? kQueryNameOffset : mOffsets.mHostName;
644+
SuccessOrExit(error = Name::AppendPointerLabel(nameOffset, *mMessage));
632645
SuccessOrExit(error = mMessage->Append(aRecord));
633646
IncResourceRecordCount();
634647

@@ -653,6 +666,7 @@ Error Server::Response::AppendTxtRecord(const void *aTxtData, uint16_t aTxtLengt
653666
{
654667
Error error = kErrorNone;
655668
TxtRecord txtRecord;
669+
uint16_t nameOffset;
656670
uint8_t emptyTxt = 0;
657671

658672
if (aTxtLength == 0)
@@ -665,7 +679,8 @@ Error Server::Response::AppendTxtRecord(const void *aTxtData, uint16_t aTxtLengt
665679
txtRecord.SetTtl(aTtl);
666680
txtRecord.SetLength(aTxtLength);
667681

668-
SuccessOrExit(error = Name::AppendPointerLabel(mOffsets.mInstanceName, *mMessage));
682+
nameOffset = mQuestions.IsFor(kRrTypeAny) ? kQueryNameOffset : mOffsets.mInstanceName;
683+
SuccessOrExit(error = Name::AppendPointerLabel(nameOffset, *mMessage));
669684
SuccessOrExit(error = mMessage->Append(txtRecord));
670685
SuccessOrExit(error = mMessage->AppendBytes(aTxtData, aTxtLength));
671686

@@ -705,7 +720,7 @@ Error Server::Response::AppendGenericRecord(uint16_t aRrType, const RecordData &
705720
record.Init(aRrType);
706721
record.SetTtl(aTtl);
707722

708-
SuccessOrExit(error = Name::AppendPointerLabel(mOffsets.mHostName, *mMessage));
723+
SuccessOrExit(error = Name::AppendPointerLabel(kQueryNameOffset, *mMessage));
709724

710725
recordOffset = mMessage->GetLength();
711726
SuccessOrExit(error = mMessage->Append(record));
@@ -735,6 +750,7 @@ template <typename ServiceType> Error Server::Response::AppendServiceRecords(con
735750

736751
if (mSection == kAdditionalDataSection)
737752
{
753+
VerifyOrExit(!mQuestions.IsFor(kRrTypeAny));
738754
VerifyOrExit(!(Get<Server>().mTestMode & kTestModeEmptyAdditionalSection));
739755
}
740756

@@ -788,7 +804,6 @@ Error Server::Response::ResolveBySrp(void)
788804
{
789805
Error error = kErrorNone;
790806
const Srp::Server::Service *matchedService = nullptr;
791-
bool found = false;
792807

793808
mSection = kAnswerSection;
794809

@@ -801,18 +816,7 @@ Error Server::Response::ResolveBySrp(void)
801816

802817
if (QueryNameMatches(host.GetFullName()))
803818
{
804-
if (mQuestions.IsFor(kRrTypeAaaa) || mQuestions.IsFor(kRrTypeA))
805-
{
806-
mSection = mQuestions.SectionFor(kRrTypeAaaa);
807-
SuccessOrExit(error = AppendHostAddresses(host));
808-
}
809-
810-
if (mQuestions.IsFor(kRrTypeKey))
811-
{
812-
mSection = kAnswerSection;
813-
SuccessOrExit(error = AppendKeyRecord(host));
814-
}
815-
819+
error = ResolveUsingSrpHost(host);
816820
ExitNow();
817821
}
818822

@@ -823,52 +827,74 @@ Error Server::Response::ResolveBySrp(void)
823827
continue;
824828
}
825829

826-
if (mQuestions.IsFor(kRrTypePtr))
830+
if (QueryNameMatches(service.GetInstanceName()))
827831
{
828-
if (QueryNameMatchesService(service))
829-
{
830-
SuccessOrExit(error = AppendPtrRecord(service));
831-
matchedService = &service;
832-
}
832+
error = ResolveUsingSrpService(service);
833+
ExitNow();
833834
}
834-
else if (QueryNameMatches(service.GetInstanceName()))
835+
836+
if ((mQuestions.IsFor(kRrTypePtr) || mQuestions.IsFor(kRrTypeAny)) && QueryNameMatchesService(service))
835837
{
838+
SuccessOrExit(error = AppendPtrRecord(service));
836839
matchedService = &service;
837-
found = true;
838-
break;
839840
}
840841
}
841-
842-
if (found)
843-
{
844-
break;
845-
}
846842
}
847843

848844
VerifyOrExit(matchedService != nullptr, error = kErrorNotFound);
849845

850-
if (mQuestions.IsFor(kRrTypePtr))
846+
// We append SRV/TXT/AAAA records in additional section for a PTR
847+
// query when there is only a single matched service. This is the
848+
// recommended behavior to keep the size of the response small.
849+
850+
if (mQuestions.IsFor(kRrTypePtr) && (mHeader.GetAnswerCount() == 1))
851851
{
852-
// Skip adding additional records, when answering a
853-
// PTR query with more than one answer. This is the
854-
// recommended behavior to keep the size of the
855-
// response small.
852+
error = AppendServiceRecords(*matchedService);
853+
}
854+
855+
exit:
856+
return error;
857+
}
858+
859+
Error Server::Response::ResolveUsingSrpHost(const Srp::Server::Host &aHost)
860+
{
861+
// The query name is already checked to match the `aHost` name.
862+
863+
Error error = kErrorNone;
856864

857-
VerifyOrExit(mHeader.GetAnswerCount() == 1);
865+
if (mQuestions.IsFor(kRrTypeAaaa) || mQuestions.IsFor(kRrTypeA) || mQuestions.IsFor(kRrTypeAny))
866+
{
867+
mSection = mQuestions.SectionFor(kRrTypeAaaa);
868+
SuccessOrExit(error = AppendHostAddresses(aHost));
858869
}
859-
else
870+
871+
if (mQuestions.IsFor(kRrTypeKey) || mQuestions.IsFor(kRrTypeAny))
860872
{
861-
if (mQuestions.IsFor(kRrTypeKey))
862-
{
863-
mSection = kAnswerSection;
864-
error = AppendKeyRecord(matchedService->GetHost());
865-
ExitNow();
866-
}
873+
mSection = kAnswerSection;
874+
SuccessOrExit(error = AppendKeyRecord(aHost));
875+
}
876+
877+
exit:
878+
return error;
879+
}
867880

868-
VerifyOrExit(mQuestions.IsFor(kRrTypeSrv) || mQuestions.IsFor(kRrTypeTxt));
881+
Error Server::Response::ResolveUsingSrpService(const Srp::Server::Service &aService)
882+
{
883+
// The query name is already checked to match the
884+
// `aService` instance name.
885+
886+
Error error = kErrorNone;
887+
888+
if (mQuestions.IsFor(kRrTypeKey) || mQuestions.IsFor(kRrTypeAny))
889+
{
890+
mSection = kAnswerSection;
891+
SuccessOrExit(error = AppendKeyRecord(aService.GetHost()));
869892
}
870893

871-
error = AppendServiceRecords(*matchedService);
894+
if (mQuestions.IsFor(kRrTypeSrv) || mQuestions.IsFor(kRrTypeTxt) || mQuestions.IsFor(kRrTypeAny))
895+
{
896+
SuccessOrExit(error = AppendServiceRecords(aService));
897+
}
872898

873899
exit:
874900
return error;
@@ -1573,6 +1599,7 @@ void Server::DiscoveryProxy::ReadNameFor(ProxyAction aAction,
15731599
case kNoAction:
15741600
break;
15751601
case kBrowsing:
1602+
case kQueryingRecord:
15761603
ReadQueryName(aQuery, aName);
15771604
break;
15781605
case kResolvingSrv:
@@ -1581,7 +1608,6 @@ void Server::DiscoveryProxy::ReadNameFor(ProxyAction aAction,
15811608
break;
15821609
case kResolvingIp6Address:
15831610
case kResolvingIp4Address:
1584-
case kQueryingRecord:
15851611
ReadQueryHostName(aQuery, aInfo, aName);
15861612
break;
15871613
}
@@ -1819,7 +1845,7 @@ void Server::DiscoveryProxy::StartOrStopRecordQuerier(Command aCom
18191845
Dnssd::RecordQuerier querier;
18201846
Name::LabelBuffer firstLabel;
18211847
Name::Buffer nextLabels;
1822-
uint16_t offset = aInfo.mOffsets.mHostName;
1848+
uint16_t offset = sizeof(Header);
18231849
uint8_t labelLength = sizeof(firstLabel);
18241850

18251851
IgnoreError(Dns::Name::ReadLabel(aQuery, offset, firstLabel, labelLength));
@@ -1847,25 +1873,37 @@ bool Server::DiscoveryProxy::QueryMatches(const ProxyQuery &aQuery,
18471873
const ProxyQueryInfo &aInfo,
18481874
ProxyAction aAction,
18491875
const Name::Buffer &aName,
1850-
uint16_t aQuerierRrType) const
1876+
uint16_t aQuerierRrType,
1877+
RrTypeMatchMode aRrTypeMatchMode) const
18511878
{
18521879
// Check whether `aQuery` is performing `aAction` and
1853-
// its name matches `aName`. The `aQuerierRrType` is only
1854-
// used when the action is `kQueryingRecord` to indicate
1855-
// which record is being queried.
1880+
// its name matches `aName`. The `aQuerierRrType` and
1881+
// `aRrTypeMatchMode` are only used when the action is
1882+
// `kQueryingRecord` to indicate queried record type and
1883+
// how to determine a match.
18561884

18571885
bool matches = false;
18581886

18591887
VerifyOrExit(aInfo.mAction == aAction);
18601888

18611889
if (aAction == kQueryingRecord)
18621890
{
1863-
VerifyOrExit(aInfo.mQuestions.IsFor(aQuerierRrType));
1891+
switch (aRrTypeMatchMode)
1892+
{
1893+
case kRequireExactMatch:
1894+
VerifyOrExit(aInfo.mQuestions.IsFor(aQuerierRrType));
1895+
break;
1896+
1897+
case kPermitAnyOrExactMatch:
1898+
VerifyOrExit(aInfo.mQuestions.IsFor(aQuerierRrType) || aInfo.mQuestions.IsFor(kRrTypeAny));
1899+
break;
1900+
}
18641901
}
18651902

18661903
switch (aAction)
18671904
{
18681905
case kBrowsing:
1906+
case kQueryingRecord:
18691907
VerifyOrExit(QueryNameMatches(aQuery, aName));
18701908
break;
18711909
case kResolvingSrv:
@@ -1874,7 +1912,6 @@ bool Server::DiscoveryProxy::QueryMatches(const ProxyQuery &aQuery,
18741912
break;
18751913
case kResolvingIp6Address:
18761914
case kResolvingIp4Address:
1877-
case kQueryingRecord:
18781915
VerifyOrExit(QueryHostNameMatches(aQuery, aInfo, aName));
18791916
break;
18801917
case kNoAction:
@@ -1902,7 +1939,7 @@ bool Server::DiscoveryProxy::HasActive(ProxyAction aAction, const Name::Buffer &
19021939

19031940
info.ReadFrom(query);
19041941

1905-
if (QueryMatches(query, info, aAction, aName, aQuerierRrType))
1942+
if (QueryMatches(query, info, aAction, aName, aQuerierRrType, kRequireExactMatch))
19061943
{
19071944
has = true;
19081945
break;
@@ -2105,7 +2142,7 @@ void Server::DiscoveryProxy::HandleResult(ProxyAction aAction,
21052142

21062143
info.ReadFrom(query);
21072144

2108-
if (!QueryMatches(query, info, aAction, aName, querierRrType))
2145+
if (!QueryMatches(query, info, aAction, aName, querierRrType, kPermitAnyOrExactMatch))
21092146
{
21102147
continue;
21112148
}

0 commit comments

Comments
 (0)