| /* |
| * |
| * Copyright (c) 2020-2023 Project CHIP Authors |
| * Copyright (c) 2013-2017 Nest Labs, Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| #include <lib/core/TLVReader.h> |
| |
| #include <stdint.h> |
| #include <string.h> |
| |
| #include <lib/core/CHIPConfig.h> |
| #include <lib/core/CHIPEncoding.h> |
| #include <lib/core/CHIPError.h> |
| #include <lib/core/CHIPSafeCasts.h> |
| #include <lib/core/DataModelTypes.h> |
| #include <lib/core/Optional.h> |
| #include <lib/core/TLVBackingStore.h> |
| #include <lib/core/TLVCommon.h> |
| #include <lib/core/TLVTags.h> |
| #include <lib/core/TLVTypes.h> |
| #include <lib/support/BufferWriter.h> |
| #include <lib/support/BytesToHex.h> |
| #include <lib/support/CHIPMem.h> |
| #include <lib/support/CodeUtils.h> |
| #include <lib/support/SafeInt.h> |
| #include <lib/support/Span.h> |
| #include <lib/support/logging/TextOnlyLogging.h> |
| |
| #if CHIP_CONFIG_TLV_VALIDATE_CHAR_STRING_ON_READ |
| #include <lib/support/utf8.h> |
| #endif |
| |
| namespace chip { |
| namespace TLV { |
| |
| using namespace chip::Encoding; |
| |
| static const uint8_t sTagSizes[] = { 0, 1, 2, 4, 2, 4, 6, 8 }; |
| |
| TLVReader::TLVReader() : |
| ImplicitProfileId(kProfileIdNotSpecified), AppData(nullptr), mElemLenOrVal(0), mBackingStore(nullptr), mReadPoint(nullptr), |
| mBufEnd(nullptr), mLenRead(0), mMaxLen(0), mContainerType(kTLVType_NotSpecified), mControlByte(kTLVControlByte_NotSpecified), |
| mContainerOpen(false) |
| {} |
| |
| void TLVReader::Init(const uint8_t * data, size_t dataLen) |
| { |
| // TODO: Maybe we can just make mMaxLen and mLenRead size_t instead? |
| uint32_t actualDataLen = dataLen > UINT32_MAX ? UINT32_MAX : static_cast<uint32_t>(dataLen); |
| mBackingStore = nullptr; |
| mReadPoint = data; |
| mBufEnd = data + actualDataLen; |
| mLenRead = 0; |
| mMaxLen = actualDataLen; |
| ClearElementState(); |
| mContainerType = kTLVType_NotSpecified; |
| SetContainerOpen(false); |
| |
| ImplicitProfileId = kProfileIdNotSpecified; |
| } |
| |
| CHIP_ERROR TLVReader::Init(TLVBackingStore & backingStore, uint32_t maxLen) |
| { |
| mBackingStore = &backingStore; |
| mReadPoint = nullptr; |
| uint32_t bufLen = 0; |
| CHIP_ERROR err = mBackingStore->OnInit(*this, mReadPoint, bufLen); |
| if (err != CHIP_NO_ERROR) |
| return err; |
| |
| mBufEnd = mReadPoint + bufLen; |
| mLenRead = 0; |
| mMaxLen = maxLen; |
| ClearElementState(); |
| mContainerType = kTLVType_NotSpecified; |
| SetContainerOpen(false); |
| |
| ImplicitProfileId = kProfileIdNotSpecified; |
| AppData = nullptr; |
| return CHIP_NO_ERROR; |
| } |
| |
| void TLVReader::Init(const TLVReader & aReader) |
| { |
| // Initialize private data members |
| |
| mElemTag = aReader.mElemTag; |
| mElemLenOrVal = aReader.mElemLenOrVal; |
| mBackingStore = aReader.mBackingStore; |
| mReadPoint = aReader.mReadPoint; |
| mBufEnd = aReader.mBufEnd; |
| mLenRead = aReader.mLenRead; |
| mMaxLen = aReader.mMaxLen; |
| mControlByte = aReader.mControlByte; |
| mContainerType = aReader.mContainerType; |
| SetContainerOpen(aReader.IsContainerOpen()); |
| |
| // Initialize public data members |
| |
| ImplicitProfileId = aReader.ImplicitProfileId; |
| AppData = aReader.AppData; |
| } |
| |
| TLVType TLVReader::GetType() const |
| { |
| TLVElementType elemType = ElementType(); |
| if (elemType == TLVElementType::EndOfContainer) |
| return kTLVType_NotSpecified; |
| if (elemType == TLVElementType::FloatingPointNumber32 || elemType == TLVElementType::FloatingPointNumber64) |
| return kTLVType_FloatingPointNumber; |
| if (elemType == TLVElementType::NotSpecified || elemType >= TLVElementType::Null) |
| return static_cast<TLVType>(elemType); |
| return static_cast<TLVType>(static_cast<uint8_t>(elemType) & ~kTLVTypeSizeMask); |
| } |
| |
| uint32_t TLVReader::GetLength() const |
| { |
| if (TLVTypeHasLength(ElementType())) |
| return static_cast<uint32_t>(mElemLenOrVal); |
| return 0; |
| } |
| |
| CHIP_ERROR TLVReader::Get(bool & v) const |
| { |
| TLVElementType elemType = ElementType(); |
| if (elemType == TLVElementType::BooleanFalse) |
| v = false; |
| else if (elemType == TLVElementType::BooleanTrue) |
| v = true; |
| else |
| return CHIP_ERROR_WRONG_TLV_TYPE; |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR TLVReader::Get(int8_t & v) const |
| { |
| int64_t v64 = 0; |
| CHIP_ERROR err = Get(v64); |
| if (!CanCastTo<int8_t>(v64)) |
| { |
| return CHIP_ERROR_INVALID_INTEGER_VALUE; |
| } |
| v = static_cast<int8_t>(v64); |
| return err; |
| } |
| |
| CHIP_ERROR TLVReader::Get(int16_t & v) const |
| { |
| int64_t v64 = 0; |
| CHIP_ERROR err = Get(v64); |
| if (!CanCastTo<int16_t>(v64)) |
| { |
| return CHIP_ERROR_INVALID_INTEGER_VALUE; |
| } |
| v = static_cast<int16_t>(v64); |
| return err; |
| } |
| |
| CHIP_ERROR TLVReader::Get(int32_t & v) const |
| { |
| int64_t v64 = 0; |
| CHIP_ERROR err = Get(v64); |
| if (!CanCastTo<int32_t>(v64)) |
| { |
| return CHIP_ERROR_INVALID_INTEGER_VALUE; |
| } |
| v = static_cast<int32_t>(v64); |
| return err; |
| } |
| |
| CHIP_ERROR TLVReader::Get(int64_t & v) const |
| { |
| // Internal callers of this method depend on it not modifying "v" on failure. |
| switch (ElementType()) |
| { |
| case TLVElementType::Int8: |
| v = CastToSigned(static_cast<uint8_t>(mElemLenOrVal)); |
| break; |
| case TLVElementType::Int16: |
| v = CastToSigned(static_cast<uint16_t>(mElemLenOrVal)); |
| break; |
| case TLVElementType::Int32: |
| v = CastToSigned(static_cast<uint32_t>(mElemLenOrVal)); |
| break; |
| case TLVElementType::Int64: |
| v = CastToSigned(mElemLenOrVal); |
| break; |
| default: |
| return CHIP_ERROR_WRONG_TLV_TYPE; |
| } |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR TLVReader::Get(uint8_t & v) const |
| { |
| uint64_t v64 = 0; |
| CHIP_ERROR err = Get(v64); |
| if (!CanCastTo<uint8_t>(v64)) |
| { |
| return CHIP_ERROR_INVALID_INTEGER_VALUE; |
| } |
| v = static_cast<uint8_t>(v64); |
| return err; |
| } |
| |
| CHIP_ERROR TLVReader::Get(uint16_t & v) const |
| { |
| uint64_t v64 = 0; |
| CHIP_ERROR err = Get(v64); |
| if (!CanCastTo<uint16_t>(v64)) |
| { |
| return CHIP_ERROR_INVALID_INTEGER_VALUE; |
| } |
| v = static_cast<uint16_t>(v64); |
| return err; |
| } |
| |
| CHIP_ERROR TLVReader::Get(uint32_t & v) const |
| { |
| uint64_t v64 = 0; |
| CHIP_ERROR err = Get(v64); |
| if (!CanCastTo<uint32_t>(v64)) |
| { |
| return CHIP_ERROR_INVALID_INTEGER_VALUE; |
| } |
| v = static_cast<uint32_t>(v64); |
| return err; |
| } |
| |
| CHIP_ERROR TLVReader::Get(uint64_t & v) const |
| { |
| // Internal callers of this method depend on it not modifying "v" on failure. |
| switch (ElementType()) |
| { |
| case TLVElementType::UInt8: |
| case TLVElementType::UInt16: |
| case TLVElementType::UInt32: |
| case TLVElementType::UInt64: |
| v = mElemLenOrVal; |
| break; |
| default: |
| return CHIP_ERROR_WRONG_TLV_TYPE; |
| } |
| return CHIP_NO_ERROR; |
| } |
| |
| namespace { |
| float BitCastToFloat(const uint64_t elemLenOrVal) |
| { |
| float f; |
| auto unsigned32 = static_cast<uint32_t>(elemLenOrVal); |
| memcpy(&f, &unsigned32, sizeof(f)); |
| return f; |
| } |
| } // namespace |
| |
| // Note: Unlike the integer Get functions, this code avoids doing conversions |
| // between float and double wherever possible, because these conversions are |
| // relatively expensive on platforms that use soft-float instruction sets. |
| |
| CHIP_ERROR TLVReader::Get(float & v) const |
| { |
| switch (ElementType()) |
| { |
| case TLVElementType::FloatingPointNumber32: { |
| v = BitCastToFloat(mElemLenOrVal); |
| break; |
| } |
| default: |
| return CHIP_ERROR_WRONG_TLV_TYPE; |
| } |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR TLVReader::Get(double & v) const |
| { |
| switch (ElementType()) |
| { |
| case TLVElementType::FloatingPointNumber32: { |
| v = BitCastToFloat(mElemLenOrVal); |
| break; |
| } |
| case TLVElementType::FloatingPointNumber64: { |
| double d; |
| memcpy(&d, &mElemLenOrVal, sizeof(d)); |
| v = d; |
| break; |
| } |
| default: |
| return CHIP_ERROR_WRONG_TLV_TYPE; |
| } |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR TLVReader::Get(ByteSpan & v) const |
| { |
| const uint8_t * val; |
| ReturnErrorOnFailure(GetDataPtr(val)); |
| v = ByteSpan(val, GetLength()); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| namespace { |
| constexpr int kUnicodeInformationSeparator1 = 0x1F; |
| constexpr size_t kMaxLocalizedStringIdentifierLen = 2 * sizeof(LocalizedStringIdentifier); |
| } // namespace |
| |
| CHIP_ERROR TLVReader::Get(CharSpan & v) const |
| { |
| if (!TLVTypeIsUTF8String(ElementType())) |
| { |
| return CHIP_ERROR_WRONG_TLV_TYPE; |
| } |
| |
| const uint8_t * bytes; |
| ReturnErrorOnFailure(GetDataPtr(bytes)); // Does length sanity checks |
| if (bytes == nullptr) |
| { |
| // Calling memchr further down with bytes == nullptr would have undefined behaviour, exiting early. |
| return CHIP_NO_ERROR; |
| } |
| |
| uint32_t len = GetLength(); |
| |
| // If Unicode Information Separator 1 (0x1f) is present in the string then method returns |
| // string ending at first appearance of the Information Separator 1. |
| const uint8_t * infoSeparator = reinterpret_cast<const uint8_t *>(memchr(bytes, kUnicodeInformationSeparator1, len)); |
| if (infoSeparator != nullptr) |
| { |
| len = static_cast<uint32_t>(infoSeparator - bytes); |
| } |
| |
| v = CharSpan(Uint8::to_const_char(bytes), len); |
| #if CHIP_CONFIG_TLV_VALIDATE_CHAR_STRING_ON_READ |
| // Spec requirement: A.11.2. UTF-8 and Octet Strings |
| // |
| // For UTF-8 strings, the value octets SHALL encode a valid |
| // UTF-8 character (code points) sequence. |
| // |
| // Senders SHALL NOT include a terminating null character to |
| // mark the end of a string. |
| |
| if (!Utf8::IsValid(v)) |
| { |
| return CHIP_ERROR_INVALID_UTF8; |
| } |
| |
| if (!v.empty() && (v.back() == 0)) |
| { |
| return CHIP_ERROR_INVALID_TLV_CHAR_STRING; |
| } |
| #endif // CHIP_CONFIG_TLV_VALIDATE_CHAR_STRING_ON_READ |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR TLVReader::Get(Optional<LocalizedStringIdentifier> & lsid) |
| { |
| lsid.ClearValue(); |
| VerifyOrReturnError(TLVTypeIsUTF8String(ElementType()), CHIP_ERROR_WRONG_TLV_TYPE); |
| |
| const uint8_t * bytes; |
| ReturnErrorOnFailure(GetDataPtr(bytes)); // Does length sanity checks |
| if (bytes == nullptr) |
| { |
| // Calling memchr further down with bytes == nullptr would have undefined behaviour, exiting early. |
| return CHIP_NO_ERROR; |
| } |
| |
| uint32_t len = GetLength(); |
| |
| const uint8_t * infoSeparator1 = static_cast<const uint8_t *>(memchr(bytes, kUnicodeInformationSeparator1, len)); |
| if (infoSeparator1 == nullptr) |
| { |
| return CHIP_NO_ERROR; |
| } |
| |
| const uint8_t * lsidPtr = infoSeparator1 + 1; |
| len -= static_cast<uint32_t>(lsidPtr - bytes); |
| |
| const uint8_t * infoSeparator2 = static_cast<const uint8_t *>(memchr(lsidPtr, kUnicodeInformationSeparator1, len)); |
| if (infoSeparator2 != nullptr) |
| { |
| len = static_cast<uint32_t>(infoSeparator2 - lsidPtr); |
| } |
| if (len == 0) |
| { |
| return CHIP_NO_ERROR; |
| } |
| VerifyOrReturnError(len <= kMaxLocalizedStringIdentifierLen, CHIP_ERROR_INVALID_TLV_ELEMENT); |
| // Leading zeroes are not allowed. |
| VerifyOrReturnError(static_cast<char>(lsidPtr[0]) != '0', CHIP_ERROR_INVALID_TLV_ELEMENT); |
| |
| char idStr[kMaxLocalizedStringIdentifierLen] = { '0', '0', '0', '0' }; |
| memcpy(&idStr[kMaxLocalizedStringIdentifierLen - len], lsidPtr, len); |
| |
| LocalizedStringIdentifier id; |
| VerifyOrReturnError(Encoding::UppercaseHexToUint16(idStr, sizeof(idStr), id) == sizeof(LocalizedStringIdentifier), |
| CHIP_ERROR_INVALID_TLV_ELEMENT); |
| |
| lsid.SetValue(id); |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR TLVReader::GetBytes(uint8_t * buf, size_t bufSize) |
| { |
| if (!TLVTypeIsString(ElementType())) |
| return CHIP_ERROR_WRONG_TLV_TYPE; |
| |
| if (mElemLenOrVal > bufSize) |
| return CHIP_ERROR_BUFFER_TOO_SMALL; |
| |
| CHIP_ERROR err = ReadData(buf, static_cast<uint32_t>(mElemLenOrVal)); |
| if (err != CHIP_NO_ERROR) |
| return err; |
| |
| mElemLenOrVal = 0; |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR TLVReader::GetString(char * buf, size_t bufSize) |
| { |
| if (!TLVTypeIsString(ElementType())) |
| return CHIP_ERROR_WRONG_TLV_TYPE; |
| |
| if ((mElemLenOrVal + 1) > bufSize) |
| return CHIP_ERROR_BUFFER_TOO_SMALL; |
| |
| buf[mElemLenOrVal] = 0; |
| |
| return GetBytes(reinterpret_cast<uint8_t *>(buf), bufSize - 1); |
| } |
| |
| CHIP_ERROR TLVReader::DupBytes(uint8_t *& buf, uint32_t & dataLen) |
| { |
| if (!TLVTypeIsString(ElementType())) |
| return CHIP_ERROR_WRONG_TLV_TYPE; |
| |
| buf = static_cast<uint8_t *>(chip::Platform::MemoryAlloc(static_cast<uint32_t>(mElemLenOrVal))); |
| if (buf == nullptr) |
| return CHIP_ERROR_NO_MEMORY; |
| |
| CHIP_ERROR err = ReadData(buf, static_cast<uint32_t>(mElemLenOrVal)); |
| if (err != CHIP_NO_ERROR) |
| { |
| chip::Platform::MemoryFree(buf); |
| buf = nullptr; |
| return err; |
| } |
| |
| dataLen = static_cast<uint32_t>(mElemLenOrVal); |
| mElemLenOrVal = 0; |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR TLVReader::DupString(char *& buf) |
| { |
| if (!TLVTypeIsString(ElementType())) |
| return CHIP_ERROR_WRONG_TLV_TYPE; |
| |
| if (mElemLenOrVal > UINT32_MAX - 1) |
| return CHIP_ERROR_NO_MEMORY; |
| |
| buf = static_cast<char *>(chip::Platform::MemoryAlloc(static_cast<uint32_t>(mElemLenOrVal + 1))); |
| if (buf == nullptr) |
| return CHIP_ERROR_NO_MEMORY; |
| |
| CHIP_ERROR err = ReadData(reinterpret_cast<uint8_t *>(buf), static_cast<uint32_t>(mElemLenOrVal)); |
| if (err != CHIP_NO_ERROR) |
| { |
| chip::Platform::MemoryFree(buf); |
| buf = nullptr; |
| return err; |
| } |
| |
| buf[mElemLenOrVal] = 0; |
| mElemLenOrVal = 0; |
| |
| return err; |
| } |
| |
| CHIP_ERROR TLVReader::GetDataPtr(const uint8_t *& data) const |
| { |
| VerifyOrReturnError(TLVTypeIsString(ElementType()), CHIP_ERROR_WRONG_TLV_TYPE); |
| |
| if (GetLength() == 0) |
| { |
| data = nullptr; |
| return CHIP_NO_ERROR; |
| } |
| |
| uint32_t remainingLen = static_cast<decltype(mMaxLen)>(mBufEnd - mReadPoint); |
| |
| // Verify that the entirety of the data is available in the buffer. |
| // Note that this may not be possible if the reader is reading from a chain of buffers. |
| VerifyOrReturnError(remainingLen >= static_cast<uint32_t>(mElemLenOrVal), CHIP_ERROR_TLV_UNDERRUN); |
| data = mReadPoint; |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR TLVReader::OpenContainer(TLVReader & containerReader) |
| { |
| TLVElementType elemType = ElementType(); |
| if (!TLVTypeIsContainer(elemType)) |
| return CHIP_ERROR_INCORRECT_STATE; |
| |
| containerReader.mBackingStore = mBackingStore; |
| containerReader.mReadPoint = mReadPoint; |
| containerReader.mBufEnd = mBufEnd; |
| containerReader.mLenRead = mLenRead; |
| containerReader.mMaxLen = mMaxLen; |
| containerReader.ClearElementState(); |
| containerReader.mContainerType = static_cast<TLVType>(elemType); |
| containerReader.SetContainerOpen(false); |
| containerReader.ImplicitProfileId = ImplicitProfileId; |
| containerReader.AppData = AppData; |
| |
| SetContainerOpen(true); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR TLVReader::CloseContainer(TLVReader & containerReader) |
| { |
| CHIP_ERROR err; |
| |
| if (!IsContainerOpen()) |
| return CHIP_ERROR_INCORRECT_STATE; |
| |
| if (static_cast<TLVElementType>(containerReader.mContainerType) != ElementType()) |
| return CHIP_ERROR_INCORRECT_STATE; |
| |
| err = containerReader.SkipToEndOfContainer(); |
| if (err != CHIP_NO_ERROR) |
| return err; |
| |
| mBackingStore = containerReader.mBackingStore; |
| mReadPoint = containerReader.mReadPoint; |
| mBufEnd = containerReader.mBufEnd; |
| mLenRead = containerReader.mLenRead; |
| mMaxLen = containerReader.mMaxLen; |
| ClearElementState(); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR TLVReader::EnterContainer(TLVType & outerContainerType) |
| { |
| TLVElementType elemType = ElementType(); |
| if (!TLVTypeIsContainer(elemType)) |
| return CHIP_ERROR_INCORRECT_STATE; |
| |
| outerContainerType = mContainerType; |
| mContainerType = static_cast<TLVType>(elemType); |
| |
| ClearElementState(); |
| SetContainerOpen(false); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR TLVReader::ExitContainer(TLVType outerContainerType) |
| { |
| CHIP_ERROR err; |
| |
| err = SkipToEndOfContainer(); |
| if (err != CHIP_NO_ERROR) |
| return err; |
| |
| mContainerType = outerContainerType; |
| ClearElementState(); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR TLVReader::VerifyEndOfContainer() |
| { |
| CHIP_ERROR err = Next(); |
| if (err == CHIP_END_OF_TLV) |
| return CHIP_NO_ERROR; |
| if (err == CHIP_NO_ERROR) |
| return CHIP_ERROR_UNEXPECTED_TLV_ELEMENT; |
| return err; |
| } |
| |
| CHIP_ERROR TLVReader::Next() |
| { |
| ReturnErrorOnFailure(Skip()); |
| ReturnErrorOnFailure(ReadElement()); |
| |
| TLVElementType elemType = ElementType(); |
| |
| VerifyOrReturnError(elemType != TLVElementType::EndOfContainer, CHIP_END_OF_TLV); |
| |
| // Ensure that GetDataPtr calls can be called immediately after Next, so |
| // that `Get(ByteSpan&)` does not need to advance buffers and just works |
| if (TLVTypeIsString(elemType) && (GetLength() != 0)) |
| { |
| ReturnErrorOnFailure(EnsureData(CHIP_ERROR_TLV_UNDERRUN)); |
| } |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR TLVReader::Expect(Tag expectedTag) |
| { |
| VerifyOrReturnError(GetType() != kTLVType_NotSpecified, CHIP_ERROR_WRONG_TLV_TYPE); |
| VerifyOrReturnError(GetTag() == expectedTag, CHIP_ERROR_UNEXPECTED_TLV_ELEMENT); |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR TLVReader::Next(Tag expectedTag) |
| { |
| ReturnErrorOnFailure(Next()); |
| ReturnErrorOnFailure(Expect(expectedTag)); |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR TLVReader::Expect(TLVType expectedType, Tag expectedTag) |
| { |
| VerifyOrReturnError(GetType() == expectedType, CHIP_ERROR_WRONG_TLV_TYPE); |
| VerifyOrReturnError(GetTag() == expectedTag, CHIP_ERROR_UNEXPECTED_TLV_ELEMENT); |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR TLVReader::Next(TLVType expectedType, Tag expectedTag) |
| { |
| ReturnErrorOnFailure(Next()); |
| ReturnErrorOnFailure(Expect(expectedType, expectedTag)); |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR TLVReader::Skip() |
| { |
| CHIP_ERROR err; |
| TLVElementType elemType = ElementType(); |
| |
| if (elemType == TLVElementType::EndOfContainer) |
| return CHIP_END_OF_TLV; |
| |
| if (TLVTypeIsContainer(elemType)) |
| { |
| TLVType outerContainerType; |
| err = EnterContainer(outerContainerType); |
| if (err != CHIP_NO_ERROR) |
| return err; |
| err = ExitContainer(outerContainerType); |
| if (err != CHIP_NO_ERROR) |
| return err; |
| } |
| |
| else |
| { |
| err = SkipData(); |
| if (err != CHIP_NO_ERROR) |
| return err; |
| |
| ClearElementState(); |
| } |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| /** |
| * Clear the state of the TLVReader. |
| * This method is used to position the reader before the first TLV, |
| * between TLVs or after the last TLV. |
| */ |
| void TLVReader::ClearElementState() |
| { |
| mElemTag = AnonymousTag(); |
| mControlByte = kTLVControlByte_NotSpecified; |
| mElemLenOrVal = 0; |
| } |
| |
| /** |
| * Skip any data contained in the current TLV by reading over it without |
| * a destination buffer. |
| * |
| * @retval #CHIP_NO_ERROR If the reader was successfully positioned at the end of the |
| * data. |
| * @retval other Other CHIP or platform error codes returned by the configured |
| * TLVBackingStore. |
| */ |
| CHIP_ERROR TLVReader::SkipData() |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| TLVElementType elemType = ElementType(); |
| |
| if (TLVTypeHasLength(elemType)) |
| { |
| err = ReadData(nullptr, static_cast<uint32_t>(mElemLenOrVal)); |
| if (err != CHIP_NO_ERROR) |
| return err; |
| } |
| |
| return err; |
| } |
| |
| CHIP_ERROR TLVReader::SkipToEndOfContainer() |
| { |
| CHIP_ERROR err; |
| TLVType outerContainerType = mContainerType; |
| uint32_t nestLevel = 0; |
| |
| // If the user calls Next() after having called OpenContainer() but before calling |
| // CloseContainer() they're effectively doing a close container by skipping over |
| // the container element. So reset the 'container open' flag here to prevent them |
| // from calling CloseContainer() with the now orphaned container reader. |
| SetContainerOpen(false); |
| |
| while (true) |
| { |
| TLVElementType elemType = ElementType(); |
| |
| if (elemType == TLVElementType::EndOfContainer) |
| { |
| if (nestLevel == 0) |
| return CHIP_NO_ERROR; |
| |
| nestLevel--; |
| mContainerType = (nestLevel == 0) ? outerContainerType : kTLVType_UnknownContainer; |
| } |
| |
| else if (TLVTypeIsContainer(elemType)) |
| { |
| nestLevel++; |
| mContainerType = static_cast<TLVType>(elemType); |
| } |
| |
| err = SkipData(); |
| if (err != CHIP_NO_ERROR) |
| return err; |
| |
| err = ReadElement(); |
| if (err != CHIP_NO_ERROR) |
| return err; |
| } |
| } |
| |
| CHIP_ERROR TLVReader::ReadElement() |
| { |
| CHIP_ERROR err; |
| uint8_t stagingBuf[17]; // 17 = 1 control byte + 8 tag bytes + 8 length/value bytes |
| const uint8_t * p; |
| TLVElementType elemType; |
| |
| // Make sure we have input data. Return CHIP_END_OF_TLV if no more data is available. |
| err = EnsureData(CHIP_END_OF_TLV); |
| if (err != CHIP_NO_ERROR) |
| return err; |
| |
| if (mReadPoint == nullptr) |
| { |
| return CHIP_ERROR_INVALID_TLV_ELEMENT; |
| } |
| // Get the element's control byte. |
| mControlByte = *mReadPoint; |
| |
| // Extract the element type from the control byte. Fail if it's invalid. |
| elemType = ElementType(); |
| if (!IsValidTLVType(elemType)) |
| return CHIP_ERROR_INVALID_TLV_ELEMENT; |
| |
| // Extract the tag control from the control byte. |
| TLVTagControl tagControl = static_cast<TLVTagControl>(mControlByte & kTLVTagControlMask); |
| |
| // Determine the number of bytes in the element's tag, if any. |
| uint8_t tagBytes = sTagSizes[tagControl >> kTLVTagControlShift]; |
| |
| // Extract the size of length/value field from the control byte. |
| TLVFieldSize lenOrValFieldSize = GetTLVFieldSize(elemType); |
| |
| // Determine the number of bytes in the length/value field. |
| uint8_t valOrLenBytes = TLVFieldSizeToBytes(lenOrValFieldSize); |
| |
| // Determine the number of bytes in the element's 'head'. This includes: the control byte, the tag bytes (if present), the |
| // length bytes (if present), and for elements that don't have a length (e.g. integers), the value bytes. |
| uint8_t elemHeadBytes = static_cast<uint8_t>(1 + tagBytes + valOrLenBytes); |
| |
| // If the head of the element overlaps the end of the input buffer, read the bytes into the staging buffer |
| // and arrange to parse them from there. Otherwise read them directly from the input buffer. |
| if (elemHeadBytes > (mBufEnd - mReadPoint)) |
| { |
| err = ReadData(stagingBuf, elemHeadBytes); |
| if (err != CHIP_NO_ERROR) |
| return err; |
| p = stagingBuf; |
| } |
| else |
| { |
| p = mReadPoint; |
| mReadPoint += elemHeadBytes; |
| mLenRead += elemHeadBytes; |
| } |
| |
| // Skip over the control byte. |
| p++; |
| |
| // Read the tag field, if present. |
| mElemTag = ReadTag(tagControl, p); |
| |
| // Read the length/value field, if present. |
| switch (lenOrValFieldSize) |
| { |
| case kTLVFieldSize_0Byte: |
| mElemLenOrVal = 0; |
| break; |
| case kTLVFieldSize_1Byte: |
| mElemLenOrVal = Read8(p); |
| break; |
| case kTLVFieldSize_2Byte: |
| mElemLenOrVal = LittleEndian::Read16(p); |
| break; |
| case kTLVFieldSize_4Byte: |
| mElemLenOrVal = LittleEndian::Read32(p); |
| break; |
| case kTLVFieldSize_8Byte: |
| mElemLenOrVal = LittleEndian::Read64(p); |
| VerifyOrReturnError(!TLVTypeHasLength(elemType) || (mElemLenOrVal <= UINT32_MAX), CHIP_ERROR_NOT_IMPLEMENTED); |
| break; |
| } |
| |
| return VerifyElement(); |
| } |
| |
| CHIP_ERROR TLVReader::VerifyElement() |
| { |
| if (ElementType() == TLVElementType::EndOfContainer) |
| { |
| if (mContainerType == kTLVType_NotSpecified) |
| return CHIP_ERROR_INVALID_TLV_ELEMENT; |
| if (mElemTag != AnonymousTag()) |
| return CHIP_ERROR_INVALID_TLV_TAG; |
| } |
| else |
| { |
| if (mElemTag == UnknownImplicitTag()) |
| return CHIP_ERROR_UNKNOWN_IMPLICIT_TLV_TAG; |
| switch (mContainerType) |
| { |
| case kTLVType_NotSpecified: |
| if (IsContextTag(mElemTag)) |
| return CHIP_ERROR_INVALID_TLV_TAG; |
| break; |
| case kTLVType_Structure: |
| if (mElemTag == AnonymousTag()) |
| return CHIP_ERROR_INVALID_TLV_TAG; |
| break; |
| case kTLVType_Array: |
| if (mElemTag != AnonymousTag()) |
| return CHIP_ERROR_INVALID_TLV_TAG; |
| break; |
| case kTLVType_UnknownContainer: |
| case kTLVType_List: |
| break; |
| default: |
| return CHIP_ERROR_INCORRECT_STATE; |
| } |
| } |
| |
| // If the current element encodes a specific length (e.g. a UTF8 string or a byte string), verify |
| // that the purported length fits within the remaining bytes of the encoding (as delineated by mMaxLen). |
| // |
| // Note that this check is not strictly necessary to prevent runtime errors, as any attempt to access |
| // the data of an element with an invalid length will result in an error. However checking the length |
| // here catches the error earlier, and ensures that the application will never see the erroneous length |
| // value. |
| // |
| if (TLVTypeHasLength(ElementType())) |
| { |
| uint32_t overallLenRemaining = mMaxLen - mLenRead; |
| if (overallLenRemaining < static_cast<uint32_t>(mElemLenOrVal)) |
| return CHIP_ERROR_TLV_UNDERRUN; |
| } |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| Tag TLVReader::ReadTag(TLVTagControl tagControl, const uint8_t *& p) const |
| { |
| uint16_t vendorId; |
| uint16_t profileNum; |
| |
| switch (tagControl) |
| { |
| case TLVTagControl::ContextSpecific: |
| return ContextTag(Read8(p)); |
| case TLVTagControl::CommonProfile_2Bytes: |
| return CommonTag(LittleEndian::Read16(p)); |
| case TLVTagControl::CommonProfile_4Bytes: |
| return CommonTag(LittleEndian::Read32(p)); |
| case TLVTagControl::ImplicitProfile_2Bytes: |
| if (ImplicitProfileId == kProfileIdNotSpecified) |
| return UnknownImplicitTag(); |
| return ProfileTag(ImplicitProfileId, LittleEndian::Read16(p)); |
| case TLVTagControl::ImplicitProfile_4Bytes: |
| if (ImplicitProfileId == kProfileIdNotSpecified) |
| return UnknownImplicitTag(); |
| return ProfileTag(ImplicitProfileId, LittleEndian::Read32(p)); |
| case TLVTagControl::FullyQualified_6Bytes: |
| vendorId = LittleEndian::Read16(p); |
| profileNum = LittleEndian::Read16(p); |
| return ProfileTag(vendorId, profileNum, LittleEndian::Read16(p)); |
| case TLVTagControl::FullyQualified_8Bytes: |
| vendorId = LittleEndian::Read16(p); |
| profileNum = LittleEndian::Read16(p); |
| return ProfileTag(vendorId, profileNum, LittleEndian::Read32(p)); |
| case TLVTagControl::Anonymous: |
| default: |
| return AnonymousTag(); |
| } |
| } |
| |
| CHIP_ERROR TLVReader::ReadData(uint8_t * buf, uint32_t len) |
| { |
| CHIP_ERROR err; |
| |
| while (len > 0) |
| { |
| err = EnsureData(CHIP_ERROR_TLV_UNDERRUN); |
| if (err != CHIP_NO_ERROR) |
| return err; |
| |
| uint32_t remainingLen = static_cast<decltype(mMaxLen)>(mBufEnd - mReadPoint); |
| |
| uint32_t readLen = len; |
| if (readLen > remainingLen) |
| readLen = remainingLen; |
| |
| if (buf != nullptr) |
| { |
| memcpy(buf, mReadPoint, readLen); |
| buf += readLen; |
| } |
| mReadPoint += readLen; |
| mLenRead += readLen; |
| len -= readLen; |
| } |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR TLVReader::EnsureData(CHIP_ERROR noDataErr) |
| { |
| CHIP_ERROR err; |
| |
| if (mReadPoint == mBufEnd) |
| { |
| if (mLenRead == mMaxLen) |
| return noDataErr; |
| |
| if (mBackingStore == nullptr) |
| return noDataErr; |
| |
| uint32_t bufLen; |
| err = mBackingStore->GetNextBuffer(*this, mReadPoint, bufLen); |
| if (err != CHIP_NO_ERROR) |
| return err; |
| if (bufLen == 0) |
| return noDataErr; |
| |
| // Cap mBufEnd so that we don't read beyond the user's specified maximum length, even |
| // if the underlying buffer is larger. |
| uint32_t overallLenRemaining = mMaxLen - mLenRead; |
| if (overallLenRemaining < bufLen) |
| bufLen = overallLenRemaining; |
| |
| mBufEnd = mReadPoint + bufLen; |
| } |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| /** |
| * This is a private method used to compute the length of a TLV element head. |
| */ |
| CHIP_ERROR TLVReader::GetElementHeadLength(uint8_t & elemHeadBytes) const |
| { |
| uint8_t tagBytes; |
| uint8_t valOrLenBytes; |
| TLVTagControl tagControl; |
| TLVFieldSize lenOrValFieldSize; |
| TLVElementType elemType = ElementType(); |
| |
| // Verify element is of valid TLVType. |
| VerifyOrReturnError(IsValidTLVType(elemType), CHIP_ERROR_INVALID_TLV_ELEMENT); |
| |
| // Extract the tag control from the control byte. |
| tagControl = static_cast<TLVTagControl>(mControlByte & kTLVTagControlMask); |
| |
| // Determine the number of bytes in the element's tag, if any. |
| tagBytes = sTagSizes[tagControl >> kTLVTagControlShift]; |
| |
| // Extract the size of length/value field from the control byte. |
| lenOrValFieldSize = GetTLVFieldSize(elemType); |
| |
| // Determine the number of bytes in the length/value field. |
| valOrLenBytes = TLVFieldSizeToBytes(lenOrValFieldSize); |
| |
| // Determine the number of bytes in the element's 'head'. This includes: the |
| // control byte, the tag bytes (if present), the length bytes (if present), |
| // and for elements that don't have a length (e.g. integers), the value |
| // bytes. |
| VerifyOrReturnError(CanCastTo<uint8_t>(1 + tagBytes + valOrLenBytes), CHIP_ERROR_INTERNAL); |
| elemHeadBytes = static_cast<uint8_t>(1 + tagBytes + valOrLenBytes); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| /** |
| * This is a private method that returns the TLVElementType from mControlByte |
| */ |
| TLVElementType TLVReader::ElementType() const |
| { |
| if (mControlByte == static_cast<uint16_t>(kTLVControlByte_NotSpecified)) |
| return TLVElementType::NotSpecified; |
| return static_cast<TLVElementType>(mControlByte & kTLVTypeMask); |
| } |
| |
| CHIP_ERROR TLVReader::FindElementWithTag(Tag tag, TLVReader & destReader) const |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| chip::TLV::TLVReader reader; |
| reader.Init(*this); |
| |
| while (CHIP_NO_ERROR == (err = reader.Next())) |
| { |
| VerifyOrExit(chip::TLV::kTLVType_NotSpecified != reader.GetType(), err = CHIP_ERROR_INVALID_TLV_ELEMENT); |
| |
| if (tag == reader.GetTag()) |
| { |
| destReader.Init(reader); |
| break; |
| } |
| } |
| |
| exit: |
| ChipLogIfFalse((CHIP_NO_ERROR == err) || (CHIP_END_OF_TLV == err)); |
| |
| return err; |
| } |
| |
| CHIP_ERROR TLVReader::CountRemainingInContainer(size_t * size) const |
| { |
| if (mContainerType == kTLVType_NotSpecified) |
| { |
| return CHIP_ERROR_INCORRECT_STATE; |
| } |
| |
| TLVReader tempReader(*this); |
| size_t count = 0; |
| CHIP_ERROR err; |
| while ((err = tempReader.Next()) == CHIP_NO_ERROR) |
| { |
| ++count; |
| }; |
| if (err == CHIP_END_OF_TLV) |
| { |
| *size = count; |
| return CHIP_NO_ERROR; |
| } |
| return err; |
| } |
| |
| CHIP_ERROR ContiguousBufferTLVReader::OpenContainer(ContiguousBufferTLVReader & containerReader) |
| { |
| // We are going to initialize containerReader by calling our superclass |
| // OpenContainer method. The superclass only knows how to initialize |
| // members the superclass knows about, so we assert that we don't have any |
| // extra members that need initializing. If such members ever get added, |
| // they would need to be initialized in this method. |
| static_assert(sizeof(ContiguousBufferTLVReader) == sizeof(TLVReader), "We have state the superclass is not initializing?"); |
| return TLVReader::OpenContainer(containerReader); |
| } |
| |
| CHIP_ERROR ContiguousBufferTLVReader::GetStringView(Span<const char> & data) |
| { |
| return Get(data); |
| } |
| |
| CHIP_ERROR ContiguousBufferTLVReader::GetByteView(ByteSpan & data) |
| { |
| if (!TLVTypeIsByteString(ElementType())) |
| { |
| return CHIP_ERROR_WRONG_TLV_TYPE; |
| } |
| |
| return Get(data); |
| } |
| |
| } // namespace TLV |
| } // namespace chip |