| /* |
| * Copyright (c) 2016, The OpenThread Authors. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of the copyright holder nor the |
| * names of its contributors may be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| /** |
| * @file |
| * This file implements common methods for manipulating MLE TLVs. |
| */ |
| |
| #include "tlvs.hpp" |
| |
| #include "common/code_utils.hpp" |
| #include "common/debug.hpp" |
| #include "common/message.hpp" |
| |
| namespace ot { |
| |
| uint32_t Tlv::GetSize(void) const |
| { |
| return IsExtended() ? sizeof(ExtendedTlv) + As<ExtendedTlv>(this)->GetLength() : sizeof(Tlv) + GetLength(); |
| } |
| |
| uint8_t *Tlv::GetValue(void) |
| { |
| return reinterpret_cast<uint8_t *>(this) + (IsExtended() ? sizeof(ExtendedTlv) : sizeof(Tlv)); |
| } |
| |
| const uint8_t *Tlv::GetValue(void) const |
| { |
| return reinterpret_cast<const uint8_t *>(this) + (IsExtended() ? sizeof(ExtendedTlv) : sizeof(Tlv)); |
| } |
| |
| Error Tlv::AppendTo(Message &aMessage) const { return aMessage.AppendBytes(this, static_cast<uint16_t>(GetSize())); } |
| |
| Error Tlv::FindTlv(const Message &aMessage, uint8_t aType, uint16_t aMaxSize, Tlv &aTlv) |
| { |
| uint16_t offset; |
| |
| return FindTlv(aMessage, aType, aMaxSize, aTlv, offset); |
| } |
| |
| Error Tlv::FindTlv(const Message &aMessage, uint8_t aType, uint16_t aMaxSize, Tlv &aTlv, uint16_t &aOffset) |
| { |
| Error error; |
| ParsedInfo info; |
| |
| SuccessOrExit(error = info.FindIn(aMessage, aType)); |
| |
| if (aMaxSize > info.mSize) |
| { |
| aMaxSize = info.mSize; |
| } |
| |
| aMessage.ReadBytes(info.mOffset, &aTlv, aMaxSize); |
| aOffset = info.mOffset; |
| |
| exit: |
| return error; |
| } |
| Error Tlv::FindTlvValueOffset(const Message &aMessage, uint8_t aType, uint16_t &aValueOffset, uint16_t &aLength) |
| { |
| Error error; |
| ParsedInfo info; |
| |
| SuccessOrExit(error = info.FindIn(aMessage, aType)); |
| |
| aValueOffset = info.mValueOffset; |
| aLength = info.mLength; |
| |
| exit: |
| return error; |
| } |
| |
| Error Tlv::FindTlvValueStartEndOffsets(const Message &aMessage, |
| uint8_t aType, |
| uint16_t &aValueStartOffset, |
| uint16_t &aValueEndOffset) |
| { |
| Error error; |
| ParsedInfo info; |
| |
| SuccessOrExit(error = info.FindIn(aMessage, aType)); |
| |
| aValueStartOffset = info.mValueOffset; |
| aValueEndOffset = info.mValueOffset + info.mLength; |
| |
| exit: |
| return error; |
| } |
| |
| Error Tlv::ParsedInfo::ParseFrom(const Message &aMessage, uint16_t aOffset) |
| { |
| // This method reads and parses the TLV info from `aMessage` at |
| // `aOffset`. This can be used independent of whether the TLV is |
| // extended or not. It validates that the entire TLV can be read |
| // from `aMessage`. Returns `kErrorNone` when successfully parsed, |
| // otherwise `kErrorParse`. |
| |
| Error error; |
| Tlv tlv; |
| ExtendedTlv extTlv; |
| uint16_t headerSize; |
| |
| SuccessOrExit(error = aMessage.Read(aOffset, tlv)); |
| |
| if (!tlv.IsExtended()) |
| { |
| mType = tlv.GetType(); |
| mLength = tlv.GetLength(); |
| headerSize = sizeof(Tlv); |
| } |
| else |
| { |
| SuccessOrExit(error = aMessage.Read(aOffset, extTlv)); |
| |
| mType = extTlv.GetType(); |
| mLength = extTlv.GetLength(); |
| headerSize = sizeof(ExtendedTlv); |
| } |
| |
| // We know that we could successfully read `tlv` or `extTlv` |
| // (`headerSize` bytes) from the message, so the calculation of the |
| // remaining length as `aMessage.GetLength() - aOffset - headerSize` |
| // cannot underflow. |
| |
| VerifyOrExit(mLength <= aMessage.GetLength() - aOffset - headerSize, error = kErrorParse); |
| |
| // Now that we know the entire TLV is contained within the |
| // `aMessage`, we can safely calculate `mValueOffset` and `mSize` |
| // as `uint16_t` and know that there will be no overflow. |
| |
| mType = tlv.GetType(); |
| mOffset = aOffset; |
| mValueOffset = aOffset + headerSize; |
| mSize = mLength + headerSize; |
| |
| exit: |
| return error; |
| } |
| |
| Error Tlv::ParsedInfo::FindIn(const Message &aMessage, uint8_t aType) |
| { |
| // This method searches within `aMessage` starting from |
| // `aMessage.GetOffset()` for a TLV type `aType` and parsed its |
| // info and validates that the entire TLV can be read from |
| // `aMessage`. Returns `kErrorNone` when found, otherwise |
| // `kErrorNotFound`. |
| |
| Error error = kErrorNotFound; |
| uint16_t offset = aMessage.GetOffset(); |
| |
| while (true) |
| { |
| SuccessOrExit(ParseFrom(aMessage, offset)); |
| |
| if (mType == aType) |
| { |
| error = kErrorNone; |
| ExitNow(); |
| } |
| |
| // `ParseFrom()` already validated that `offset + mSize` is |
| // less than `aMessage.GetLength()` and therefore we can not |
| // have an overflow here. |
| |
| offset += mSize; |
| } |
| |
| exit: |
| return error; |
| } |
| |
| Error Tlv::ReadStringTlv(const Message &aMessage, uint16_t aOffset, uint8_t aMaxStringLength, char *aValue) |
| { |
| Error error = kErrorNone; |
| ParsedInfo info; |
| uint16_t length; |
| |
| SuccessOrExit(error = info.ParseFrom(aMessage, aOffset)); |
| |
| length = Min(info.mLength, static_cast<uint16_t>(aMaxStringLength)); |
| |
| aMessage.ReadBytes(info.mValueOffset, aValue, length); |
| aValue[length] = '\0'; |
| |
| exit: |
| return error; |
| } |
| |
| template <typename UintType> Error Tlv::ReadUintTlv(const Message &aMessage, uint16_t aOffset, UintType &aValue) |
| { |
| Error error; |
| |
| SuccessOrExit(error = ReadTlvValue(aMessage, aOffset, &aValue, sizeof(aValue))); |
| aValue = Encoding::BigEndian::HostSwap<UintType>(aValue); |
| |
| exit: |
| return error; |
| } |
| |
| // Explicit instantiations of `ReadUintTlv<>()` |
| template Error Tlv::ReadUintTlv<uint8_t>(const Message &aMessage, uint16_t aOffset, uint8_t &aValue); |
| template Error Tlv::ReadUintTlv<uint16_t>(const Message &aMessage, uint16_t aOffset, uint16_t &aValue); |
| template Error Tlv::ReadUintTlv<uint32_t>(const Message &aMessage, uint16_t aOffset, uint32_t &aValue); |
| |
| Error Tlv::ReadTlvValue(const Message &aMessage, uint16_t aOffset, void *aValue, uint8_t aMinLength) |
| { |
| Error error; |
| ParsedInfo info; |
| |
| SuccessOrExit(error = info.ParseFrom(aMessage, aOffset)); |
| |
| VerifyOrExit(info.mLength >= aMinLength, error = kErrorParse); |
| |
| aMessage.ReadBytes(info.mValueOffset, aValue, aMinLength); |
| |
| exit: |
| return error; |
| } |
| |
| Error Tlv::FindStringTlv(const Message &aMessage, uint8_t aType, uint8_t aMaxStringLength, char *aValue) |
| { |
| Error error; |
| ParsedInfo info; |
| |
| SuccessOrExit(error = info.FindIn(aMessage, aType)); |
| error = ReadStringTlv(aMessage, info.mOffset, aMaxStringLength, aValue); |
| |
| exit: |
| return error; |
| } |
| |
| template <typename UintType> Error Tlv::FindUintTlv(const Message &aMessage, uint8_t aType, UintType &aValue) |
| { |
| Error error; |
| ParsedInfo info; |
| |
| SuccessOrExit(error = info.FindIn(aMessage, aType)); |
| error = ReadUintTlv<UintType>(aMessage, info.mOffset, aValue); |
| |
| exit: |
| return error; |
| } |
| |
| // Explicit instantiations of `FindUintTlv<>()` |
| template Error Tlv::FindUintTlv<uint8_t>(const Message &aMessage, uint8_t aType, uint8_t &aValue); |
| template Error Tlv::FindUintTlv<uint16_t>(const Message &aMessage, uint8_t aType, uint16_t &aValue); |
| template Error Tlv::FindUintTlv<uint32_t>(const Message &aMessage, uint8_t aType, uint32_t &aValue); |
| |
| Error Tlv::FindTlv(const Message &aMessage, uint8_t aType, void *aValue, uint8_t aLength) |
| { |
| Error error; |
| uint16_t offset; |
| uint16_t length; |
| |
| SuccessOrExit(error = FindTlvValueOffset(aMessage, aType, offset, length)); |
| VerifyOrExit(length >= aLength, error = kErrorParse); |
| aMessage.ReadBytes(offset, aValue, aLength); |
| |
| exit: |
| return error; |
| } |
| |
| Error Tlv::AppendStringTlv(Message &aMessage, uint8_t aType, uint8_t aMaxStringLength, const char *aValue) |
| { |
| uint16_t length = (aValue == nullptr) ? 0 : StringLength(aValue, aMaxStringLength); |
| |
| return AppendTlv(aMessage, aType, aValue, static_cast<uint8_t>(length)); |
| } |
| |
| template <typename UintType> Error Tlv::AppendUintTlv(Message &aMessage, uint8_t aType, UintType aValue) |
| { |
| UintType value = Encoding::BigEndian::HostSwap<UintType>(aValue); |
| |
| return AppendTlv(aMessage, aType, &value, sizeof(UintType)); |
| } |
| |
| // Explicit instantiations of `AppendUintTlv<>()` |
| template Error Tlv::AppendUintTlv<uint8_t>(Message &aMessage, uint8_t aType, uint8_t aValue); |
| template Error Tlv::AppendUintTlv<uint16_t>(Message &aMessage, uint8_t aType, uint16_t aValue); |
| template Error Tlv::AppendUintTlv<uint32_t>(Message &aMessage, uint8_t aType, uint32_t aValue); |
| |
| Error Tlv::AppendTlv(Message &aMessage, uint8_t aType, const void *aValue, uint8_t aLength) |
| { |
| Error error = kErrorNone; |
| Tlv tlv; |
| |
| OT_ASSERT(aLength <= Tlv::kBaseTlvMaxLength); |
| |
| tlv.SetType(aType); |
| tlv.SetLength(aLength); |
| SuccessOrExit(error = aMessage.Append(tlv)); |
| |
| VerifyOrExit(aLength > 0); |
| error = aMessage.AppendBytes(aValue, aLength); |
| |
| exit: |
| return error; |
| } |
| |
| } // namespace ot |