From 9cc1cd9e58995f8fb0748b1b2264a45f3dd13242 Mon Sep 17 00:00:00 2001 From: Cristian Bulacu <127317648+Cristib05@users.noreply.github.com> Date: Sat, 8 Jun 2024 00:01:50 +0300 Subject: [PATCH] [mdns] add support for legacy unicast response feature (#10053) Signed-off-by: Cristib05 --- src/core/net/mdns.cpp | 143 ++++++++++++++----- src/core/net/mdns.hpp | 37 +++-- tests/unit/test_mdns.cpp | 293 ++++++++++++++++++++++++++++++++++----- 3 files changed, 391 insertions(+), 82 deletions(-) diff --git a/src/core/net/mdns.cpp b/src/core/net/mdns.cpp index 8fe50f77e..75ae2acef 100644 --- a/src/core/net/mdns.cpp +++ b/src/core/net/mdns.cpp @@ -391,12 +391,13 @@ bool Core::NameMatch(const Heap::String &aFirst, const Heap::String &aSecond) return !aSecond.IsNull() && NameMatch(aFirst, aSecond.AsCString()); } -void Core::UpdateCacheFlushFlagIn(ResourceRecord &aResourceRecord, Section aSection) +void Core::UpdateCacheFlushFlagIn(ResourceRecord &aResourceRecord, Section aSection, bool aIsLegacyUnicast) { - // Do not set the cache-flush flag is the record is - // appended in Authority Section in a probe message. + // Do not set the cache-flush flag if the record is + // appended in Authority Section in a probe message, + // or is intended for a Legacy Unicast response. - if (aSection != kAuthoritySection) + if (aSection != kAuthoritySection && !aIsLegacyUnicast) { aResourceRecord.SetClass(aResourceRecord.GetClass() | kClassCacheFlushFlag); } @@ -579,6 +580,11 @@ void Core::RecordInfo::UpdateProperty(AddressArray &aAddrProperty, const Ip6::Ad } } +uint32_t Core::RecordInfo::GetTtl(bool aIsLegacyUnicast) const +{ + return aIsLegacyUnicast ? Min(kMaxLegacyUnicastTtl, mTtl) : mTtl; +} + void Core::RecordInfo::UpdateTtl(uint32_t aTtl) { return UpdateProperty(mTtl, aTtl); } void Core::RecordInfo::StartAnnouncing(void) @@ -596,7 +602,7 @@ void Core::RecordInfo::ScheduleAnswer(const AnswerInfo &aInfo) { VerifyOrExit(CanAnswer()); - if (aInfo.mUnicastResponse) + if (aInfo.mUnicastResponse || aInfo.mLegacyUnicastResponse) { mUnicastAnswerPending = true; ExitNow(); @@ -647,6 +653,7 @@ bool Core::RecordInfo::ShouldAppendTo(TxMessage &aResponse, TimeMilli aNow) cons break; case TxMessage::kUnicastResponse: + case TxMessage::kLegacyUnicastResponse: shouldAppend = mUnicastAnswerPending; break; @@ -693,6 +700,7 @@ void Core::RecordInfo::UpdateStateAfterAnswer(const TxMessage &aResponse) break; case TxMessage::kUnicastResponse: + case TxMessage::kLegacyUnicastResponse: VerifyOrExit(IsAppended()); VerifyOrExit(mAppendSection == kAnswerSection); mUnicastAnswerPending = false; @@ -765,6 +773,7 @@ void Core::RecordInfo::MarkAsAppended(TxMessage &aTxMessage, Section aSection) break; case TxMessage::kUnicastResponse: + case TxMessage::kLegacyUnicastResponse: mAppendState = kAppendedInUnicastMsg; break; @@ -1229,7 +1238,6 @@ template void Core::Entry::HandleTimer(EntryTimerContext &a case kRemoving: ExitNow(); } - thisAsEntryType->DetermineNextFireTime(); exit: @@ -1258,6 +1266,7 @@ void Core::Entry::AppendKeyRecordTo(TxMessage &aTxMessage, Section aSection, Nam { Message *message; ResourceRecord record; + bool isLegacyUnicast = (aTxMessage.GetType() == TxMessage::kLegacyUnicastResponse); VerifyOrExit(mKeyRecord.CanAppend()); mKeyRecord.MarkAsAppended(aTxMessage, aSection); @@ -1270,9 +1279,9 @@ void Core::Entry::AppendKeyRecordTo(TxMessage &aTxMessage, Section aSection, Nam aNameAppender(*this, aTxMessage, aSection); record.Init(ResourceRecord::kTypeKey); - record.SetTtl(mKeyRecord.GetTtl()); record.SetLength(mKeyData.GetLength()); - UpdateCacheFlushFlagIn(record, aSection); + record.SetTtl(mKeyRecord.GetTtl(isLegacyUnicast)); + UpdateCacheFlushFlagIn(record, aSection, isLegacyUnicast); SuccessOrAssert(message->Append(record)); SuccessOrAssert(message->AppendBytes(mKeyData.GetBytes(), mKeyData.GetLength())); @@ -1292,10 +1301,11 @@ void Core::Entry::AppendNsecRecordTo(TxMessage &aTxMessage, NsecRecord nsec; NsecRecord::TypeBitMap bitmap; uint16_t offset; + bool isLegacyUnicast = (aTxMessage.GetType() == TxMessage::kLegacyUnicastResponse); nsec.Init(); - nsec.SetTtl(kNsecTtl); - UpdateCacheFlushFlagIn(nsec, aSection); + nsec.SetTtl(isLegacyUnicast ? kLegacyUnicastNsecTtl : kNsecTtl); + UpdateCacheFlushFlagIn(nsec, aSection, isLegacyUnicast); bitmap.Clear(); @@ -1606,6 +1616,7 @@ exit: void Core::HostEntry::AppendAddressRecordsTo(TxMessage &aTxMessage, Section aSection) { Message *message; + bool isLegacyUnicast = (aTxMessage.GetType() == TxMessage::kLegacyUnicastResponse); VerifyOrExit(mAddrRecord.CanAppend()); mAddrRecord.MarkAsAppended(aTxMessage, aSection); @@ -1617,9 +1628,9 @@ void Core::HostEntry::AppendAddressRecordsTo(TxMessage &aTxMessage, Section aSec AaaaRecord aaaaRecord; aaaaRecord.Init(); - aaaaRecord.SetTtl(mAddrRecord.GetTtl()); aaaaRecord.SetAddress(address); - UpdateCacheFlushFlagIn(aaaaRecord, aSection); + aaaaRecord.SetTtl(mAddrRecord.GetTtl(isLegacyUnicast)); + UpdateCacheFlushFlagIn(aaaaRecord, aSection, isLegacyUnicast); AppendNameTo(aTxMessage, aSection); SuccessOrAssert(message->Append(aaaaRecord)); @@ -2383,6 +2394,7 @@ void Core::ServiceEntry::AppendSrvRecordTo(TxMessage &aTxMessage, Section aSecti Message *message; SrvRecord srv; uint16_t offset; + bool isLegacyUnicast = (aTxMessage.GetType() == TxMessage::kLegacyUnicastResponse); VerifyOrExit(mSrvRecord.CanAppend()); mSrvRecord.MarkAsAppended(aTxMessage, aSection); @@ -2390,13 +2402,17 @@ void Core::ServiceEntry::AppendSrvRecordTo(TxMessage &aTxMessage, Section aSecti message = &aTxMessage.SelectMessageFor(aSection); srv.Init(); - srv.SetTtl(mSrvRecord.GetTtl()); srv.SetPriority(mPriority); srv.SetWeight(mWeight); srv.SetPort(mPort); - UpdateCacheFlushFlagIn(srv, aSection); + srv.SetTtl(mSrvRecord.GetTtl(isLegacyUnicast)); + UpdateCacheFlushFlagIn(srv, aSection, isLegacyUnicast); + + // RFC6762, Section 18.14 Name Compression: + // In legacy unicast responses generated to answer legacy queries, name + // compression MUST NOT be performed on SRV records. + AppendServiceNameTo(aTxMessage, aSection, /* aPerformNameCompression */ !isLegacyUnicast); - AppendServiceNameTo(aTxMessage, aSection); offset = message->GetLength(); SuccessOrAssert(message->Append(srv)); AppendHostNameTo(aTxMessage, aSection); @@ -2412,6 +2428,7 @@ void Core::ServiceEntry::AppendTxtRecordTo(TxMessage &aTxMessage, Section aSecti { Message *message; TxtRecord txt; + bool isLegacyUnicast = (aTxMessage.GetType() == TxMessage::kLegacyUnicastResponse); VerifyOrExit(mTxtRecord.CanAppend()); mTxtRecord.MarkAsAppended(aTxMessage, aSection); @@ -2419,9 +2436,9 @@ void Core::ServiceEntry::AppendTxtRecordTo(TxMessage &aTxMessage, Section aSecti message = &aTxMessage.SelectMessageFor(aSection); txt.Init(); - txt.SetTtl(mTxtRecord.GetTtl()); txt.SetLength(mTxtData.GetLength()); - UpdateCacheFlushFlagIn(txt, aSection); + txt.SetTtl(mTxtRecord.GetTtl(isLegacyUnicast)); + UpdateCacheFlushFlagIn(txt, aSection, isLegacyUnicast); AppendServiceNameTo(aTxMessage, aSection); SuccessOrAssert(message->Append(txt)); @@ -2442,6 +2459,7 @@ void Core::ServiceEntry::AppendPtrRecordTo(TxMessage &aTxMessage, Section aSecti RecordInfo &ptrRecord = (aSubType == nullptr) ? mPtrRecord : aSubType->mPtrRecord; PtrRecord ptr; uint16_t offset; + bool isLegacyUnicast = (aTxMessage.GetType() == TxMessage::kLegacyUnicastResponse); VerifyOrExit(ptrRecord.CanAppend()); ptrRecord.MarkAsAppended(aTxMessage, aSection); @@ -2449,7 +2467,7 @@ void Core::ServiceEntry::AppendPtrRecordTo(TxMessage &aTxMessage, Section aSecti message = &aTxMessage.SelectMessageFor(aSection); ptr.Init(); - ptr.SetTtl(ptrRecord.GetTtl()); + ptr.SetTtl(ptrRecord.GetTtl(isLegacyUnicast)); if (aSubType == nullptr) { @@ -2506,12 +2524,22 @@ void Core::ServiceEntry::AppendEntryName(Entry &aEntry, TxMessage &aTxMessage, S static_cast(aEntry).AppendServiceNameTo(aTxMessage, aSection); } -void Core::ServiceEntry::AppendServiceNameTo(TxMessage &aTxMessage, Section aSection) +void Core::ServiceEntry::AppendServiceNameTo(TxMessage &aTxMessage, Section aSection, bool aPerformNameCompression) { AppendOutcome outcome; - outcome = aTxMessage.AppendLabel(aSection, mServiceInstance.AsCString(), mServiceNameOffset); - VerifyOrExit(outcome != kAppendedFullNameAsCompressed); + if (!aPerformNameCompression) + { + uint16_t compressOffset = kUnspecifiedOffset; + + outcome = aTxMessage.AppendLabel(aSection, mServiceInstance.AsCString(), compressOffset); + VerifyOrExit(outcome == kAppendedLabels); + } + else + { + outcome = aTxMessage.AppendLabel(aSection, mServiceInstance.AsCString(), mServiceNameOffset); + VerifyOrExit(outcome != kAppendedFullNameAsCompressed); + } AppendServiceTypeTo(aTxMessage, aSection); @@ -2775,7 +2803,14 @@ void Core::ServiceType::AppendPtrRecordTo(TxMessage &aResponse, uint16_t aServic message = &aResponse.SelectMessageFor(kAnswerSection); ptr.Init(); - ptr.SetTtl(mServicesPtr.GetTtl()); + if (aResponse.GetType() == TxMessage::kLegacyUnicastResponse) + { + ptr.SetTtl(Min(Core::RecordInfo::kMaxLegacyUnicastTtl, mServicesPtr.GetTtl())); + } + else + { + ptr.SetTtl(mServicesPtr.GetTtl()); + } aResponse.AppendServicesDnssdName(kAnswerSection); offset = message->GetLength(); @@ -2792,19 +2827,19 @@ exit: //---------------------------------------------------------------------------------------------------------------------- // Core::TxMessage -Core::TxMessage::TxMessage(Instance &aInstance, Type aType) +Core::TxMessage::TxMessage(Instance &aInstance, Type aType, uint16_t aQueryId) : InstanceLocator(aInstance) { - Init(aType); + Init(aType, aQueryId); } -Core::TxMessage::TxMessage(Instance &aInstance, Type aType, const AddressInfo &aUnicastDest) - : TxMessage(aInstance, aType) +Core::TxMessage::TxMessage(Instance &aInstance, Type aType, const AddressInfo &aUnicastDest, uint16_t aQueryId) + : TxMessage(aInstance, aType, aQueryId) { mUnicastDest = aUnicastDest; } -void Core::TxMessage::Init(Type aType) +void Core::TxMessage::Init(Type aType, uint16_t aMessageId) { Header header; @@ -2837,7 +2872,9 @@ void Core::TxMessage::Init(Type aType) break; case kMulticastResponse: case kUnicastResponse: + case kLegacyUnicastResponse: header.SetType(Header::kTypeResponse); + header.SetMessageId(aMessageId); break; } @@ -2864,9 +2901,9 @@ Message &Core::TxMessage::SelectMessageFor(Section aSection) mainSection = kQuestionSection; extraSection = kAnswerSection; break; - - case kMulticastResponse: + case kLegacyUnicastResponse: case kUnicastResponse: + case kMulticastResponse: break; } @@ -3022,6 +3059,16 @@ exit: return; } +void Core::TxMessage::AddQuestionFrom(const Message &aMessage) +{ + uint16_t offset = sizeof(Header); + + IgnoreError(Name::ParseName(aMessage, offset)); + offset += sizeof(ot::Dns::Question); + SuccessOrAssert(mMsgPtr->AppendBytesFromMessage(aMessage, sizeof(Header), offset - sizeof(Header))); + IncrementRecordCount(kQuestionSection); +} + void Core::TxMessage::SaveOffset(uint16_t &aCompressOffset, const Message &aMessage, Section aSection) { // Saves the current message offset in `aCompressOffset` for name @@ -3147,6 +3194,7 @@ void Core::TxMessage::Send(void) break; case kUnicastResponse: + case kLegacyUnicastResponse: otPlatMdnsSendUnicast(&GetInstance(), mMsgPtr.Release(), &mUnicastDest); break; } @@ -3215,6 +3263,8 @@ void Core::TxMessage::Reinit(void) // compress offset since the host name should not be used // in any other query question. + break; + case kLegacyUnicastResponse: break; } } @@ -3297,11 +3347,12 @@ Error Core::RxMessage::Init(Instance &aInstance, if (aSenderAddress.mPort != kUdpPort) { - if (mIsQuery) + // Simple DNS resolver does not allow more than one question in a query message + if (mIsQuery && header.GetQuestionCount() == 1) { // Section 6.7 Legacy Unicast - LogInfo("We do not yet support legacy unicast message (source port not matching mDNS port)"); - ExitNow(error = kErrorNotCapable); + mIsLegacyUnicast = true; + mQueryId = header.GetMessageId(); } else { @@ -3427,13 +3478,18 @@ Core::RxMessage::ProcessOutcome Core::RxMessage::ProcessQuery(bool aShouldProces { canAnswer = true; - if (question.mUnicastResponse) + if (question.mUnicastResponse || mIsLegacyUnicast) { needUnicastResponse = true; } } } + if (mIsLegacyUnicast) + { + shouldDelay = false; + } + VerifyOrExit(canAnswer); if (mTruncated && !aShouldProcessTruncated) @@ -3569,10 +3625,11 @@ void Core::RxMessage::AnswerQuestion(const Question &aQuestion, TimeMilli aAnswe VerifyOrExit(aQuestion.mCanAnswer); - answerInfo.mQuestionRrType = aQuestion.mRrType; - answerInfo.mAnswerTime = aAnswerTime; - answerInfo.mIsProbe = aQuestion.mIsProbe; - answerInfo.mUnicastResponse = aQuestion.mUnicastResponse; + answerInfo.mQuestionRrType = aQuestion.mRrType; + answerInfo.mAnswerTime = aAnswerTime; + answerInfo.mIsProbe = aQuestion.mIsProbe; + answerInfo.mUnicastResponse = aQuestion.mUnicastResponse; + answerInfo.mLegacyUnicastResponse = mIsLegacyUnicast; if (aQuestion.mIsForAllServicesDnssd) { @@ -3790,7 +3847,17 @@ exit: void Core::RxMessage::SendUnicastResponse(const AddressInfo &aUnicastDest) { - TxMessage response(GetInstance(), TxMessage::kUnicastResponse, aUnicastDest); + TxMessage response(GetInstance(), + mIsLegacyUnicast ? TxMessage::kLegacyUnicastResponse : TxMessage::kUnicastResponse, aUnicastDest, + mIsLegacyUnicast ? mQueryId : 0); + + if (mIsLegacyUnicast) + { + // RFC6762, section 6.7: + // Legacy Unicast Response must repeat the question + response.AddQuestionFrom(*mMessagePtr); + } + TimeMilli now = TimerMilli::GetNow(); for (HostEntry &entry : Get().mHostEntries) diff --git a/src/core/net/mdns.hpp b/src/core/net/mdns.hpp index 1065bf160..5a8ede1ec 100644 --- a/src/core/net/mdns.hpp +++ b/src/core/net/mdns.hpp @@ -759,11 +759,12 @@ private: static constexpr uint32_t kMaxInitialQueryDelay = 120; // msec static constexpr uint32_t kRandomDelayReuseInterval = 2; // msec - static constexpr uint32_t kUnspecifiedTtl = 0; - static constexpr uint32_t kDefaultTtl = 120; - static constexpr uint32_t kDefaultKeyTtl = kDefaultTtl; - static constexpr uint32_t kNsecTtl = 4500; - static constexpr uint32_t kServicesPtrTtl = 4500; + static constexpr uint32_t kUnspecifiedTtl = 0; + static constexpr uint32_t kDefaultTtl = 120; + static constexpr uint32_t kDefaultKeyTtl = kDefaultTtl; + static constexpr uint32_t kLegacyUnicastNsecTtl = 10; + static constexpr uint32_t kNsecTtl = 4500; + static constexpr uint32_t kServicesPtrTtl = 4500; static constexpr uint16_t kClassQuestionUnicastFlag = (1U << 15); static constexpr uint16_t kClassCacheFlushFlag = (1U << 15); @@ -858,6 +859,7 @@ private: TimeMilli mAnswerTime; bool mIsProbe; bool mUnicastResponse; + bool mLegacyUnicastResponse; }; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -896,16 +898,19 @@ private: public: // Keeps track of record state and timings. + static constexpr uint32_t kMaxLegacyUnicastTtl = 10; // seconds + RecordInfo(void) { Clear(); } - bool IsPresent(void) const { return mIsPresent; } - uint32_t GetTtl(void) const { return mTtl; } + bool IsPresent(void) const { return mIsPresent; } template void UpdateProperty(UintType &aProperty, UintType aValue); void UpdateProperty(AddressArray &aAddrProperty, const Ip6::Address *aAddrs, uint16_t aNumAddrs); void UpdateProperty(Heap::String &aStringProperty, const char *aString); void UpdateProperty(Heap::Data &aDataProperty, const uint8_t *aData, uint16_t aLength); - void UpdateTtl(uint32_t aTtl); + + uint32_t GetTtl(bool aIsLegacyUnicast = false) const; + void UpdateTtl(uint32_t aTtl); void StartAnnouncing(void); bool ShouldAppendTo(TxMessage &aResponse, TimeMilli aNow) const; @@ -1162,7 +1167,7 @@ private: void AppendPtrRecordTo(TxMessage &aTxMessage, Section aSection, SubType *aSubType = nullptr); void AppendKeyRecordTo(TxMessage &aTxMessage, Section aSection); void AppendNsecRecordTo(TxMessage &aTxMessage, Section aSection); - void AppendServiceNameTo(TxMessage &TxMessage, Section aSection); + void AppendServiceNameTo(TxMessage &TxMessage, Section aSection, bool aPerformNameCompression = true); void AppendServiceTypeTo(TxMessage &aTxMessage, Section aSection); void AppendSubServiceTypeTo(TxMessage &aTxMessage, Section aSection); void AppendSubServiceNameTo(TxMessage &aTxMessage, Section aSection, SubType &aSubType); @@ -1239,10 +1244,11 @@ private: kMulticastQuery, kMulticastResponse, kUnicastResponse, + kLegacyUnicastResponse, }; - TxMessage(Instance &aInstance, Type aType); - TxMessage(Instance &aInstance, Type aType, const AddressInfo &aUnicastDest); + TxMessage(Instance &aInstance, Type aType, uint16_t aQueryId = 0); + TxMessage(Instance &aInstance, Type aType, const AddressInfo &aUnicastDest, uint16_t aQueryId = 0); Type GetType(void) const { return mType; } Message &SelectMessageFor(Section aSection); AppendOutcome AppendLabel(Section aSection, const char *aLabel, uint16_t &aCompressOffset); @@ -1250,6 +1256,7 @@ private: void AppendServiceType(Section aSection, const char *aServiceType, uint16_t &aCompressOffset); void AppendDomainName(Section aSection); void AppendServicesDnssdName(Section aSection); + void AddQuestionFrom(const Message &aMessage); void IncrementRecordCount(Section aSection) { mRecordCounts.Increment(aSection); } void CheckSizeLimitToPrepareAgain(bool &aPrepareAgain); void SaveCurrentState(void); @@ -1259,7 +1266,7 @@ private: private: static constexpr bool kIsSingleLabel = true; - void Init(Type aType); + void Init(Type aType, uint16_t aMessageId = 0); void Reinit(void); bool IsOverSizeLimit(void) const; AppendOutcome AppendLabels(Section aSection, @@ -1382,8 +1389,10 @@ private: AddressInfo mSenderAddress; RecordCounts mRecordCounts; uint16_t mStartOffset[kNumSections]; + uint16_t mQueryId; bool mIsQuery : 1; bool mIsUnicast : 1; + bool mIsLegacyUnicast : 1; bool mTruncated : 1; bool mIsSelfOriginating : 1; }; @@ -1984,7 +1993,9 @@ private: static uint32_t DetermineTtl(uint32_t aTtl, uint32_t aDefaultTtl); static bool NameMatch(const Heap::String &aHeapString, const char *aName); static bool NameMatch(const Heap::String &aFirst, const Heap::String &aSecond); - static void UpdateCacheFlushFlagIn(ResourceRecord &aResourceRecord, Section aSection); + static void UpdateCacheFlushFlagIn(ResourceRecord &aResourceRecord, + Section aSection, + bool aIsLegacyUnicast = false); static void UpdateRecordLengthInMessage(ResourceRecord &aRecord, Message &aMessage, uint16_t aOffset); static void UpdateCompressOffset(uint16_t &aOffset, uint16_t aNewOffse); static bool QuestionMatches(uint16_t aQuestionRrType, uint16_t aRrType); diff --git a/tests/unit/test_mdns.cpp b/tests/unit/test_mdns.cpp index 1dd114e96..2f1fb562b 100644 --- a/tests/unit/test_mdns.cpp +++ b/tests/unit/test_mdns.cpp @@ -62,17 +62,20 @@ namespace Multicast { //--------------------------------------------------------------------------------------------------------------------- // Constants -static constexpr uint16_t kClassQueryUnicastFlag = (1U << 15); -static constexpr uint16_t kClassCacheFlushFlag = (1U << 15); -static constexpr uint16_t kClassMask = 0x7fff; -static constexpr uint16_t kStringSize = 300; -static constexpr uint16_t kMaxDataSize = 400; -static constexpr uint16_t kNumAnnounces = 3; -static constexpr uint16_t kNumInitalQueries = 3; -static constexpr uint16_t kNumRefreshQueries = 4; -static constexpr bool kCacheFlush = true; -static constexpr uint16_t kMdnsPort = 5353; -static constexpr uint32_t kInfraIfIndex = 1; +static constexpr uint16_t kClassQueryUnicastFlag = (1U << 15); +static constexpr uint16_t kClassCacheFlushFlag = (1U << 15); +static constexpr uint16_t kClassMask = 0x7fff; +static constexpr uint16_t kStringSize = 300; +static constexpr uint16_t kMaxDataSize = 400; +static constexpr uint16_t kNumAnnounces = 3; +static constexpr uint16_t kNumInitalQueries = 3; +static constexpr uint16_t kNumRefreshQueries = 4; +static constexpr bool kCacheFlush = true; +static constexpr uint16_t kMdnsPort = 5353; +static constexpr uint16_t kEphemeralPort = 49152; +static constexpr uint16_t kLegacyUnicastMessageId = 1; +static constexpr uint16_t kMaxLegacyUnicastTtl = 10; +static constexpr uint32_t kInfraIfIndex = 1; static const char kDeviceIp6Address[] = "fd01::1"; @@ -209,6 +212,7 @@ enum TtlCheckMode : uint8_t { kZeroTtl, kNonZeroTtl, + kLegacyUnicastTtl, }; enum Section : uint8_t @@ -372,6 +376,9 @@ struct DnsRecord : public Allocatable, public LinkedListEntry 0); break; + case kLegacyUnicastTtl: + VerifyOrQuit(mTtl <= kMaxLegacyUnicastTtl); + break; } matches = true; @@ -571,6 +578,7 @@ enum DnsMessageType : uint8_t kMulticastQuery, kMulticastResponse, kUnicastResponse, + kLegacyUnicastResponse, }; struct DnsMessage : public Allocatable, public LinkedListEntry @@ -682,9 +690,15 @@ struct DnsMessage : public Allocatable, public LinkedListEntry, public LinkedListEntry, public LinkedListEntry, public LinkedListEntry, public LinkedListEntry, public LinkedListEntry, public LinkedListEntry, public LinkedListEntrymType = kUnicastResponse; + msg->mType = (aUnicastDest->mPort == kEphemeralPort) ? kLegacyUnicastResponse : kUnicastResponse; msg->mUnicastDest = *aUnicastDest; } } @@ -1045,8 +1090,9 @@ static void ParseMessage(const Message &aMessage, const Core::AddressInfo *aUnic static void SendQuery(const char *aName, uint16_t aRecordType, - uint16_t aRecordClass = ResourceRecord::kClassInternet, - bool aTruncated = false) + uint16_t aRecordClass = ResourceRecord::kClassInternet, + bool aTruncated = false, + bool aLegacyUnicastQuery = false) { Message *message; Header header; @@ -1059,6 +1105,11 @@ static void SendQuery(const char *aName, header.SetType(Header::kTypeQuery); header.SetQuestionCount(1); + if (aLegacyUnicastQuery) + { + header.SetMessageId(kLegacyUnicastMessageId); + } + if (aTruncated) { header.SetTruncationFlag(); @@ -1069,7 +1120,7 @@ static void SendQuery(const char *aName, SuccessOrQuit(message->Append(Question(aRecordType, aRecordClass))); SuccessOrQuit(AsCoreType(&senderAddrInfo.mAddress).FromString(kDeviceIp6Address)); - senderAddrInfo.mPort = kMdnsPort; + senderAddrInfo.mPort = aLegacyUnicastQuery ? kEphemeralPort : kMdnsPort; senderAddrInfo.mInfraIfIndex = 0; Log("Sending query for %s %s", aName, RecordTypeToString(aRecordType)); @@ -1077,7 +1128,11 @@ static void SendQuery(const char *aName, otPlatMdnsHandleReceive(sInstance, message, /* aIsUnicast */ false, &senderAddrInfo); } -static void SendQueryForTwo(const char *aName1, uint16_t aRecordType1, const char *aName2, uint16_t aRecordType2) +static void SendQueryForTwo(const char *aName1, + uint16_t aRecordType1, + const char *aName2, + uint16_t aRecordType2, + bool aIsLegacyUnicast = false) { // Send query with two questions. @@ -1099,7 +1154,7 @@ static void SendQueryForTwo(const char *aName1, uint16_t aRecordType1, const cha SuccessOrQuit(message->Append(Question(aRecordType2, ResourceRecord::kClassInternet))); SuccessOrQuit(AsCoreType(&senderAddrInfo.mAddress).FromString(kDeviceIp6Address)); - senderAddrInfo.mPort = kMdnsPort; + senderAddrInfo.mPort = aIsLegacyUnicast ? kEphemeralPort : kMdnsPort; senderAddrInfo.mInfraIfIndex = 0; Log("Sending query for %s %s and %s %s", aName1, RecordTypeToString(aRecordType1), aName2, @@ -6877,6 +6932,181 @@ void TestPassiveCache(void) testFreeInstance(sInstance); } +void TestLegacyUnicastResponse(void) +{ + Core *mdns = InitTest(); + Core::Host host; + Core::Service service; + const DnsMessage *dnsMsg; + uint16_t heapAllocations; + DnsNameString fullServiceName; + DnsNameString fullServiceType; + DnsNameString hostFullName; + Ip6::Address hostAddresses[2]; + + Log("-------------------------------------------------------------------------------------------"); + Log("TestLegacyUnicastResponse"); + + AdvanceTime(1); + + heapAllocations = sHeapAllocatedPtrs.GetLength(); + SuccessOrQuit(mdns->SetEnabled(true, kInfraIfIndex)); + + SuccessOrQuit(hostAddresses[0].FromString("fd00::1:aaaa")); + SuccessOrQuit(hostAddresses[1].FromString("fd00::1:bbbb")); + host.mHostName = "host"; + host.mAddresses = hostAddresses; + host.mAddressesLength = 2; + host.mTtl = 1500; + hostFullName.Append("%s.local.", host.mHostName); + + service.mHostName = host.mHostName; + service.mServiceInstance = "myservice"; + service.mServiceType = "_srv._udp"; + service.mSubTypeLabels = nullptr; + service.mSubTypeLabelsLength = 0; + service.mTxtData = kTxtData1; + service.mTxtDataLength = sizeof(kTxtData1); + service.mPort = 1234; + service.mPriority = 1; + service.mWeight = 2; + service.mTtl = 1000; + + fullServiceName.Append("%s.%s.local.", service.mServiceInstance, service.mServiceType); + fullServiceType.Append("%s.local.", service.mServiceType); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); + + sDnsMessages.Clear(); + + for (RegCallback ®Callbck : sRegCallbacks) + { + regCallbck.Reset(); + } + + SuccessOrQuit(mdns->RegisterHost(host, 0, HandleSuccessCallback)); + SuccessOrQuit(mdns->RegisterService(service, 1, HandleSuccessCallback)); + + AdvanceTime(10 * 1000); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); + Log("Send a query with two questions (SRV for service1 and AAAA for host). Validate that no response is sent"); + + AdvanceTime(2000); + + sDnsMessages.Clear(); + SendQueryForTwo(fullServiceName.AsCString(), ResourceRecord::kTypeSrv, hostFullName.AsCString(), + ResourceRecord::kTypeAaaa, /* aIsLegacyUnicast */ true); + + AdvanceTime(200); + + dnsMsg = sDnsMessages.GetHead(); + VerifyOrQuit(dnsMsg == nullptr); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); + Log("Send a query for SRV record and validate the response"); + + AdvanceTime(2000); + + sDnsMessages.Clear(); + SendQuery(fullServiceName.AsCString(), ResourceRecord::kTypeSrv, ResourceRecord::kClassInternet, + /* aTruncated */ false, + /* aLegacyUnicastQuery */ true); + + AdvanceTime(1000); + + dnsMsg = sDnsMessages.GetHead(); + VerifyOrQuit(dnsMsg != nullptr); + dnsMsg->ValidateHeader(kLegacyUnicastResponse, /* Q */ 1, /* Ans */ 1, /* Auth */ 0, /* Addnl */ 3); + dnsMsg->Validate(service, kInAnswerSection, kCheckSrv); + dnsMsg->Validate(host, kInAdditionalSection); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); + Log("Send a query for TXT record and validate the response"); + + AdvanceTime(2000); + + sDnsMessages.Clear(); + SendQuery(fullServiceName.AsCString(), ResourceRecord::kTypeTxt, ResourceRecord::kClassInternet, + /* aTruncated */ false, + /* aLegacyUnicastQuery */ true); + + AdvanceTime(1000); + + dnsMsg = sDnsMessages.GetHead(); + VerifyOrQuit(dnsMsg != nullptr); + dnsMsg->ValidateHeader(kLegacyUnicastResponse, /* Q */ 1, /* Ans */ 1, /* Auth */ 0, /* Addnl */ 1); + dnsMsg->Validate(service, kInAnswerSection, kCheckTxt); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); + Log("Send a query for ANY record and validate the response"); + + AdvanceTime(2000); + + sDnsMessages.Clear(); + SendQuery(fullServiceName.AsCString(), ResourceRecord::kTypeAny, ResourceRecord::kClassInternet, + /* aTruncated */ false, + /* aLegacyUnicastQuery */ true); + + AdvanceTime(1000); + + dnsMsg = sDnsMessages.GetHead(); + VerifyOrQuit(dnsMsg != nullptr); + dnsMsg->ValidateHeader(kLegacyUnicastResponse, /* Q */ 1, /* Ans */ 2, /* Auth */ 0, /* Addnl */ 3); + dnsMsg->Validate(service, kInAnswerSection, kCheckSrv | kCheckTxt); + dnsMsg->Validate(host, kInAdditionalSection); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); + Log("Send a query for PTR record for service type and validate the response"); + + AdvanceTime(2000); + + sDnsMessages.Clear(); + SendQuery(fullServiceType.AsCString(), ResourceRecord::kTypePtr, ResourceRecord::kClassInternet, + /* aTruncated */ false, + /* aLegacyUnicastQuery */ true); + + AdvanceTime(1000); + + dnsMsg = sDnsMessages.GetHead(); + VerifyOrQuit(dnsMsg != nullptr); + dnsMsg->ValidateHeader(kLegacyUnicastResponse, /* Q */ 1, /* Ans */ 1, /* Auth */ 0, /* Addnl */ 4); + dnsMsg->Validate(service, kInAnswerSection, kCheckPtr); + dnsMsg->Validate(service, kInAdditionalSection, kCheckSrv | kCheckTxt); + dnsMsg->Validate(host, kInAdditionalSection); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); + Log("Send a query for non-existing record and validate the response with NSEC"); + + AdvanceTime(2000); + + sDnsMessages.Clear(); + SendQuery(hostFullName.AsCString(), ResourceRecord::kTypeA, ResourceRecord::kClassInternet, /* aTruncated */ false, + /* aLegacyUnicastQuery */ true); + + AdvanceTime(1000); + + dnsMsg = sDnsMessages.GetHead(); + VerifyOrQuit(dnsMsg != nullptr); + dnsMsg->ValidateHeader(kLegacyUnicastResponse, /* Q */ 1, /* Ans */ 0, /* Auth */ 0, /* Addnl */ 1); + VerifyOrQuit(dnsMsg->mAdditionalRecords.ContainsNsec(hostFullName, ResourceRecord::kTypeAaaa)); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); + + sDnsMessages.Clear(); + + SuccessOrQuit(mdns->UnregisterHost(host)); + + AdvanceTime(15000); + + SuccessOrQuit(mdns->SetEnabled(false, kInfraIfIndex)); + VerifyOrQuit(sHeapAllocatedPtrs.GetLength() <= heapAllocations); + + Log("End of test"); + + testFreeInstance(sInstance); +} + } // namespace Multicast } // namespace Dns } // namespace ot @@ -6904,6 +7134,7 @@ int main(void) ot::Dns::Multicast::TestTxtResolver(); ot::Dns::Multicast::TestIp6AddrResolver(); ot::Dns::Multicast::TestPassiveCache(); + ot::Dns::Multicast::TestLegacyUnicastResponse(); printf("All tests passed\n"); #else