diff --git a/src/core/common/tlvs.cpp b/src/core/common/tlvs.cpp index 7ae94acf1..ddefbf69f 100644 --- a/src/core/common/tlvs.cpp +++ b/src/core/common/tlvs.cpp @@ -36,6 +36,7 @@ #include "common/code_utils.hpp" #include "common/debug.hpp" #include "common/message.hpp" +#include "common/string.hpp" namespace ot { @@ -162,26 +163,32 @@ exit: return error; } -Error Tlv::ReadStringTlv(const Message &aMessage, uint16_t aOffset, uint8_t aMaxStringLength, char *aValue) +Error Tlv::Info::ReadValue(const Message &aMessage, void *aValue, uint8_t aMinLength) const { - Error error = kErrorNone; - Info info; + // The `Message::Read()` flavor used below (with an `OffsetRange`) + // handles the boundary check. It will return `kErrorParse` if + // the requested read length exceeds the `OffsetRange`. - SuccessOrExit(error = info.ParseFrom(aMessage, aOffset)); + return aMessage.Read(mValueOffsetRange, aValue, aMinLength); +} - info.mValueOffsetRange.ShrinkLength(aMaxStringLength); - aMessage.ReadBytes(info.mValueOffsetRange, aValue); - aValue[info.mValueOffsetRange.GetLength()] = kNullChar; +Error Tlv::Info::ReadStringValue(const Message &aMessage, uint8_t aMaxStringLength, char *aValue) const +{ + Error error; + uint16_t length = Min(mValueOffsetRange.GetLength(), aMaxStringLength); + + SuccessOrExit(error = aMessage.Read(mValueOffsetRange.GetOffset(), aValue, length)); + aValue[length] = kNullChar; exit: return error; } -template Error Tlv::ReadUintTlv(const Message &aMessage, uint16_t aOffset, UintType &aValue) +template Error Tlv::Info::ReadUintValue(const Message &aMessage, UintType &aValue) const { Error error; - SuccessOrExit(error = ReadTlvValue(aMessage, aOffset, &aValue, sizeof(aValue))); + SuccessOrExit(error = ReadValue(aMessage, &aValue, sizeof(aValue))); aValue = BigEndian::HostSwap(aValue); exit: @@ -189,9 +196,9 @@ exit: } // Explicit instantiations of `ReadUintTlv<>()` -template Error Tlv::ReadUintTlv(const Message &aMessage, uint16_t aOffset, uint8_t &aValue); -template Error Tlv::ReadUintTlv(const Message &aMessage, uint16_t aOffset, uint16_t &aValue); -template Error Tlv::ReadUintTlv(const Message &aMessage, uint16_t aOffset, uint32_t &aValue); +template Error Tlv::Info::ReadUintValue(const Message &aMessage, uint8_t &aValue) const; +template Error Tlv::Info::ReadUintValue(const Message &aMessage, uint16_t &aValue) const; +template Error Tlv::Info::ReadUintValue(const Message &aMessage, uint32_t &aValue) const; Error Tlv::ReadTlvValue(const Message &aMessage, uint16_t aOffset, void *aValue, uint8_t aMinLength) { @@ -199,11 +206,7 @@ Error Tlv::ReadTlvValue(const Message &aMessage, uint16_t aOffset, void *aValue, Info info; SuccessOrExit(error = info.ParseFrom(aMessage, aOffset)); - - VerifyOrExit(info.mValueOffsetRange.Contains(aMinLength), error = kErrorParse); - info.mValueOffsetRange.ShrinkLength(aMinLength); - - aMessage.ReadBytes(info.mValueOffsetRange, aValue); + error = info.ReadValue(aMessage, aValue, aMinLength); exit: return error; @@ -215,7 +218,7 @@ Error Tlv::FindStringTlv(const Message &aMessage, uint8_t aType, uint8_t aMaxStr Info info; SuccessOrExit(error = info.FindIn(aMessage, aType)); - error = ReadStringTlv(aMessage, info.GetTlvOffset(), aMaxStringLength, aValue); + error = info.ReadStringValue(aMessage, aMaxStringLength, aValue); exit: return error; @@ -227,7 +230,7 @@ template Error Tlv::FindUintTlv(const Message &aMessage, uin Info info; SuccessOrExit(error = info.FindIn(aMessage, aType)); - error = ReadUintTlv(aMessage, info.GetTlvOffset(), aValue); + error = info.ReadUintValue(aMessage, aValue); exit: return error; diff --git a/src/core/common/tlvs.hpp b/src/core/common/tlvs.hpp index c1aecfe42..ff08abf5d 100644 --- a/src/core/common/tlvs.hpp +++ b/src/core/common/tlvs.hpp @@ -342,7 +342,97 @@ public: */ uint16_t GetValueOffset(void) const { return mValueOffsetRange.GetOffset(); } + /** + * Reads the TLV's value in a message expecting a minimum length for the value. + * + * This method can parse both standard and extended TLVs. It uses the current `GetValueOffsetRange()` to read + * from. It ensures that the TLV value has at least `aMinLength` bytes, otherwise `kErrorParse` is returned. + * + * @param[in] aMessage The message to read from. + * @param[out] aValue A buffer to output the TLV's value, must contain (at least) @p aMinLength bytes. + * @param[in] aMinLength The minimum expected length of TLV and number of bytes to copy into @p aValue + * buffer. + * + * @retval kErrorNone Successfully read the TLV and copied @p aMinLength into @p aValue. + * @retval kErrorParse The TLV was not well-formed and could not be parsed. + */ + Error ReadValue(const Message &aMessage, void *aValue, uint8_t aMinLength) const; + + /** + * Reads a simple TLV with a single non-integral value in a message. + * + * This method can parse both standard and extended TLVs. It uses the current `GetValueOffsetRange()` to read + * from. It ensures that the TLV value has at least the minimum expected length, otherwise `kErrorParse` is + * returned. + * + * @note This method does not check if `GetType()` matches the `SimpleTlvType`. It is caller's responsibility + * to validate this before calling this method. + * + * @tparam SimpleTlvType The simple TLV type to read (must be a sub-class of `SimpleTlvInfo`). + * + * @param[in] aMessage The message to read from. + * @param[out] aValue A reference to the value object to output the read value. + * + * @retval kErrorNone Successfully read the TLV and updated the @p aValue. + * @retval kErrorParse The TLV was not well-formed and could not be parsed. + */ + template + Error Read(const Message &aMessage, typename SimpleTlvType::ValueType &aValue) const + { + return ReadValue(aMessage, &aValue, sizeof(aValue)); + } + + /** + * Reads a simple TLV with a single integral value in a message. + * + * This method can parse both standard and extended TLVs. It uses the current `GetValueOffsetRange()` to read + * from. It ensures that the TLV value has at least the minimum expected length, otherwise `kErrorParse` is + * returned. + * + * @note This method does not check if `GetType()` matches the `UintTlvType`. It is caller's responsibility to + * validate this before calling this method. + * + * @tparam UintTlvType The simple TLV type to read (must be a sub-class of `UintTlvInfo`). + * + * @param[in] aMessage The message to read from. + * @param[out] aValue A reference to an unsigned int to output the read value. + * + * @retval kErrorNone Successfully read the TLV and updated the @p aValue. + * @retval kErrorParse The TLV was not well-formed and could not be parsed. + */ + template + Error Read(const Message &aMessage, typename UintTlvType::UintValueType &aValue) const + { + return ReadUintValue(aMessage, aValue); + } + + /** + * Reads a simple TLV with a UTF-8 string value in a message. + * + * This method can parse both standard and extended TLVs. It uses the current `GetValueOffsetRange()` to read + * from. The returned string in @p aValue is always null-terminated. + * + * @note This method does not check if `GetType()` matches the `StringTlvType`. It is caller's responsibility + * to validate this before calling this method. + * + * @tparam StringTlvType The simple TLV type to read (must be a sub-class of `StringTlvInfo`). + * + * @param[in] aMessage The message to read from. + * @param[out] aValue A reference to the string buffer to output the read value. + * + * @retval kErrorNone Successfully read the TLV and updated the @p aValue. + * @retval kErrorParse The TLV was not well-formed and could not be parsed. + */ + template + Error Read(const Message &aMessage, typename StringTlvType::StringType &aValue) const + { + return ReadStringValue(aMessage, StringTlvType::kMaxStringLength, aValue); + } + private: + template Error ReadUintValue(const Message &aMessage, UintType &aValue) const; + Error ReadStringValue(const Message &aMessage, uint8_t aMaxStringLength, char *aValue) const; + uint8_t mType; bool mIsExtended; OffsetRange mTlvOffsetRange; @@ -364,60 +454,6 @@ public: */ static Error ReadTlvValue(const Message &aMessage, uint16_t aOffset, void *aValue, uint8_t aMinLength); - /** - * Reads a simple TLV with a single non-integral value in a message at a given offset. - * - * @tparam SimpleTlvType The simple TLV type to read (must be a sub-class of `SimpleTlvInfo`). - * - * @param[in] aMessage The message to read from. - * @param[in] aOffset The offset into the message pointing to the start of the TLV. - * @param[out] aValue A reference to the value object to output the read value. - * - * @retval kErrorNone Successfully read the TLV and updated the @p aValue. - * @retval kErrorParse The TLV was not well-formed and could not be parsed. - */ - template - static Error Read(const Message &aMessage, uint16_t aOffset, typename SimpleTlvType::ValueType &aValue) - { - return ReadTlvValue(aMessage, aOffset, &aValue, sizeof(aValue)); - } - - /** - * Reads a simple TLV with a single integral value in a message at a given offset. - * - * @tparam UintTlvType The simple TLV type to read (must be a sub-class of `UintTlvInfo`). - * - * @param[in] aMessage The message to read from. - * @param[in] aOffset The offset into the message pointing to the start of the TLV. - * @param[out] aValue A reference to an unsigned int to output the read value. - * - * @retval kErrorNone Successfully read the TLV and updated the @p aValue. - * @retval kErrorParse The TLV was not well-formed and could not be parsed. - */ - template - static Error Read(const Message &aMessage, uint16_t aOffset, typename UintTlvType::UintValueType &aValue) - { - return ReadUintTlv(aMessage, aOffset, aValue); - } - - /** - * Reads a simple TLV with a UTF-8 string value in a message at a given offset. - * - * @tparam StringTlvType The simple TLV type to read (must be a sub-class of `StringTlvInfo`). - * - * @param[in] aMessage The message to read from. - * @param[in] aOffset The offset into the message pointing to the start of the TLV. - * @param[out] aValue A reference to the string buffer to output the read value. - * - * @retval kErrorNone Successfully read the TLV and updated the @p aValue. - * @retval kErrorParse The TLV was not well-formed and could not be parsed. - */ - template - static Error Read(const Message &aMessage, uint16_t aOffset, typename StringTlvType::StringType &aValue) - { - return ReadStringTlv(aMessage, aOffset, StringTlvType::kMaxStringLength, aValue); - } - /** * Searches for and reads a requested TLV out of a given message. * @@ -865,12 +901,10 @@ protected: private: static Error FindTlv(const Message &aMessage, uint8_t aType, void *aValue, uint16_t aLength); - static Error ReadStringTlv(const Message &aMessage, uint16_t aOffset, uint8_t aMaxStringLength, char *aValue); static Error FindStringTlv(const Message &aMessage, uint8_t aType, uint8_t aMaxStringLength, char *aValue); static Error AppendStringTlv(Message &aMessage, uint8_t aType, uint8_t aMaxStringLength, const char *aValue); static Error ValidateStringTlvValue(uint8_t aMaxStringLength, const char *aStringValue); static Error UpdateTlv(Message &aMessage, Bookmark aBookmark, bool aShouldWriteLength); - template static Error ReadUintTlv(const Message &aMessage, uint16_t aOffset, UintType &aValue); template static Error FindUintTlv(const Message &aMessage, uint8_t aType, UintType &aValue); template static Error AppendUintTlv(Message &aMessage, uint8_t aType, UintType aValue); @@ -961,7 +995,7 @@ public: * Defines constants and types for a simple TLV with an unsigned int value type. * * This class and its sub-classes are intended to be used as the template type in `Tlv::Append()`, and - * the related `Tlv::Find()` and `Tlv::Read()`. + * the related `Tlv::Find()` and `Tlv::Info::Read()`. * * @tparam kTlvTypeValue The TLV Type value. * @tparam UintType The TLV Value's type (must be an unsigned int, i.e. uint8_t, uint16_t, or uint32_t). @@ -978,7 +1012,7 @@ public: * Defines constants and types for a simple TLV with a single value. * * This class and its sub-classes are intended to be used as the template type in `Tlv::Append()`, - * and the related `Tlv::Find()` and `Tlv::Read()`. + * and the related `Tlv::Find()` and `Tlv::Info::Read()`. * * @tparam kTlvTypeValue The TLV Type value. * @tparam TlvValueType The TLV Value's type (must not be an integral type). @@ -997,7 +1031,7 @@ public: * Defines constants and types for a simple TLV with a UTF-8 string value. * * This class and its sub-classes are intended to be used as the template type in `Tlv::Append()`, - * and the related `Tlv::Find()` and `Tlv::Read()`. + * and the related `Tlv::Find()` and `Tlv::Info::Read()`. * * @tparam kTlvTypeValue The TLV Type value. * @tparam kTlvMaxValueLength The maximum allowed string length (as TLV value). diff --git a/src/core/thread/link_metrics.cpp b/src/core/thread/link_metrics.cpp index 955823912..17388416e 100644 --- a/src/core/thread/link_metrics.cpp +++ b/src/core/thread/link_metrics.cpp @@ -142,7 +142,7 @@ void Initiator::HandleReport(const Message &aMessage, OffsetRange &aOffsetRange, { case StatusSubTlv::kType: VerifyOrExit(!hasStatus && !hasReport, error = kErrorDrop); - SuccessOrExit(error = Tlv::Read(aMessage, aOffsetRange.GetOffset(), status)); + SuccessOrExit(error = tlvInfo.Read(aMessage, status)); hasStatus = true; break; @@ -310,7 +310,7 @@ Error Initiator::HandleManagementResponse(const Message &aMessage, const Ip6::Ad { case StatusSubTlv::kType: VerifyOrExit(!hasStatus, error = kErrorParse); - SuccessOrExit(error = Tlv::Read(aMessage, offsetRange.GetOffset(), status)); + SuccessOrExit(error = tlvInfo.Read(aMessage, status)); hasStatus = true; break; @@ -431,7 +431,7 @@ Error Subject::AppendReport(Message &aMessage, const Message &aRequestMessage, N switch (tlvInfo.GetType()) { case SubTlv::kQueryId: - SuccessOrExit(error = Tlv::Read(aRequestMessage, tlvInfo.GetTlvOffset(), queryId)); + SuccessOrExit(error = tlvInfo.Read(aRequestMessage, queryId)); hasQueryId = true; break; diff --git a/src/core/thread/mle_ftd.cpp b/src/core/thread/mle_ftd.cpp index 15435d3ac..32e056dd0 100644 --- a/src/core/thread/mle_ftd.cpp +++ b/src/core/thread/mle_ftd.cpp @@ -2731,14 +2731,13 @@ void Mle::HandleDiscoveryRequest(RxInfo &aRxInfo) switch (tlvInfo.GetType()) { case MeshCoP::Tlv::kDiscoveryRequest: - SuccessOrExit(error = Tlv::Read(aRxInfo.mMessage, offsetRange.GetOffset(), - discoveryRequestTlvValue)); + SuccessOrExit(error = + tlvInfo.Read(aRxInfo.mMessage, discoveryRequestTlvValue)); parsedDiscoveryRequestTlv = true; break; case MeshCoP::Tlv::kExtendedPanId: - SuccessOrExit( - error = Tlv::Read(aRxInfo.mMessage, offsetRange.GetOffset(), extPanId)); + SuccessOrExit(error = tlvInfo.Read(aRxInfo.mMessage, extPanId)); VerifyOrExit(Get().GetExtPanId() != extPanId, error = kErrorDrop); break; diff --git a/src/core/thread/network_diagnostic.cpp b/src/core/thread/network_diagnostic.cpp index 8390e8fd6..d074cceec 100644 --- a/src/core/thread/network_diagnostic.cpp +++ b/src/core/thread/network_diagnostic.cpp @@ -1290,25 +1290,24 @@ Error Client::GetNextDiagTlv(const Coap::Message &aMessage, Iterator &aIterator, switch (tlvInfo.GetType()) { case Tlv::kExtMacAddress: - SuccessOrExit(error = - Tlv::Read(aMessage, offset, AsCoreType(&aDiagTlv.mData.mExtAddress))); + SuccessOrExit(error = tlvInfo.Read(aMessage, AsCoreType(&aDiagTlv.mData.mExtAddress))); break; case Tlv::kAddress16: - SuccessOrExit(error = Tlv::Read(aMessage, offset, aDiagTlv.mData.mAddr16)); + SuccessOrExit(error = tlvInfo.Read(aMessage, aDiagTlv.mData.mAddr16)); break; case Tlv::kMode: { uint8_t mode; - SuccessOrExit(error = Tlv::Read(aMessage, offset, mode)); + SuccessOrExit(error = tlvInfo.Read(aMessage, mode)); Mle::DeviceMode(mode).Get(aDiagTlv.mData.mMode); break; } case Tlv::kTimeout: - SuccessOrExit(error = Tlv::Read(aMessage, offset, aDiagTlv.mData.mTimeout)); + SuccessOrExit(error = tlvInfo.Read(aMessage, aDiagTlv.mData.mTimeout)); break; case Tlv::kConnectivity: @@ -1340,7 +1339,7 @@ Error Client::GetNextDiagTlv(const Coap::Message &aMessage, Iterator &aIterator, { LeaderDataTlvValue tlvValue; - SuccessOrExit(error = Tlv::Read(aMessage, offset, tlvValue)); + SuccessOrExit(error = tlvInfo.Read(aMessage, tlvValue)); tlvValue.Get(AsCoreType(&aDiagTlv.mData.mLeaderData)); break; } @@ -1379,11 +1378,11 @@ Error Client::GetNextDiagTlv(const Coap::Message &aMessage, Iterator &aIterator, } case Tlv::kBatteryLevel: - SuccessOrExit(error = Tlv::Read(aMessage, offset, aDiagTlv.mData.mBatteryLevel)); + SuccessOrExit(error = tlvInfo.Read(aMessage, aDiagTlv.mData.mBatteryLevel)); break; case Tlv::kSupplyVoltage: - SuccessOrExit(error = Tlv::Read(aMessage, offset, aDiagTlv.mData.mSupplyVoltage)); + SuccessOrExit(error = tlvInfo.Read(aMessage, aDiagTlv.mData.mSupplyVoltage)); break; case Tlv::kChildTable: @@ -1429,36 +1428,35 @@ Error Client::GetNextDiagTlv(const Coap::Message &aMessage, Iterator &aIterator, break; case Tlv::kMaxChildTimeout: - SuccessOrExit(error = Tlv::Read(aMessage, offset, aDiagTlv.mData.mMaxChildTimeout)); + SuccessOrExit(error = tlvInfo.Read(aMessage, aDiagTlv.mData.mMaxChildTimeout)); break; case Tlv::kEui64: - SuccessOrExit(error = Tlv::Read(aMessage, offset, AsCoreType(&aDiagTlv.mData.mEui64))); + SuccessOrExit(error = tlvInfo.Read(aMessage, AsCoreType(&aDiagTlv.mData.mEui64))); break; case Tlv::kVersion: - SuccessOrExit(error = Tlv::Read(aMessage, offset, aDiagTlv.mData.mVersion)); + SuccessOrExit(error = tlvInfo.Read(aMessage, aDiagTlv.mData.mVersion)); break; case Tlv::kVendorName: - SuccessOrExit(error = Tlv::Read(aMessage, offset, aDiagTlv.mData.mVendorName)); + SuccessOrExit(error = tlvInfo.Read(aMessage, aDiagTlv.mData.mVendorName)); break; case Tlv::kVendorModel: - SuccessOrExit(error = Tlv::Read(aMessage, offset, aDiagTlv.mData.mVendorModel)); + SuccessOrExit(error = tlvInfo.Read(aMessage, aDiagTlv.mData.mVendorModel)); break; case Tlv::kVendorSwVersion: - SuccessOrExit(error = Tlv::Read(aMessage, offset, aDiagTlv.mData.mVendorSwVersion)); + SuccessOrExit(error = tlvInfo.Read(aMessage, aDiagTlv.mData.mVendorSwVersion)); break; case Tlv::kVendorAppUrl: - SuccessOrExit(error = Tlv::Read(aMessage, offset, aDiagTlv.mData.mVendorAppUrl)); + SuccessOrExit(error = tlvInfo.Read(aMessage, aDiagTlv.mData.mVendorAppUrl)); break; case Tlv::kThreadStackVersion: - SuccessOrExit(error = - Tlv::Read(aMessage, offset, aDiagTlv.mData.mThreadStackVersion)); + SuccessOrExit(error = tlvInfo.Read(aMessage, aDiagTlv.mData.mThreadStackVersion)); break; case Tlv::kNonPreferredChannels: @@ -1470,7 +1468,7 @@ Error Client::GetNextDiagTlv(const Coap::Message &aMessage, Iterator &aIterator, { uint8_t state; - SuccessOrExit(error = Tlv::Read(aMessage, offset, state)); + SuccessOrExit(error = tlvInfo.Read(aMessage, state)); aDiagTlv.mData.mBrState = static_cast(state); break; }