diff --git a/src/core/net/dns_types.cpp b/src/core/net/dns_types.cpp index c9c2cdebd..34412e3b2 100644 --- a/src/core/net/dns_types.cpp +++ b/src/core/net/dns_types.cpp @@ -1592,6 +1592,10 @@ Error PtrRecord::ReadPtrName(const Message &aMessage, aOffset = startOffset + sizeof(PtrRecord); SuccessOrExit(error = Name::ReadLabel(aMessage, aOffset, aLabelBuffer, aLabelBufferSize)); + // The first label of a PTR target (the service-instance or host label) + // must be non-empty. + VerifyOrExit(aLabelBuffer[0] != kNullChar, error = kErrorParse); + if (aNameBuffer != nullptr) { SuccessOrExit(error = Name::ReadName(aMessage, aOffset, aNameBuffer, aNameBufferSize)); diff --git a/tests/unit/test_mdns.cpp b/tests/unit/test_mdns.cpp index ac916b7eb..45100f17d 100644 --- a/tests/unit/test_mdns.cpp +++ b/tests/unit/test_mdns.cpp @@ -1331,6 +1331,53 @@ static void SendPtrResponse(const char *aName, const char *aPtrName, uint32_t aT otPlatMdnsHandleReceive(sInstance, message, /* aIsUnicast */ false, &senderAddrInfo); } +static void SendPtrResponseWithEmptyInstanceLabel(const char *aName, uint32_t aTtl) +{ + // Sends a PTR response whose target name starts with a single + // NUL-byte (empty) label, i.e. the wire label `01 00` followed by + // `aName`. The RDLENGTH is computed from the appended target bytes. + + static const uint8_t kEmptyLabel[] = {1, 0}; + + Message *message; + Header header; + PtrRecord ptr; + Core::AddressInfo senderAddrInfo; + uint16_t ptrOffset; + uint16_t rdataOffset; + + message = sInstance->Get().Allocate(Message::kTypeOther); + VerifyOrQuit(message != nullptr); + + header.Clear(); + header.SetType(Header::kTypeResponse); + header.SetAnswerCount(1); + + SuccessOrQuit(message->Append(header)); + SuccessOrQuit(Name::AppendName(aName, *message)); + + ptr.Init(); + ptr.SetTtl(aTtl); + ptr.SetLength(0); + ptrOffset = message->GetLength(); + SuccessOrQuit(message->Append(ptr)); + + rdataOffset = message->GetLength(); + SuccessOrQuit(message->AppendBytes(kEmptyLabel, sizeof(kEmptyLabel))); + SuccessOrQuit(Name::AppendName(aName, *message)); + + ptr.SetLength(message->GetLength() - rdataOffset); + message->WriteBytes(ptrOffset, &ptr, sizeof(ptr)); + + SuccessOrQuit(AsCoreType(&senderAddrInfo.mAddress).FromString(kDeviceIp6Address)); + senderAddrInfo.mPort = kMdnsPort; + senderAddrInfo.mInfraIfIndex = 0; + + Log("Sending PTR response for %s with empty instance label, ttl:%lu", aName, ToUlong(aTtl)); + + otPlatMdnsHandleReceive(sInstance, message, /* aIsUnicast */ false, &senderAddrInfo); +} + static void SendSrvResponse(const char *aServiceName, const char *aHostName, uint16_t aPort, @@ -6223,6 +6270,69 @@ void TestBrowser(void) testFreeInstance(sInstance); } +void TestBrowserMalformedPtrName(void) +{ + Core *mdns = InitTest(); + Core::Browser browser; + const DnsMessage *dnsMsg; + uint16_t heapAllocations; + + Log("-------------------------------------------------------------------------------------------"); + Log("TestBrowserMalformedPtrName"); + + AdvanceTime(1); + + heapAllocations = sHeapAllocatedPtrs.GetLength(); + SuccessOrQuit(mdns->SetEnabled(true, kInfraIfIndex)); + + ClearAllBytes(browser); + browser.mServiceType = "_srv._udp"; + browser.mSubTypeLabel = nullptr; + browser.mInfraIfIndex = kInfraIfIndex; + browser.mCallback = HandleBrowseResult; + + sDnsMessages.Clear(); + sBrowseCallbacks.Clear(); + SuccessOrQuit(mdns->StartBrowser(browser)); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); + Log("Send a PTR response whose service-instance label is a single NUL byte"); + + SendPtrResponseWithEmptyInstanceLabel("_srv._udp.local.", 120); + + AdvanceTime(1); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); + Log("Validate that the malformed record is dropped and no result is reported"); + + VerifyOrQuit(sBrowseCallbacks.IsEmpty()); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); + Log("Validate that the browser keeps querying with no known-answer for the dropped record"); + + for (uint8_t queryCount = 0; queryCount < kNumInitialQueries; queryCount++) + { + sDnsMessages.Clear(); + + AdvanceTime(DetermineQueryWaitTime(queryCount)); + + VerifyOrQuit(!sDnsMessages.IsEmpty()); + dnsMsg = sDnsMessages.GetHead(); + dnsMsg->ValidateHeader(kMulticastQuery, /* Q */ 1, /* Ans */ 0, /* Auth */ 0, /* Addnl */ 0); + dnsMsg->ValidateAsQueryFor(browser); + VerifyOrQuit(dnsMsg->GetNext() == nullptr); + } + + VerifyOrQuit(sBrowseCallbacks.IsEmpty()); + + SuccessOrQuit(mdns->SetEnabled(false, kInfraIfIndex)); + VerifyOrQuit(sHeapAllocatedPtrs.GetLength() <= heapAllocations); + + Log("End of test"); + + testFreeInstance(sInstance); +} + void TestSrvResolver(void) { Core *mdns = InitTest(); @@ -8923,6 +9033,7 @@ int main(void) ot::Dns::Multicast::TestServiceConflict(); ot::Dns::Multicast::TestBrowser(); + ot::Dns::Multicast::TestBrowserMalformedPtrName(); ot::Dns::Multicast::TestSrvResolver(); ot::Dns::Multicast::TestTxtResolver(); ot::Dns::Multicast::TestIp6AddrResolver();