diff --git a/src/core/net/dns_types.cpp b/src/core/net/dns_types.cpp index 631051b4e..ebb0524bf 100644 --- a/src/core/net/dns_types.cpp +++ b/src/core/net/dns_types.cpp @@ -100,6 +100,51 @@ Error Header::ResponseCodeToError(Response aResponse) return error; } +Error Name::AppendTo(Message &aMessage) const +{ + Error error; + + if (IsEmpty()) + { + error = AppendTerminator(aMessage); + } + else if (IsFromCString()) + { + error = AppendName(GetAsCString(), aMessage); + } + else + { + // Name is from a message. Read labels one by one from + // `mMessage` and and append each to the `aMessage`. + + LabelIterator iterator(*mMessage, mOffset); + + while (true) + { + error = iterator.GetNextLabel(); + + switch (error) + { + case kErrorNone: + SuccessOrExit(error = iterator.AppendLabel(aMessage)); + break; + + case kErrorNotFound: + // We reached the end of name successfully. + error = AppendTerminator(aMessage); + + OT_FALL_THROUGH; + + default: + ExitNow(); + } + } + } + +exit: + return error; +} + Error Name::AppendLabel(const char *aLabel, Message &aMessage) { return AppendLabel(aLabel, static_cast(StringLength(aLabel, kMaxLabelSize)), aMessage); @@ -545,6 +590,21 @@ bool Name::LabelIterator::CompareLabel(const LabelIterator &aOtherIterator) cons mLabelLength); } +Error Name::LabelIterator::AppendLabel(Message &aMessage) const +{ + // This method reads and appends the current label in the iterator + // to `aMessage`. + + Error error; + + VerifyOrExit((0 < mLabelLength) && (mLabelLength <= kMaxLabelLength), error = kErrorInvalidArgs); + SuccessOrExit(error = aMessage.Append(mLabelLength)); + error = aMessage.AppendBytesFromMessage(mMessage, mLabelStartOffset, mLabelLength); + +exit: + return error; +} + bool Name::IsSubDomainOf(const char *aName, const char *aDomain) { bool match = false; diff --git a/src/core/net/dns_types.hpp b/src/core/net/dns_types.hpp index b9d40cb6a..753dc2f46 100644 --- a/src/core/net/dns_types.hpp +++ b/src/core/net/dns_types.hpp @@ -655,6 +655,24 @@ public: return *mMessage; } + /** + * This method encodes and appends the name to a message. + * + * If the name is empty (not specified), then root "." is appended to @p aMessage. If the name is from a C string + * then the string is checked and appended (similar to static `AppendName(const char *aName, Message &)` method). + * If the the name is from a message, then it is read from the message and appended to @p aMessage. Note that in + * this case independent of whether the name is compressed or not in its original message, the name is appended + * as full (uncompressed) in @p aMessage. + * + * @param[in] aMessage The message to append to. + * + * @retval kErrorNone Successfully encoded and appended the name to @p aMessage. + * @retval kErrorInvalidArgs Name is not valid. + * @retval kErrorNoBufs Insufficient available buffers to grow the message. + * + */ + Error AppendTo(Message &aMessage) const; + /** * This static method encodes and appends a single name label to a message. * @@ -1021,6 +1039,7 @@ private: Error ReadLabel(char *aLabelBuffer, uint8_t &aLabelLength, bool aAllowDotCharInLabel) const; bool CompareLabel(const char *&aName, bool aIsSingleLabel) const; bool CompareLabel(const LabelIterator &aOtherIterator) const; + Error AppendLabel(Message &aMessage) const; const Message &mMessage; // Message to read labels from. uint16_t mLabelStartOffset; // Offset in `mMessage` to the first char of current label text. diff --git a/tests/unit/test_dns.cpp b/tests/unit/test_dns.cpp index 64011e531..da152e8da 100644 --- a/tests/unit/test_dns.cpp +++ b/tests/unit/test_dns.cpp @@ -407,6 +407,7 @@ void TestDnsCompressedName(void) Instance * instance; MessagePool *messagePool; Message * message; + Message * message2; uint16_t offset; uint16_t name1Offset; uint16_t name2Offset; @@ -416,6 +417,10 @@ void TestDnsCompressedName(void) char label[kLabelSize]; uint8_t labelLength; char name[kNameSize]; + Dns::Name dnsName1; + Dns::Name dnsName2; + Dns::Name dnsName3; + Dns::Name dnsName4; printf("================================================================\n"); printf("TestDnsCompressedName()\n"); @@ -685,7 +690,44 @@ void TestDnsCompressedName(void) VerifyOrQuit(Dns::Name::CompareName(*message, offset, *message, name1Offset) == kErrorNotFound, "Name::CompareName() did not fail with mismatching name"); + printf("----------------------------------------------------------------\n"); + printf("Append names from one message to another\n"); + + VerifyOrQuit((message2 = messagePool->New(Message::kTypeIp6, 0)) != nullptr, "Message::New failed"); + + dnsName1.SetFromMessage(*message, name1Offset); + dnsName2.SetFromMessage(*message, name2Offset); + dnsName3.SetFromMessage(*message, name3Offset); + dnsName4.SetFromMessage(*message, name4Offset); + + offset = 0; + SuccessOrQuit(dnsName1.AppendTo(*message2), "Name::AppendTo() failed"); + SuccessOrQuit(dnsName2.AppendTo(*message2), "Name::AppendTo() failed"); + SuccessOrQuit(dnsName3.AppendTo(*message2), "Name::AppendTo() failed"); + SuccessOrQuit(dnsName4.AppendTo(*message2), "Name::AppendTo() failed"); + + SuccessOrQuit(message2->Read(0, buffer, message2->GetLength()), "Message::Read() failed"); + DumpBuffer("message2", buffer, message2->GetLength()); + + // Now compare the names one by one in `message2`. Note that + // `CompareName()` will update `offset` on success. + + VerifyOrQuit(Dns::Name::CompareName(*message2, offset, dnsName1) == kErrorNone, "Incorrect name after AppendTo()"); + VerifyOrQuit(Dns::Name::CompareName(*message2, offset, dnsName2) == kErrorNone, "Incorrect name after AppendTo()"); + VerifyOrQuit(Dns::Name::CompareName(*message2, offset, dnsName3) == kErrorNone, "Incorrect name after AppendTo()"); + VerifyOrQuit(Dns::Name::CompareName(*message2, offset, dnsName4) == kErrorNone, "Incorrect name after AppendTo()"); + + offset = 0; + SuccessOrQuit(Dns::Name::ReadName(*message2, offset, name, sizeof(name)), "ReadName() failed"); + printf("- Name1 after `AppendTo()`: \"%s\"\n", name); + SuccessOrQuit(Dns::Name::ReadName(*message2, offset, name, sizeof(name)), "ReadName() failed"); + printf("- Name2 after `AppendTo()`: \"%s\"\n", name); + SuccessOrQuit(Dns::Name::ReadName(*message2, offset, name, sizeof(name)), "ReadName() failed"); + printf("- Name3 after `AppendTo()`: \"%s\"\n", name); + // `ReadName()` for name-4 will fail due to first label containing dot char. + message->Free(); + message2->Free(); testFreeInstance(instance); }