/* * * Copyright (c) 2020-2021 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. */ #include #include #include #include #include #include #include #include namespace chip { namespace TLV { namespace Debug { /** * Dump the TLV element referenced by @a aReader in human-readable form using * @a aWriter. * * @param[in] aWriter The writer to log the TLV data. * @param[in] aIndent The indentation for logging the current depth into * the TLV data. * @param[in] aReader A read-only reference to the TLV reader containing * the TLV data to log. * @param[in] aDepth The current depth into the TLV data. * */ static void DumpHandler(DumpWriter aWriter, const char * aIndent, const TLVReader & aReader, size_t aDepth) { const TLVType type = aReader.GetType(); const Tag tag = aReader.GetTag(); const uint32_t len = aReader.GetLength(); const uint8_t * strbuf = nullptr; CHIP_ERROR err = CHIP_NO_ERROR; TLVReader temp; TLVTagControl tagControl; temp.Init(aReader); tagControl = static_cast(temp.GetControlByte() & kTLVTagControlMask); aWriter("0x%02X, ", temp.GetLengthRead()); for (size_t i = 0; i < aDepth; i++) aWriter("%s", aIndent); if (IsProfileTag(tag)) { aWriter("tag[%s]: 0x%x::0x%x::0x%x, ", DecodeTagControl(tagControl), VendorIdFromTag(tag), ProfileNumFromTag(tag), TagNumFromTag(tag)); } else if (IsContextTag(tag)) { aWriter("tag[%s]: 0x%x, ", DecodeTagControl(tagControl), TagNumFromTag(tag)); } else if (IsSpecialTag(tag)) { aWriter("tag[%s]: 0x%x, ", DecodeTagControl(tagControl), tag); } else { aWriter("tag[unknown]: 0x%x, ", tag); } aWriter("type: %s (0x%02x), ", DecodeType(type), type); if (TLVTypeIsContainer(type)) { aWriter("container: "); } else { if (type == kTLVType_UTF8String || type == kTLVType_ByteString) aWriter("length: %" PRIu32 ", ", len); aWriter("value: "); switch (type) { case kTLVType_SignedInteger: int64_t sVal; err = temp.Get(sVal); VerifyOrExit(err == CHIP_NO_ERROR, aWriter("Error in kTLVType_SignedInteger")); aWriter("%" PRIi64, sVal); break; case kTLVType_UnsignedInteger: uint64_t uVal; err = temp.Get(uVal); VerifyOrExit(err == CHIP_NO_ERROR, aWriter("Error in kTLVType_UnsignedInteger")); aWriter("%" PRIu64, uVal); break; case kTLVType_Boolean: bool bVal; err = temp.Get(bVal); VerifyOrExit(err == CHIP_NO_ERROR, aWriter("Error in kTLVType_Boolean")); aWriter("%s", bVal ? "true" : "false"); break; case kTLVType_FloatingPointNumber: double fpVal; err = temp.Get(fpVal); VerifyOrExit(err == CHIP_NO_ERROR, aWriter("Error in kTLVType_FloatingPointNumber")); aWriter("%lf", fpVal); break; case kTLVType_UTF8String: err = temp.GetDataPtr(strbuf); VerifyOrExit(err == CHIP_NO_ERROR, aWriter("Error in kTLVType_UTF8String")); aWriter("\"%-.*s\"", static_cast(len), strbuf); break; case kTLVType_ByteString: err = temp.GetDataPtr(strbuf); VerifyOrExit(err == CHIP_NO_ERROR, aWriter("Error in kTLVType_ByteString")); aWriter("hex:"); for (uint32_t i = 0; i < len; i++) { aWriter("%02X", strbuf[i]); } break; case kTLVType_Null: aWriter("NULL"); break; case kTLVType_NotSpecified: aWriter("Not Specified"); break; default: aWriter("Error: Type is not primitive."); break; } } exit: aWriter("\n"); } /** * Decode a TLV tag control with a descriptive string. * * @param[in] aTagControl The TLV tag control to decode and for which to return * a descriptive string. * * @return A pointer to a NULL-terminated string describing the specified * tag control on success; otherwise, NULL. * */ const char * DecodeTagControl(const TLVTagControl aTagControl) { const char * retval; switch (aTagControl) { case TLVTagControl::Anonymous: retval = "Anonymous"; break; case TLVTagControl::ContextSpecific: retval = "Context Specific"; break; case TLVTagControl::CommonProfile_2Bytes: retval = "Common Profile (2 Bytes)"; break; case TLVTagControl::CommonProfile_4Bytes: retval = "Common Profile (4 Bytes)"; break; case TLVTagControl::ImplicitProfile_2Bytes: retval = "Implicit Profile (2 Bytes)"; break; case TLVTagControl::ImplicitProfile_4Bytes: retval = "Implicit Profile (4 Bytes)"; break; case TLVTagControl::FullyQualified_6Bytes: retval = "Fully Qualified (6 Bytes)"; break; case TLVTagControl::FullyQualified_8Bytes: retval = "Fully Qualified (8 Bytes)"; break; default: retval = nullptr; break; } return retval; } /** * Decode a TLV type with a descriptive string. * * @param[in] aType The TLV type to decode and for which to return * a descriptive string. * * @return A pointer to a NULL-terminated string describing the specified * type on success; otherwise, NULL. * */ const char * DecodeType(const TLVType aType) { const char * retval; switch (aType) { case kTLVType_NotSpecified: retval = "Not Specified"; break; case kTLVType_SignedInteger: retval = "Signed Fixed Point"; break; case kTLVType_UnsignedInteger: retval = "Unsigned Fixed Point"; break; case kTLVType_Boolean: retval = "Boolean"; break; case kTLVType_FloatingPointNumber: retval = "Floating Point"; break; case kTLVType_UTF8String: retval = "UTF-8 String"; break; case kTLVType_ByteString: retval = "Octet String"; break; case kTLVType_Null: retval = "Null"; break; case kTLVType_Structure: retval = "Structure"; break; case kTLVType_Array: retval = "Array"; break; case kTLVType_List: retval = "List"; break; default: retval = nullptr; break; } return retval; } /** * Log the TLV data within the specified reader in human-readable form to * the specified writer. * * @param[in] aWriter The writer to log the TLV data. * @param[in] aReader A read-only reference to the TLV reader containing * the TLV data to log. * * @retval #CHIP_NO_ERROR Unconditionally. * */ CHIP_ERROR DumpIterator(DumpWriter aWriter, const TLVReader & aReader) { const char * tabs = ""; const size_t depth = 0; CHIP_ERROR retval = CHIP_NO_ERROR; DumpHandler(aWriter, tabs, aReader, depth); return retval; } /** * Log the TLV data within the specified reader in human-readable form. * * @param[in] aReader A read-only reference to the TLV reader containing * the TLV data to log. * @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 aContext is NULL or if * aContext->mWriter is NULL. * */ CHIP_ERROR DumpHandler(const TLVReader & aReader, size_t aDepth, void * aContext) { static const char indent[] = " "; DumpContext * context; VerifyOrReturnError(aContext != nullptr, CHIP_ERROR_INVALID_ARGUMENT); context = static_cast(aContext); VerifyOrReturnError(context->mWriter != nullptr, CHIP_ERROR_INVALID_ARGUMENT); DumpHandler(context->mWriter, indent, aReader, aDepth); return CHIP_NO_ERROR; } /** * Dump the TLV data within the specified reader in human-readable form with * the specified writer. * * @param[in] aReader A read-only reference to the TLV reader containing * the TLV data to log. * * @param[in] aWriter A dump writer to log the TLV data of the TLV reader. * * @retval #CHIP_NO_ERROR On success. * */ CHIP_ERROR Dump(const TLVReader & aReader, DumpWriter aWriter) { void * context = nullptr; DumpContext dumpContext = { aWriter, context }; CHIP_ERROR retval; retval = Utilities::Iterate(aReader, DumpHandler, &dumpContext); return retval; } } // namespace Debug } // namespace TLV } // namespace chip