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

Generated by: LCOV version 2.0-1