Matter SDK Coverage Report
Current view: top level - lib/dnssd - IncrementalResolve.cpp (source / functions) Coverage Total Hit
Test: SHA:b879ecb8e99e175eea0a293a888bda853da2b19c Lines: 90.8 % 130 118
Test Date: 2025-01-17 19:00:11 Functions: 100.0 % 18 18

            Line data    Source code
       1              : /*
       2              :  *
       3              :  *    Copyright (c) 2022 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              : #include <lib/dnssd/IncrementalResolve.h>
      18              : 
      19              : #include <lib/dnssd/IPAddressSorter.h>
      20              : #include <lib/dnssd/ServiceNaming.h>
      21              : #include <lib/dnssd/TxtFields.h>
      22              : #include <lib/dnssd/minimal_mdns/Logging.h>
      23              : #include <lib/dnssd/minimal_mdns/core/RecordWriter.h>
      24              : #include <lib/support/CHIPMemString.h>
      25              : #include <tracing/macros.h>
      26              : 
      27              : namespace chip {
      28              : namespace Dnssd {
      29              : 
      30              : using namespace mdns::Minimal;
      31              : using namespace mdns::Minimal::Logging;
      32              : 
      33              : namespace {
      34              : 
      35          264 : ByteSpan GetSpan(const mdns::Minimal::BytesRange & range)
      36              : {
      37          264 :     return ByteSpan(range.Start(), range.Size());
      38              : }
      39              : 
      40              : /// Handles filling record data from TXT records.
      41              : ///
      42              : /// Supported records are whatever `FillNodeDataFromTxt` supports.
      43              : template <class DataType>
      44              : class TxtParser : public mdns::Minimal::TxtRecordDelegate
      45              : {
      46              : public:
      47           27 :     explicit TxtParser(DataType & data) : mData(data) {}
      48          132 :     void OnRecord(const mdns::Minimal::BytesRange & name, const mdns::Minimal::BytesRange & value) override
      49              :     {
      50          132 :         FillNodeDataFromTxt(GetSpan(name), GetSpan(value), mData);
      51          132 :     }
      52              : 
      53              : private:
      54              :     DataType & mData;
      55              : };
      56              : 
      57              : // Common prefix to check for all operational/commissioner/commissionable name parts
      58              : constexpr QNamePart kOperationalSuffix[]    = { kOperationalServiceName, kOperationalProtocol, kLocalDomain };
      59              : constexpr QNamePart kCommissionableSuffix[] = { kCommissionableServiceName, kCommissionProtocol, kLocalDomain };
      60              : constexpr QNamePart kCommissionerSuffix[]   = { kCommissionerServiceName, kCommissionProtocol, kLocalDomain };
      61              : 
      62           18 : IncrementalResolver::ServiceNameType ComputeServiceNameType(SerializedQNameIterator name)
      63              : {
      64              :     // SRV record names look like:
      65              :     //   <compressed-fabric-id>-<node-id>._matter._tcp.local  (operational)
      66              :     //   <instance>._matterc._udp.local  (commissionable)
      67              :     //   <instance>._matterd._udp.local  (commissioner)
      68              : 
      69              :     // Starts with compressed-fabric-node or instance, skip over it.
      70           18 :     if (!name.Next() || !name.IsValid())
      71              :     {
      72              :         // missing required components - empty service name
      73            0 :         return IncrementalResolver::ServiceNameType::kInvalid;
      74              :     }
      75              : 
      76           18 :     if (name == kOperationalSuffix)
      77              :     {
      78            2 :         return IncrementalResolver::ServiceNameType::kOperational;
      79              :     }
      80              : 
      81           16 :     if (name == kCommissionableSuffix)
      82              :     {
      83           14 :         return IncrementalResolver::ServiceNameType::kCommissionable;
      84              :     }
      85              : 
      86            2 :     if (name == kCommissionerSuffix)
      87              :     {
      88            1 :         return IncrementalResolver::ServiceNameType::kCommissioner;
      89              :     }
      90              : 
      91            1 :     return IncrementalResolver::ServiceNameType::kInvalid;
      92              : }
      93              : 
      94              : /// Automatically resets a IncrementalResolver to inactive in its destructor
      95              : /// unless disarmed.
      96              : ///
      97              : /// Used for RAII for inactive reset
      98              : class AutoInactiveResetter
      99              : {
     100              : public:
     101           18 :     explicit AutoInactiveResetter(IncrementalResolver & resolver) : mResolver(resolver) {}
     102           18 :     ~AutoInactiveResetter()
     103              :     {
     104           18 :         if (mArmed)
     105              :         {
     106            1 :             mResolver.ResetToInactive();
     107              :         }
     108           18 :     }
     109              : 
     110           17 :     void Disarm() { mArmed = false; }
     111              : 
     112              : private:
     113              :     bool mArmed = true;
     114              :     IncrementalResolver & mResolver;
     115              : };
     116              : 
     117              : } // namespace
     118              : 
     119           71 : CHIP_ERROR StoredServerName::Set(SerializedQNameIterator value)
     120              : {
     121           71 :     chip::Encoding::BigEndian::BufferWriter output(mNameBuffer, sizeof(mNameBuffer));
     122           71 :     RecordWriter writer(&output);
     123              : 
     124           71 :     writer.WriteQName(value);
     125              : 
     126           71 :     if (!writer.Fit())
     127              :     {
     128           25 :         Clear();
     129           25 :         return CHIP_ERROR_NO_MEMORY;
     130              :     }
     131              : 
     132           46 :     return CHIP_NO_ERROR;
     133              : }
     134              : 
     135          143 : SerializedQNameIterator StoredServerName::Get() const
     136              : {
     137          143 :     return SerializedQNameIterator(BytesRange(mNameBuffer, mNameBuffer + sizeof(mNameBuffer)), mNameBuffer);
     138              : }
     139              : 
     140           18 : CHIP_ERROR IncrementalResolver::InitializeParsing(mdns::Minimal::SerializedQNameIterator name, const uint64_t ttl,
     141              :                                                   const mdns::Minimal::SrvRecord & srv)
     142              : {
     143           18 :     AutoInactiveResetter inactiveReset(*this);
     144              : 
     145           18 :     ReturnErrorOnFailure(mRecordName.Set(name));
     146           18 :     ReturnErrorOnFailure(mTargetHostName.Set(srv.GetName()));
     147           18 :     mCommonResolutionData.port = srv.GetPort();
     148              : 
     149              :     {
     150              :         // TODO: Chip code historically seems to assume that the host name is of the
     151              :         // form "<MAC or 802.15.4 Extended Address in hex>.local" and only saves the first part.
     152              :         //
     153              :         // This should not be needed as server name should not be relevant once parsed.
     154              :         // The Resolver keeps track of this name for the purpose of address resolution
     155              :         // in mTargetHostName.
     156              :         //
     157              :         // Keeping track of this in resolution data does not seem useful and should be
     158              :         // removed.
     159           18 :         SerializedQNameIterator serverName = srv.GetName();
     160              : 
     161           18 :         VerifyOrReturnError(serverName.Next() && serverName.IsValid(), CHIP_ERROR_INVALID_ARGUMENT);
     162              : 
     163              :         // only save the first part: the MAC or 802.15.4 Extended Address in hex
     164           18 :         Platform::CopyString(mCommonResolutionData.hostName, serverName.Value());
     165              :     }
     166              : 
     167           18 :     mServiceNameType = ComputeServiceNameType(name);
     168              : 
     169           18 :     switch (mServiceNameType)
     170              :     {
     171            2 :     case ServiceNameType::kOperational:
     172            2 :         mSpecificResolutionData.Set<OperationalNodeData>();
     173              :         {
     174              :             // Operational addresses start with peer node information
     175            2 :             SerializedQNameIterator nameCopy = name;
     176            2 :             if (!nameCopy.Next() || !nameCopy.IsValid())
     177              :             {
     178            0 :                 return CHIP_ERROR_INVALID_ARGUMENT;
     179              :             }
     180              : 
     181              :             CHIP_ERROR err =
     182            2 :                 ExtractIdFromInstanceName(nameCopy.Value(), &mSpecificResolutionData.Get<OperationalNodeData>().peerId);
     183            2 :             if (err != CHIP_NO_ERROR)
     184              :             {
     185            0 :                 return err;
     186              :             }
     187            2 :             mSpecificResolutionData.Get<OperationalNodeData>().hasZeroTTL = (ttl == 0);
     188              :         }
     189              : 
     190            2 :         LogFoundOperationalSrvRecord(mSpecificResolutionData.Get<OperationalNodeData>().peerId, mTargetHostName.Get());
     191            2 :         break;
     192           15 :     case ServiceNameType::kCommissioner:
     193              :     case ServiceNameType::kCommissionable:
     194           15 :         mSpecificResolutionData.Set<CommissionNodeData>();
     195              : 
     196              :         {
     197              :             // Commission addresses start with instance name
     198           15 :             SerializedQNameIterator nameCopy = name;
     199           15 :             if (!nameCopy.Next() || !nameCopy.IsValid())
     200              :             {
     201            0 :                 return CHIP_ERROR_INVALID_ARGUMENT;
     202              :             }
     203              : 
     204           15 :             Platform::CopyString(mSpecificResolutionData.Get<CommissionNodeData>().instanceName, nameCopy.Value());
     205              :         }
     206              : 
     207           15 :         LogFoundCommissionSrvRecord(mSpecificResolutionData.Get<CommissionNodeData>().instanceName, mTargetHostName.Get());
     208           15 :         break;
     209            1 :     default:
     210            1 :         return CHIP_ERROR_INVALID_ARGUMENT;
     211              :     }
     212              : 
     213           17 :     inactiveReset.Disarm();
     214           17 :     return CHIP_NO_ERROR;
     215           18 : }
     216              : 
     217           20 : IncrementalResolver::RequiredInformationFlags IncrementalResolver::GetMissingRequiredInformation() const
     218              : {
     219           20 :     RequiredInformationFlags flags;
     220              : 
     221           20 :     if (!mSpecificResolutionData.Valid())
     222              :     {
     223            1 :         flags.Set(RequiredInformationBitFlags::kSrvInitialization);
     224              :     }
     225              :     else
     226              :     {
     227           19 :         if (mCommonResolutionData.numIPs == 0)
     228              :         {
     229            5 :             flags.Set(RequiredInformationBitFlags::kIpAddress);
     230              :         }
     231              :     }
     232              : 
     233           20 :     return flags;
     234              : }
     235              : 
     236          117 : CHIP_ERROR IncrementalResolver::OnRecord(Inet::InterfaceId interface, const ResourceData & data, BytesRange packetRange)
     237              : {
     238          117 :     if (!IsActive())
     239              :     {
     240            0 :         return CHIP_NO_ERROR; // nothing to parse
     241              :     }
     242              : 
     243          117 :     switch (data.GetType())
     244              :     {
     245           16 :     case QType::TXT:
     246           16 :         if (data.GetName() != mRecordName.Get())
     247              :         {
     248              :             MATTER_TRACE_INSTANT("TXT not applicable", "Resolver");
     249            2 :             return CHIP_NO_ERROR;
     250              :         }
     251           14 :         return OnTxtRecord(data, packetRange);
     252           12 :     case QType::A: {
     253           12 :         if (data.GetName() != mTargetHostName.Get())
     254              :         {
     255              :             MATTER_TRACE_INSTANT("IPv4 not applicable", "Resolver");
     256            0 :             return CHIP_NO_ERROR;
     257              :         }
     258              : 
     259              : #if INET_CONFIG_ENABLE_IPV4
     260              :         Inet::IPAddress addr;
     261           12 :         if (!ParseARecord(data.GetData(), &addr))
     262              :         {
     263            0 :             return CHIP_ERROR_INVALID_ARGUMENT;
     264              :         }
     265              : 
     266           12 :         return OnIpAddress(interface, addr);
     267              : #else
     268              : #if CHIP_MINMDNS_HIGH_VERBOSITY
     269              :         ChipLogProgress(Discovery, "Ignoring A record: IPv4 not supported");
     270              : #endif
     271              :         // skipping IPv4 addresses
     272              :         return CHIP_NO_ERROR;
     273              : #endif
     274              :     }
     275           17 :     case QType::AAAA: {
     276           17 :         if (data.GetName() != mTargetHostName.Get())
     277              :         {
     278              :             MATTER_TRACE_INSTANT("IPv6 not applicable", "Resolver");
     279            2 :             return CHIP_NO_ERROR;
     280              :         }
     281              : 
     282              :         Inet::IPAddress addr;
     283           15 :         if (!ParseAAAARecord(data.GetData(), &addr))
     284              :         {
     285            0 :             return CHIP_ERROR_INVALID_ARGUMENT;
     286              :         }
     287              : 
     288           15 :         return OnIpAddress(interface, addr);
     289              :     }
     290           72 :     case QType::SRV: // SRV handled on creation, ignored for 'additional data'
     291              :     default:
     292              :         // Other types not interesting during parsing
     293           72 :         return CHIP_NO_ERROR;
     294              :     }
     295              : 
     296              :     return CHIP_NO_ERROR;
     297              : }
     298              : 
     299           14 : CHIP_ERROR IncrementalResolver::OnTxtRecord(const ResourceData & data, BytesRange packetRange)
     300              : {
     301              :     {
     302           14 :         TxtParser<CommonResolutionData> delegate(mCommonResolutionData);
     303           14 :         if (!ParseTxtRecord(data.GetData(), &delegate))
     304              :         {
     305            0 :             return CHIP_ERROR_INVALID_ARGUMENT;
     306              :         }
     307           14 :     }
     308              : 
     309           14 :     if (IsActiveCommissionParse())
     310              :     {
     311           13 :         TxtParser<CommissionNodeData> delegate(mSpecificResolutionData.Get<CommissionNodeData>());
     312           13 :         if (!ParseTxtRecord(data.GetData(), &delegate))
     313              :         {
     314            0 :             return CHIP_ERROR_INVALID_ARGUMENT;
     315              :         }
     316           13 :     }
     317              : 
     318           14 :     return CHIP_NO_ERROR;
     319              : }
     320              : 
     321           27 : CHIP_ERROR IncrementalResolver::OnIpAddress(Inet::InterfaceId interface, const Inet::IPAddress & addr)
     322              : {
     323           27 :     if (mCommonResolutionData.numIPs >= ArraySize(mCommonResolutionData.ipAddress))
     324              :     {
     325            0 :         return CHIP_ERROR_NO_MEMORY;
     326              :     }
     327              : 
     328           27 :     if (!mCommonResolutionData.interfaceId.IsPresent())
     329              :     {
     330           15 :         mCommonResolutionData.interfaceId = interface;
     331              :     }
     332           12 :     else if (mCommonResolutionData.interfaceId != interface)
     333              :     {
     334              :         // IP addresses received from multiple packets over different interfaces.
     335              :         // Processing is assumed per single interface.
     336            0 :         return CHIP_ERROR_INVALID_ARGUMENT;
     337              :     }
     338              : 
     339           27 :     mCommonResolutionData.ipAddress[mCommonResolutionData.numIPs++] = addr;
     340              : 
     341           27 :     LogFoundIPAddress(mTargetHostName.Get(), addr);
     342              : 
     343           27 :     return CHIP_NO_ERROR;
     344              : }
     345              : 
     346           13 : CHIP_ERROR IncrementalResolver::Take(DiscoveredNodeData & outputData)
     347              : {
     348           13 :     VerifyOrReturnError(IsActiveCommissionParse(), CHIP_ERROR_INCORRECT_STATE);
     349              : 
     350           13 :     IPAddressSorter::Sort(mCommonResolutionData.ipAddress, mCommonResolutionData.numIPs, mCommonResolutionData.interfaceId);
     351              : 
     352              :     // Set the DiscoveredNodeData with CommissionNodeData info specific to commissionable/commisssioner
     353              :     // node available in mSpecificResolutionData.
     354           13 :     outputData.Set<CommissionNodeData>(mSpecificResolutionData.Get<CommissionNodeData>());
     355              : 
     356              :     // IncrementalResolver stored CommonResolutionData separately in mCommonResolutionData hence copy the
     357              :     //  CommonResolutionData info from mCommonResolutionData, to CommissionNodeData within DiscoveredNodeData
     358           13 :     CommonResolutionData & resolutionData = outputData.Get<CommissionNodeData>();
     359           13 :     resolutionData                        = mCommonResolutionData;
     360              : 
     361           13 :     ResetToInactive();
     362              : 
     363           13 :     return CHIP_NO_ERROR;
     364              : }
     365              : 
     366            1 : CHIP_ERROR IncrementalResolver::Take(ResolvedNodeData & outputData)
     367              : {
     368            1 :     VerifyOrReturnError(IsActiveOperationalParse(), CHIP_ERROR_INCORRECT_STATE);
     369              : 
     370            1 :     outputData.resolutionData  = mCommonResolutionData;
     371            1 :     outputData.operationalData = mSpecificResolutionData.Get<OperationalNodeData>();
     372              : 
     373            1 :     ResetToInactive();
     374              : 
     375            1 :     return CHIP_NO_ERROR;
     376              : }
     377              : 
     378              : } // namespace Dnssd
     379              : } // namespace chip
        

Generated by: LCOV version 2.0-1