/* * * Copyright (c) 2020 Project CHIP Authors * Copyright (c) 2015-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. */ /** * @file * This file implements utility interfaces for managing and * working with CHIP TLV. * */ #include #include #include #include #include #include namespace chip { namespace TLV { namespace Utilities { namespace { // Sets up a limit on recursion depth, to avoid any stack overflows // on very deep TLV structures. Embedded has limited stack space. constexpr size_t kMaxRecursionDepth = 10; } // namespace struct FindContext { const Tag & mTag; TLVReader & mReader; }; /** * Iterate through the TLV data referenced by @a aReader and invoke @a aHandler * for each visited TLV element in the context of @a aContext. * The iteration is aborted if @a aHandler returns anything other than #CHIP_NO_ERROR * * @param[in] aReader A reference to the TLV reader containing the TLV * data to iterate. * @param[in] aDepth The current depth into the TLV data. * @param[in] aHandler A callback to invoke for the current TLV element * being visited. * @param[in,out] aContext An optional pointer to caller-provided context data. * @param[in] aRecurse A Boolean indicating whether (true) or not (false) * any encountered arrays or structures should be * descended into. * * @retval #CHIP_END_OF_TLV On a successful iteration to the end of a TLV encoding, * or to the end of a TLV container. * * @retval The last value returned by @a aHandler, if different than #CHIP_NO_ERROR */ static CHIP_ERROR Iterate(TLVReader & aReader, size_t aDepth, IterateHandler aHandler, void * aContext, bool aRecurse) { CHIP_ERROR retval = CHIP_NO_ERROR; if (aDepth >= kMaxRecursionDepth) { return CHIP_ERROR_RECURSION_DEPTH_LIMIT; } if (aReader.GetType() == kTLVType_NotSpecified) { ReturnErrorOnFailure(aReader.Next()); } do { const TLVType theType = aReader.GetType(); ReturnErrorOnFailure((aHandler) (aReader, aDepth, aContext)); if (aRecurse && TLVTypeIsContainer(theType)) { TLVType containerType; ReturnErrorOnFailure(aReader.EnterContainer(containerType)); retval = Iterate(aReader, aDepth + 1, aHandler, aContext, aRecurse); if ((retval != CHIP_END_OF_TLV) && (retval != CHIP_NO_ERROR)) { return retval; } ReturnErrorOnFailure(aReader.ExitContainer(containerType)); } } while ((retval = aReader.Next()) == CHIP_NO_ERROR); return retval; } /** * Iterate through the TLV data referenced by @a aReader and invoke @a aHandler * for each visited TLV element in the context of @a aContext. * The iteration is aborted if @a aHandler returns anything other than #CHIP_NO_ERROR * * @param[in] aReader A reference to the TLV reader containing the TLV * data to iterate. * @param[in] aHandler A callback to invoke for the current TLV element * being visited. * @param[in,out] aContext An optional pointer to caller-provided context data. * * @retval #CHIP_END_OF_TLV On a successful iteration to the end of a TLV encoding, * or to the end of a TLV container. * * @retval #CHIP_ERROR_INVALID_ARGUMENT If @a aHandler is NULL. * * @retval The last value returned by @a aHandler, if different than #CHIP_NO_ERROR * */ CHIP_ERROR Iterate(const TLVReader & aReader, IterateHandler aHandler, void * aContext) { const bool recurse = true; CHIP_ERROR retval; retval = Iterate(aReader, aHandler, aContext, recurse); return retval; } /** * Iterate through the TLV data referenced by @a aReader and invoke @a aHandler * for each visited TLV element in the context of @a aContext. * The iteration is aborted if @a aHandler returns anything other than #CHIP_NO_ERROR * * @param[in] aReader A reference to the TLV reader containing the TLV * data to iterate. * @param[in] aHandler A callback to invoke for the current TLV element * being visited. * @param[in,out] aContext An optional pointer to caller-provided context data. * @param[in] aRecurse A Boolean indicating whether (true) or not (false) * any encountered arrays or structures should be * descended into. * * @retval #CHIP_END_OF_TLV On a successful iteration to the end of a TLV encoding, * or to the end of a TLV container. * * @retval #CHIP_ERROR_INVALID_ARGUMENT If @a aHandler is NULL. * * @retval The last value returned by @a aHandler, if different than #CHIP_NO_ERROR * */ CHIP_ERROR Iterate(const TLVReader & aReader, IterateHandler aHandler, void * aContext, const bool aRecurse) { VerifyOrReturnError(aHandler != nullptr, CHIP_ERROR_INVALID_ARGUMENT); TLVReader temp; temp.Init(aReader); constexpr size_t depth = 0; return Iterate(temp, depth, aHandler, aContext, aRecurse); } /** * Increment the counter when iterating through the TLV data. * * @param[in] aReader A reference to the TLV reader containing the TLV * data to count the number of TLV elements. * @param[in] aDepth The current depth into the TLV data. * @param[in,out] aContext A pointer to the handler-specific context which * is a pointer to storage for the count value. * * @retval #CHIP_NO_ERROR On success. * * @retval #CHIP_ERROR_INVALID_ARGUMENT If @a aContext is NULL. * */ static CHIP_ERROR CountHandler(const TLVReader & aReader, size_t aDepth, void * aContext) { VerifyOrReturnError(aContext != nullptr, CHIP_ERROR_INVALID_ARGUMENT); *static_cast(aContext) += 1; return CHIP_NO_ERROR; } /** * Count the number of TLV elements within the specified TLV reader, * descending into arrays or structures. * * @param[in] aReader A read-only reference to the TLV reader for * which to count the number of TLV elements. * @param[in,out] aCount A reference to storage for the returned count. * This is initialized to zero (0) prior to counting * and is set to the number of elements counted on * success. * * @retval #CHIP_NO_ERROR On success. * */ CHIP_ERROR Count(const TLVReader & aReader, size_t & aCount) { constexpr bool recurse = true; return Count(aReader, aCount, recurse); } /** * Count the number of TLV elements within the specified TLV reader, * optionally descending into arrays or structures. * * @param[in] aReader A read-only reference to the TLV reader for * which to count the number of TLV elements. * @param[in,out] aCount A reference to storage for the returned count. * This is initialized to zero (0) prior to counting * and is set to the number of elements counted on * success. * @param[in] aRecurse A Boolean indicating whether (true) or not (false) * any encountered arrays or structures should be * descended into. * * @retval #CHIP_NO_ERROR On success. * */ CHIP_ERROR Count(const TLVReader & aReader, size_t & aCount, const bool aRecurse) { aCount = 0; CHIP_ERROR retval = Iterate(aReader, CountHandler, &aCount, aRecurse); if (retval == CHIP_END_OF_TLV) retval = CHIP_NO_ERROR; return retval; } /** * Search for the specified tag within the provided TLV reader. * * @param[in] aReader A read-only reference to the TLV reader in * which to find the specified tag. * @param[in] aDepth The current depth into the TLV data. * @param[in,out] aContext A pointer to the handler-specific context. * * @retval #CHIP_NO_ERROR On success. * * @retval #CHIP_ERROR_INVALID_ARGUMENT If @a aContext is NULL. * * @retval #CHIP_ERROR_SENTINEL If the specified tag is found. * */ static CHIP_ERROR FindHandler(const TLVReader & aReader, size_t aDepth, void * aContext) { VerifyOrReturnError(aContext != nullptr, CHIP_ERROR_INVALID_ARGUMENT); const FindContext * const theContext = static_cast(aContext); if (theContext->mTag == aReader.GetTag()) { theContext->mReader.Init(aReader); // terminate the iteration when the specified tag is found return CHIP_ERROR_SENTINEL; } return CHIP_NO_ERROR; } /** * Search for the specified tag within the provided TLV reader. * * @param[in] aReader A read-only reference to the TLV reader in * which to find the specified tag. * @param[in] aTag A read-only reference to the TLV tag to find. * @param[out] aResult A reference to storage to a TLV reader which * will be positioned at the specified tag * on success. * * @retval #CHIP_NO_ERROR On success. * * @retval #CHIP_ERROR_TLV_TAG_NOT_FOUND If the specified tag @a aTag was not found. * */ CHIP_ERROR Find(const TLVReader & aReader, const Tag & aTag, TLVReader & aResult) { constexpr bool recurse = true; return Find(aReader, aTag, aResult, recurse); } /** * Search for the specified tag within the provided TLV reader, * optionally descending into arrays or structures. * * @param[in] aReader A read-only reference to the TLV reader in * which to find the specified tag. * @param[in] aTag A read-only reference to the TLV tag to find. * @param[out] aResult A reference to storage to a TLV reader which * will be positioned at the specified tag * on success. * @param[in] aRecurse A Boolean indicating whether (true) or not (false) * any encountered arrays or structures should be * descended into. * * @retval #CHIP_NO_ERROR On success. * * @retval #CHIP_ERROR_TLV_TAG_NOT_FOUND If the specified tag @a aTag was not found. * */ CHIP_ERROR Find(const TLVReader & aReader, const Tag & aTag, TLVReader & aResult, const bool aRecurse) { FindContext theContext = { aTag, aResult }; CHIP_ERROR retval = Iterate(aReader, FindHandler, &theContext, aRecurse); if (retval == CHIP_ERROR_SENTINEL) retval = CHIP_NO_ERROR; else retval = CHIP_ERROR_TLV_TAG_NOT_FOUND; return retval; } struct FindPredicateContext { TLVReader & mResult; IterateHandler mHandler; void * mContext; FindPredicateContext(TLVReader & inReader, IterateHandler inHandler, void * inContext); }; FindPredicateContext::FindPredicateContext(TLVReader & inReader, IterateHandler inHandler, void * inContext) : mResult(inReader), mHandler(inHandler), mContext(inContext) {} static CHIP_ERROR FindPredicateHandler(const TLVReader & aReader, size_t aDepth, void * aContext) { FindPredicateContext * theContext = static_cast(aContext); CHIP_ERROR err; err = theContext->mHandler(aReader, aDepth, theContext->mContext); if (err == CHIP_ERROR_SENTINEL) theContext->mResult.Init(aReader); return err; } /** * Search for the first element matching the predicate within the TLV reader * descending into arrays or structures. The @a aPredicate is applied * to each visited TLV element; the @a aPredicate shall return #CHIP_ERROR_SENTINEL * for the matching elements, #CHIP_NO_ERROR for non-matching elements, and any * other value to terminate the search. * * @param[in] aReader A read-only reference to the TLV reader in which to find the * element matching the predicate. * @param[in] aPredicate A predicate to be applied to each TLV element. To * support the code reuse, aPredicate has the * IterateHandler type. The return value of aPredicate * controls the search: a #CHIP_ERROR_SENTINEL signals that * desired element has been found, #CHIP_NO_ERROR * signals that the desired element has not been found, * and all other values signal that the search should be * terminated. * @param[in] aContext An optional pointer to caller-provided context data. * * @param[out] aResult A reference to storage to a TLV reader which * will be positioned at the specified tag * on success. * @retval #CHIP_NO_ERROR On success. * * @retval #CHIP_ERROR_TLV_TAG_NOT_FOUND If the specified @a aPredicate did not locate the specified element * */ CHIP_ERROR Find(const TLVReader & aReader, IterateHandler aPredicate, void * aContext, TLVReader & aResult) { const bool recurse = true; return Find(aReader, aPredicate, aContext, aResult, recurse); } /** * Search for the first element matching the predicate within the TLV reader * optionally descending into arrays or structures. The @a aPredicate is applied * to each visited TLV element; the @a aPredicate shall return #CHIP_ERROR_SENTINEL * for the matching elements, #CHIP_NO_ERROR for non-matching elements, and any * other value to terminate the search. * * @param[in] aReader A read-only reference to the TLV reader in which to find the * element matching the predicate. * @param[in] aPredicate A predicate to be applied to each TLV element. To * support the code reuse, aPredicate has the * @a IterateHandler type. The return value of aPredicate * controls the search: a #CHIP_ERROR_SENTINEL signals that * desired element has been found, #CHIP_NO_ERROR * signals that the desired element has not been found, * and all other values signal that the saerch should be * terminated. * @param[in] aContext An optional pointer to caller-provided context data. * @param[out] aResult A reference to storage to a TLV reader which * will be positioned at the specified tag * on success. * @param[in] aRecurse A boolean indicating whether (true) or not (false) any * encountered arrays or structures should be descended * into. * * @retval #CHIP_NO_ERROR On success. * * @retval #CHIP_ERROR_TLV_TAG_NOT_FOUND If the specified @a aPredicate did not locate the specified element * */ CHIP_ERROR Find(const TLVReader & aReader, IterateHandler aPredicate, void * aContext, TLVReader & aResult, const bool aRecurse) { CHIP_ERROR retval; FindPredicateContext theContext(aResult, aPredicate, aContext); retval = Iterate(aReader, FindPredicateHandler, &theContext, aRecurse); if (retval == CHIP_ERROR_SENTINEL) retval = CHIP_NO_ERROR; else retval = CHIP_ERROR_TLV_TAG_NOT_FOUND; return retval; } } // namespace Utilities } // namespace TLV } // namespace chip