LCOV - code coverage report
Current view: top level - lib/dnssd - TxtFields.cpp (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 137 137 100.0 %
Date: 2024-02-15 08:20:41 Functions: 23 23 100.0 %

          Line data    Source code
       1             : /*
       2             :  *
       3             :  *    Copyright (c) 2021 Project CHIP Authors
       4             :  *
       5             :  *    Licensed under the Apache License, Version 2.0 (the "License");
       6             :  *    you may not use this file except in compliance with the License.
       7             :  *    You may obtain a copy of the License at
       8             :  *
       9             :  *        http://www.apache.org/licenses/LICENSE-2.0
      10             :  *
      11             :  *    Unless required by applicable law or agreed to in writing, software
      12             :  *    distributed under the License is distributed on an "AS IS" BASIS,
      13             :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      14             :  *    See the License for the specific language governing permissions and
      15             :  *    limitations under the License.
      16             :  */
      17             : 
      18             : #include "TxtFields.h"
      19             : 
      20             : #include <algorithm>
      21             : #include <cctype>
      22             : #include <climits>
      23             : #include <cstdio>
      24             : #include <inttypes.h>
      25             : #include <limits>
      26             : #include <stdlib.h>
      27             : #include <string.h>
      28             : 
      29             : #include <lib/core/CHIPSafeCasts.h>
      30             : #include <lib/dnssd/Advertiser.h>
      31             : #include <lib/dnssd/Resolver.h>
      32             : #include <lib/support/BytesToHex.h>
      33             : #include <lib/support/CHIPMemString.h>
      34             : #include <lib/support/SafeInt.h>
      35             : 
      36             : namespace chip {
      37             : namespace Dnssd {
      38             : 
      39             : namespace Internal {
      40             : 
      41             : namespace {
      42             : 
      43        3860 : char SafeToLower(uint8_t ch)
      44             : {
      45        3860 :     return static_cast<char>(std::tolower(static_cast<unsigned char>(ch)));
      46             : }
      47        2822 : bool IsKey(const ByteSpan & key, const char * desired)
      48             : {
      49        2822 :     if (key.size() != strlen(desired))
      50             :     {
      51        1455 :         return false;
      52             :     }
      53             : 
      54        1367 :     auto desired_bytes = Uint8::from_const_char(desired);
      55        2354 :     for (size_t i = 0; i < key.size(); ++i)
      56             :     {
      57        1930 :         if (SafeToLower(key.data()[i]) != SafeToLower(desired_bytes[i]))
      58             :         {
      59         943 :             return false;
      60             :         }
      61             :     }
      62         424 :     return true;
      63             : }
      64             : 
      65         250 : uint32_t MakeU32FromAsciiDecimal(const ByteSpan & val, uint32_t defaultValue = 0)
      66             : {
      67             :     // +1 because `digits10` means the number of decimal digits that fit in `uint32_t`,
      68             :     // not how many digits are enough to represent any `uint32_t`
      69             :     // +1 for null-terminator
      70             :     char nullTerminatedValue[std::numeric_limits<uint32_t>::digits10 + 2];
      71             : 
      72             :     // value is too long to store `uint32_t`
      73         250 :     if (val.size() >= sizeof(nullTerminatedValue))
      74           6 :         return defaultValue;
      75             : 
      76             :     // value contains leading zeros
      77         244 :     if (val.size() > 1 && *val.data() == static_cast<uint8_t>('0'))
      78          12 :         return defaultValue;
      79             : 
      80         232 :     Platform::CopyString(nullTerminatedValue, sizeof(nullTerminatedValue), val);
      81             : 
      82             :     char * endPtr;
      83         232 :     unsigned long num = strtoul(nullTerminatedValue, &endPtr, 10);
      84             : 
      85         232 :     if (endPtr > nullTerminatedValue && *endPtr == '\0' && num != ULONG_MAX && CanCastTo<uint32_t>(num))
      86         218 :         return static_cast<uint32_t>(num);
      87             : 
      88          14 :     return defaultValue;
      89             : }
      90             : 
      91         164 : uint16_t MakeU16FromAsciiDecimal(const ByteSpan & val)
      92             : {
      93         164 :     const uint32_t num = MakeU32FromAsciiDecimal(val);
      94         164 :     return CanCastTo<uint16_t>(num) ? static_cast<uint16_t>(num) : 0;
      95             : }
      96             : 
      97          41 : uint8_t MakeU8FromAsciiDecimal(const ByteSpan & val)
      98             : {
      99          41 :     const uint32_t num = MakeU32FromAsciiDecimal(val);
     100          41 :     return CanCastTo<uint8_t>(num) ? static_cast<uint8_t>(num) : 0;
     101             : }
     102             : 
     103           6 : bool MakeBoolFromAsciiDecimal(const ByteSpan & val)
     104             : {
     105           6 :     return val.size() == 1 && static_cast<char>(*val.data()) == '1';
     106             : }
     107             : 
     108           6 : Optional<bool> MakeOptionalBoolFromAsciiDecimal(const ByteSpan & val)
     109             : {
     110           6 :     char character = static_cast<char>(*val.data());
     111           6 :     if (val.size() == 1 && ((character == '1') || (character == '0')))
     112             :     {
     113           4 :         return MakeOptional(character == '1');
     114             :     }
     115           2 :     return NullOptional;
     116             : }
     117             : 
     118          78 : size_t GetPlusSignIdx(const ByteSpan & value)
     119             : {
     120             :     // First value is the vendor id, second (after the +) is the product.
     121         436 :     for (size_t i = 0; i < value.size(); ++i)
     122             :     {
     123         434 :         if (static_cast<char>(value.data()[i]) == '+')
     124             :         {
     125          76 :             return i;
     126             :         }
     127             :     }
     128           2 :     return value.size();
     129             : }
     130             : 
     131             : } // namespace
     132             : 
     133          39 : uint16_t GetProduct(const ByteSpan & value)
     134             : {
     135          39 :     size_t plussign = GetPlusSignIdx(value);
     136          39 :     if (plussign < value.size() - 1)
     137             :     {
     138          37 :         const uint8_t * productStrStart = value.data() + plussign + 1;
     139          37 :         size_t productStrLen            = value.size() - plussign - 1;
     140          37 :         return MakeU16FromAsciiDecimal(ByteSpan(productStrStart, productStrLen));
     141             :     }
     142           2 :     return 0;
     143             : }
     144             : 
     145          39 : uint16_t GetVendor(const ByteSpan & value)
     146             : {
     147          39 :     size_t plussign = GetPlusSignIdx(value);
     148          39 :     return MakeU16FromAsciiDecimal(ByteSpan(value.data(), plussign));
     149             : }
     150             : 
     151          36 : uint16_t GetLongDiscriminator(const ByteSpan & value)
     152             : {
     153          36 :     return MakeU16FromAsciiDecimal(value);
     154             : }
     155             : 
     156          37 : uint8_t GetCommissioningMode(const ByteSpan & value)
     157             : {
     158          37 :     return MakeU8FromAsciiDecimal(value);
     159             : }
     160             : 
     161           3 : uint32_t GetDeviceType(const ByteSpan & value)
     162             : {
     163           3 :     return MakeU32FromAsciiDecimal(value);
     164             : }
     165             : 
     166           4 : void GetDeviceName(const ByteSpan & value, char * name)
     167             : {
     168           4 :     Platform::CopyString(name, kMaxDeviceNameLen + 1, value);
     169           4 : }
     170             : 
     171           8 : void GetRotatingDeviceId(const ByteSpan & value, uint8_t * rotatingId, size_t * len)
     172             : {
     173           8 :     *len = Encoding::HexToBytes(reinterpret_cast<const char *>(value.data()), value.size(), rotatingId, kMaxRotatingIdLen);
     174           8 : }
     175             : 
     176          36 : uint16_t GetPairingHint(const ByteSpan & value)
     177             : {
     178          36 :     return MakeU16FromAsciiDecimal(value);
     179             : }
     180             : 
     181          36 : void GetPairingInstruction(const ByteSpan & value, char * pairingInstruction)
     182             : {
     183          36 :     Platform::CopyString(pairingInstruction, kMaxPairingInstructionLen + 1, value);
     184          36 : }
     185             : 
     186           4 : uint8_t GetCommissionerPasscode(const ByteSpan & value)
     187             : {
     188           4 :     return MakeU8FromAsciiDecimal(value);
     189             : }
     190             : 
     191          42 : Optional<System::Clock::Milliseconds32> GetRetryInterval(const ByteSpan & value)
     192             : {
     193          42 :     const auto undefined     = std::numeric_limits<uint32_t>::max();
     194          42 :     const auto retryInterval = MakeU32FromAsciiDecimal(value, undefined);
     195             : 
     196          42 :     if (retryInterval != undefined && retryInterval <= kMaxRetryInterval.count())
     197          18 :         return MakeOptional(System::Clock::Milliseconds32(retryInterval));
     198             : 
     199          24 :     return NullOptional;
     200             : }
     201             : 
     202          16 : Optional<System::Clock::Milliseconds16> GetRetryActiveThreshold(const ByteSpan & value)
     203             : {
     204          16 :     const auto retryInterval = MakeU16FromAsciiDecimal(value);
     205             : 
     206          16 :     if (retryInterval == 0)
     207             :     {
     208          12 :         return NullOptional;
     209             :     }
     210             : 
     211           4 :     return MakeOptional(System::Clock::Milliseconds16(retryInterval));
     212             : }
     213             : 
     214         429 : TxtFieldKey GetTxtFieldKey(const ByteSpan & key)
     215             : {
     216        2827 :     for (auto & info : txtFieldInfo)
     217             :     {
     218        2822 :         if (IsKey(key, info.keyStr))
     219             :         {
     220         424 :             return info.key;
     221             :         }
     222             :     }
     223           5 :     return TxtFieldKey::kUnknown;
     224             : }
     225             : 
     226             : } // namespace Internal
     227             : 
     228         174 : void FillNodeDataFromTxt(const ByteSpan & key, const ByteSpan & val, CommissionNodeData & nodeData)
     229             : {
     230         174 :     TxtFieldKey keyType = Internal::GetTxtFieldKey(key);
     231         174 :     switch (keyType)
     232             :     {
     233          34 :     case TxtFieldKey::kLongDiscriminator:
     234          34 :         nodeData.longDiscriminator = Internal::GetLongDiscriminator(val);
     235          34 :         break;
     236          34 :     case TxtFieldKey::kVendorProduct:
     237          34 :         nodeData.vendorId  = Internal::GetVendor(val);
     238          34 :         nodeData.productId = Internal::GetProduct(val);
     239          34 :         break;
     240          33 :     case TxtFieldKey::kCommissioningMode:
     241          33 :         nodeData.commissioningMode = Internal::GetCommissioningMode(val);
     242          33 :         break;
     243           1 :     case TxtFieldKey::kDeviceType:
     244           1 :         nodeData.deviceType = Internal::GetDeviceType(val);
     245           1 :         break;
     246           2 :     case TxtFieldKey::kDeviceName:
     247           2 :         Internal::GetDeviceName(val, nodeData.deviceName);
     248           2 :         break;
     249           1 :     case TxtFieldKey::kRotatingDeviceId:
     250           1 :         Internal::GetRotatingDeviceId(val, nodeData.rotatingId, &nodeData.rotatingIdLen);
     251           1 :         break;
     252          33 :     case TxtFieldKey::kPairingInstruction:
     253          33 :         Internal::GetPairingInstruction(val, nodeData.pairingInstruction);
     254          33 :         break;
     255          33 :     case TxtFieldKey::kPairingHint:
     256          33 :         nodeData.pairingHint = Internal::GetPairingHint(val);
     257          33 :         break;
     258           1 :     case TxtFieldKey::kCommissionerPasscode:
     259           1 :         nodeData.commissionerPasscode = Internal::GetCommissionerPasscode(val);
     260           1 :         break;
     261           2 :     default:
     262           2 :         break;
     263             :     }
     264         174 : }
     265             : 
     266         235 : void FillNodeDataFromTxt(const ByteSpan & key, const ByteSpan & value, CommonResolutionData & nodeData)
     267             : {
     268         235 :     switch (Internal::GetTxtFieldKey(key))
     269             :     {
     270          21 :     case TxtFieldKey::kSessionIdleInterval:
     271          21 :         nodeData.mrpRetryIntervalIdle = Internal::GetRetryInterval(value);
     272          21 :         break;
     273          21 :     case TxtFieldKey::kSessionActiveInterval:
     274          21 :         nodeData.mrpRetryIntervalActive = Internal::GetRetryInterval(value);
     275          21 :         break;
     276          16 :     case TxtFieldKey::kSessionActiveThreshold:
     277          16 :         nodeData.mrpRetryActiveThreshold = Internal::GetRetryActiveThreshold(value);
     278          16 :         break;
     279           6 :     case TxtFieldKey::kTcpSupported:
     280           6 :         nodeData.supportsTcp = Internal::MakeBoolFromAsciiDecimal(value);
     281           6 :         break;
     282           6 :     case TxtFieldKey::kLongIdleTimeICD:
     283           6 :         nodeData.isICDOperatingAsLIT = Internal::MakeOptionalBoolFromAsciiDecimal(value);
     284           6 :         break;
     285         165 :     default:
     286         165 :         break;
     287             :     }
     288         235 : }
     289             : 
     290             : } // namespace Dnssd
     291             : } // namespace chip

Generated by: LCOV version 1.14