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

Generated by: LCOV version 2.0-1