Matter SDK Coverage Report
Current view: top level - lib/dnssd - ActiveResolveAttempts.h (source / functions) Coverage Total Hit
Test: SHA:b879ecb8e99e175eea0a293a888bda853da2b19c Lines: 27.3 % 66 18
Test Date: 2025-01-17 19:00:11 Functions: 42.9 % 21 9

            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              : #pragma once
      19              : 
      20              : #include <cstddef>
      21              : #include <cstdint>
      22              : #include <optional>
      23              : 
      24              : #include <lib/core/PeerId.h>
      25              : #include <lib/dnssd/Resolver.h>
      26              : #include <lib/dnssd/minimal_mdns/core/HeapQName.h>
      27              : #include <lib/support/Variant.h>
      28              : #include <system/SystemClock.h>
      29              : 
      30              : namespace mdns {
      31              : namespace Minimal {
      32              : 
      33              : /// Keeps track of active resolve attempts
      34              : ///
      35              : /// Maintains a list of 'pending mdns resolve queries' and provides operations
      36              : /// for:
      37              : ///    - add/remove to the list
      38              : ///    - figuring out a 'next query time' for items in the list
      39              : ///    - iterating through the 'schedule now' items of the list
      40              : ///
      41              : class ActiveResolveAttempts
      42              : {
      43              : public:
      44              :     static constexpr size_t kRetryQueueSize                      = 4;
      45              :     static constexpr chip::System::Clock::Timeout kMaxRetryDelay = chip::System::Clock::Seconds16(16);
      46              : 
      47              :     struct ScheduledAttempt
      48              :     {
      49              :         struct Browse
      50              :         {
      51            0 :             Browse(const chip::Dnssd::DiscoveryFilter discoveryFilter, const chip::Dnssd::DiscoveryType discoveryType) :
      52            0 :                 filter(discoveryFilter), type(discoveryType)
      53            0 :             {}
      54              :             chip::Dnssd::DiscoveryFilter filter;
      55              :             chip::Dnssd::DiscoveryType type;
      56              :         };
      57              : 
      58              :         struct Resolve
      59              :         {
      60              :             chip::PeerId peerId;
      61              :             uint32_t consumerCount = 0;
      62              : 
      63            0 :             Resolve(chip::PeerId id) : peerId(id) {}
      64              :         };
      65              : 
      66              :         struct IpResolve
      67              :         {
      68              :             HeapQName hostName;
      69            0 :             IpResolve(HeapQName && host) : hostName(std::move(host)) {}
      70              :         };
      71              : 
      72          252 :         ScheduledAttempt()
      73          252 :         {
      74              :             static_assert(sizeof(Resolve) <= sizeof(Browse) || sizeof(Resolve) <= sizeof(HeapQName),
      75              :                           "Figure out where to put the Resolve counter so that Resolve is not making ScheduledAttempt bigger than "
      76              :                           "it has to be anyway to handle the other attempt types.");
      77          252 :         }
      78            0 :         ScheduledAttempt(const chip::PeerId & peer, bool first) :
      79            0 :             resolveData(chip::InPlaceTemplateType<Resolve>(), peer), firstSend(first)
      80            0 :         {}
      81            0 :         ScheduledAttempt(const chip::Dnssd::DiscoveryFilter discoveryFilter, const chip::Dnssd::DiscoveryType type, bool first) :
      82            0 :             resolveData(chip::InPlaceTemplateType<Browse>(), discoveryFilter, type), firstSend(first)
      83            0 :         {}
      84              : 
      85            0 :         ScheduledAttempt(IpResolve && ipResolve, bool first) :
      86            0 :             resolveData(chip::InPlaceTemplateType<IpResolve>(), ipResolve), firstSend(first)
      87            0 :         {}
      88              : 
      89              :         bool operator==(const ScheduledAttempt & other) const { return Matches(other) && other.firstSend == firstSend; }
      90            0 :         bool Matches(const ScheduledAttempt & other) const
      91              :         {
      92            0 :             if (!resolveData.Valid())
      93              :             {
      94            0 :                 return !other.resolveData.Valid();
      95              :             }
      96              : 
      97            0 :             if (resolveData.Is<Browse>())
      98              :             {
      99            0 :                 if (!other.resolveData.Is<Browse>())
     100              :                 {
     101            0 :                     return false;
     102              :                 }
     103              : 
     104            0 :                 auto & a = resolveData.Get<Browse>();
     105            0 :                 auto & b = other.resolveData.Get<Browse>();
     106            0 :                 return (a.filter == b.filter && a.type == b.type);
     107              :             }
     108              : 
     109            0 :             if (resolveData.Is<Resolve>())
     110              :             {
     111            0 :                 if (!other.resolveData.Is<Resolve>())
     112              :                 {
     113            0 :                     return false;
     114              :                 }
     115            0 :                 auto & a = resolveData.Get<Resolve>();
     116            0 :                 auto & b = other.resolveData.Get<Resolve>();
     117              : 
     118            0 :                 return a.peerId == b.peerId;
     119              :             }
     120              : 
     121            0 :             if (resolveData.Is<IpResolve>())
     122              :             {
     123            0 :                 if (!other.resolveData.Is<IpResolve>())
     124              :                 {
     125            0 :                     return false;
     126              :                 }
     127            0 :                 auto & a = resolveData.Get<IpResolve>();
     128            0 :                 auto & b = other.resolveData.Get<IpResolve>();
     129              : 
     130            0 :                 return a.hostName == b.hostName;
     131              :             }
     132            0 :             return false;
     133              :         }
     134              : 
     135           48 :         bool MatchesIpResolve(SerializedQNameIterator hostName) const
     136              :         {
     137           48 :             return resolveData.Is<IpResolve>() && (hostName == resolveData.Get<IpResolve>().hostName.Content());
     138              :         }
     139            6 :         bool Matches(const chip::PeerId & peer) const
     140              :         {
     141            6 :             return resolveData.Is<Resolve>() && (resolveData.Get<Resolve>().peerId == peer);
     142              :         }
     143              :         bool Matches(const chip::Dnssd::DiscoveredNodeData & data, chip::Dnssd::DiscoveryType type) const
     144              :         {
     145              :             if (!resolveData.Is<Browse>())
     146              :             {
     147              :                 return false;
     148              :             }
     149              : 
     150              :             auto & browse = resolveData.Get<Browse>();
     151              : 
     152              :             if (browse.type != type)
     153              :             {
     154              :                 return false;
     155              :             }
     156              :             auto & nodeData = data.Get<chip::Dnssd::CommissionNodeData>();
     157              : 
     158              :             switch (browse.filter.type)
     159              :             {
     160              :             case chip::Dnssd::DiscoveryFilterType::kNone:
     161              :                 return true;
     162              :             case chip::Dnssd::DiscoveryFilterType::kShortDiscriminator:
     163              :                 return browse.filter.code == static_cast<uint64_t>((nodeData.longDiscriminator >> 8) & 0x0F);
     164              :             case chip::Dnssd::DiscoveryFilterType::kLongDiscriminator:
     165              :                 return browse.filter.code == nodeData.longDiscriminator;
     166              :             case chip::Dnssd::DiscoveryFilterType::kVendorId:
     167              :                 return browse.filter.code == nodeData.vendorId;
     168              :             case chip::Dnssd::DiscoveryFilterType::kDeviceType:
     169              :                 return browse.filter.code == nodeData.deviceType;
     170              :             case chip::Dnssd::DiscoveryFilterType::kCommissioningMode:
     171              :                 return browse.filter.code == nodeData.commissioningMode;
     172              :             case chip::Dnssd::DiscoveryFilterType::kInstanceName:
     173              :                 return strncmp(browse.filter.instanceName, nodeData.instanceName,
     174              :                                chip::Dnssd::Commission::kInstanceNameMaxLength + 1) == 0;
     175              :             case chip::Dnssd::DiscoveryFilterType::kCommissioner:
     176              :             case chip::Dnssd::DiscoveryFilterType::kCompressedFabricId:
     177              :             default:
     178              :                 // These are for other discovery types.
     179              :                 return false;
     180              :             }
     181              :         }
     182              : 
     183          583 :         bool IsEmpty() const { return !resolveData.Valid(); }
     184           17 :         bool IsResolve() const { return resolveData.Is<Resolve>(); }
     185           56 :         bool IsBrowse() const { return resolveData.Is<Browse>(); }
     186            0 :         bool IsIpResolve() const { return resolveData.Is<IpResolve>(); }
     187          290 :         void Clear() { resolveData = DataType(); }
     188              : 
     189              :         // Called when this scheduled attempt will replace an existing scheduled
     190              :         // attempt, because either they match or we have run out of attempt
     191              :         // slots.  When this happens, we want to propagate the consumer count
     192              :         // from the existing attempt to the new one, if they're matching resolve
     193              :         // attempts.
     194           17 :         void WillCoalesceWith(const ScheduledAttempt & existing)
     195              :         {
     196           17 :             if (!IsResolve())
     197              :             {
     198              :                 // Consumer count is only tracked for resolve requests
     199            4 :                 return;
     200              :             }
     201              : 
     202           13 :             if (!existing.Matches(*this))
     203              :             {
     204              :                 // Out of attempt slots, so just dropping the existing attempt.
     205           12 :                 return;
     206              :             }
     207              : 
     208              :             // Adding another consumer to the same query. Propagate along the
     209              :             // consumer count to our new attempt, which will replace the
     210              :             // existing one.
     211            1 :             resolveData.Get<Resolve>().consumerCount = existing.resolveData.Get<Resolve>().consumerCount + 1;
     212              :         }
     213              : 
     214            0 :         void ConsumerRemoved()
     215              :         {
     216            0 :             if (!IsResolve())
     217              :             {
     218            0 :                 return;
     219              :             }
     220              : 
     221            0 :             auto & count = resolveData.Get<Resolve>().consumerCount;
     222            0 :             if (count > 0)
     223              :             {
     224            0 :                 --count;
     225              :             }
     226              : 
     227            0 :             if (count == 0)
     228              :             {
     229            0 :                 Clear();
     230              :             }
     231              :         }
     232              : 
     233            0 :         const Browse & BrowseData() const { return resolveData.Get<Browse>(); }
     234            0 :         const Resolve & ResolveData() const { return resolveData.Get<Resolve>(); }
     235            0 :         const IpResolve & IpResolveData() const { return resolveData.Get<IpResolve>(); }
     236              : 
     237              :         using DataType = chip::Variant<Browse, Resolve, IpResolve>;
     238              : 
     239              :         DataType resolveData;
     240              : 
     241              :         // First packet send is marked separately: minMDNS logic can choose
     242              :         // to first send a unicast query followed by a multicast one.
     243              :         bool firstSend = false;
     244              :     };
     245              : 
     246          315 :     ActiveResolveAttempts(chip::System::Clock::ClockBase * clock) : mClock(clock) { Reset(); }
     247              : 
     248              :     /// Clear out the internal queue
     249              :     void Reset();
     250              : 
     251              :     /// Mark a resolution as a success, removing it from the internal list
     252              :     void Complete(const chip::PeerId & peerId);
     253              :     void CompleteIpResolution(SerializedQNameIterator targetHostName);
     254              : 
     255              :     /// Mark all browse-type scheduled attemptes as a success, removing them
     256              :     /// from the internal list.
     257              :     CHIP_ERROR CompleteAllBrowses();
     258              : 
     259              :     /// Note that resolve attempts for the given peer id now have one fewer
     260              :     /// consumer.
     261              :     void NodeIdResolutionNoLongerNeeded(const chip::PeerId & peerId);
     262              : 
     263              :     /// Mark that a resolution is pending, adding it to the internal list
     264              :     ///
     265              :     /// Once this complete, this peer id will be returned immediately
     266              :     /// by NextScheduled (potentially with others as well)
     267              :     void MarkPending(const chip::PeerId & peerId);
     268              :     void MarkPending(const chip::Dnssd::DiscoveryFilter & filter, const chip::Dnssd::DiscoveryType type);
     269              :     void MarkPending(ScheduledAttempt::IpResolve && resolve);
     270              : 
     271              :     // Get minimum time until the next pending reply is required.
     272              :     //
     273              :     // Returns missing if no actively tracked elements exist.
     274              :     std::optional<chip::System::Clock::Timeout> GetTimeUntilNextExpectedResponse() const;
     275              : 
     276              :     // Get the peer Id that needs scheduling for a query
     277              :     //
     278              :     // Assumes that the resolution is being sent and will apply internal
     279              :     // query logic. This means:
     280              :     //  - internal tracking of 'next due time' will updated as 'request sent
     281              :     //    now'
     282              :     //  - there is NO sorting implied by this call. Returned value will be
     283              :     //    any peer that needs a new request sent
     284              :     std::optional<ScheduledAttempt> NextScheduled();
     285              : 
     286              :     /// Check if any of the pending queries are for the given host name for
     287              :     /// IP resolution.
     288              :     bool IsWaitingForIpResolutionFor(SerializedQNameIterator hostName) const;
     289              : 
     290              :     /// Determines if address resolution for the given peer ID is required
     291              :     ///
     292              :     /// IP Addresses are required for active operational discovery of specific peers
     293              :     /// or if an active browse is being performed.
     294              :     bool ShouldResolveIpAddress(chip::PeerId peerId) const;
     295              : 
     296              :     /// Check if a browse operation is active for the given discovery type
     297              :     bool HasBrowseFor(chip::Dnssd::DiscoveryType type) const;
     298              : 
     299              : private:
     300              :     struct RetryEntry
     301              :     {
     302              :         ScheduledAttempt attempt;
     303              :         // When a reply is expected for this item
     304              :         chip::System::Clock::Timestamp queryDueTime;
     305              : 
     306              :         // Next expected delay for sending if reply is not reached by
     307              :         // 'queryDueTimeMs'
     308              :         //
     309              :         // Based on RFC 6762 expectations are:
     310              :         //    - the interval between the first two queries MUST be at least
     311              :         //      one second
     312              :         //    - the intervals between successive queries MUST increase by at
     313              :         //      least a factor of two
     314              :         chip::System::Clock::Timeout nextRetryDelay = chip::System::Clock::Seconds16(1);
     315              :     };
     316              :     void MarkPending(ScheduledAttempt && attempt);
     317              :     chip::System::Clock::ClockBase * mClock;
     318              :     RetryEntry mRetryQueue[kRetryQueueSize];
     319              : };
     320              : 
     321              : } // namespace Minimal
     322              : } // namespace mdns
        

Generated by: LCOV version 2.0-1