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

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

Generated by: LCOV version 1.14