LCOV - code coverage report
Current view: top level - lib/dnssd - Advertiser_ImplMinimalMdns.cpp (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 309 378 81.7 %
Date: 2024-02-15 08:20:41 Functions: 27 30 90.0 %

          Line data    Source code
       1             : /*
       2             :  *
       3             :  *    Copyright (c) 2020-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             : 
      18             : #include "Advertiser.h"
      19             : 
      20             : #include <inttypes.h>
      21             : #include <stdio.h>
      22             : 
      23             : #include "MinimalMdnsServer.h"
      24             : #include "ServiceNaming.h"
      25             : 
      26             : #include <app/icd/server/ICDServerConfig.h>
      27             : #include <crypto/RandUtils.h>
      28             : #include <lib/dnssd/Advertiser_ImplMinimalMdnsAllocator.h>
      29             : #include <lib/dnssd/minimal_mdns/AddressPolicy.h>
      30             : #include <lib/dnssd/minimal_mdns/ResponseSender.h>
      31             : #include <lib/dnssd/minimal_mdns/Server.h>
      32             : #include <lib/dnssd/minimal_mdns/core/FlatAllocatedQName.h>
      33             : #include <lib/dnssd/minimal_mdns/responders/IP.h>
      34             : #include <lib/dnssd/minimal_mdns/responders/Ptr.h>
      35             : #include <lib/dnssd/minimal_mdns/responders/QueryResponder.h>
      36             : #include <lib/dnssd/minimal_mdns/responders/Srv.h>
      37             : #include <lib/dnssd/minimal_mdns/responders/Txt.h>
      38             : #include <lib/support/BytesToHex.h>
      39             : #include <lib/support/CHIPMem.h>
      40             : #include <lib/support/IntrusiveList.h>
      41             : #include <lib/support/StringBuilder.h>
      42             : 
      43             : // Enable detailed mDNS logging for received queries
      44             : #undef DETAIL_LOGGING
      45             : // #define DETAIL_LOGGING
      46             : 
      47             : namespace chip {
      48             : namespace Dnssd {
      49             : namespace {
      50             : 
      51             : using chip::Platform::UniquePtr;
      52             : using namespace mdns::Minimal;
      53             : 
      54             : #ifdef DETAIL_LOGGING
      55             : const char * ToString(QClass qClass)
      56             : {
      57             :     switch (qClass)
      58             :     {
      59             :     case QClass::IN:
      60             :         return "IN";
      61             :     default:
      62             :         return "???";
      63             :     }
      64             : }
      65             : 
      66             : const char * ToString(QType qType)
      67             : {
      68             :     switch (qType)
      69             :     {
      70             :     case QType::ANY:
      71             :         return "ANY";
      72             :     case QType::A:
      73             :         return "A";
      74             :     case QType::AAAA:
      75             :         return "AAAA";
      76             :     case QType::TXT:
      77             :         return "TXT";
      78             :     case QType::SRV:
      79             :         return "SRV";
      80             :     case QType::PTR:
      81             :         return "PTR";
      82             :     default:
      83             :         return "???";
      84             :     }
      85             : }
      86             : 
      87             : void LogQuery(const QueryData & data)
      88             : {
      89             :     StringBuilder<128> logString;
      90             : 
      91             :     logString.Add("QUERY ").Add(ToString(data.GetClass())).Add("/").Add(ToString(data.GetType())).Add(": ");
      92             : 
      93             :     SerializedQNameIterator name = data.GetName();
      94             :     while (name.Next())
      95             :     {
      96             :         logString.Add(name.Value()).Add(".");
      97             :     }
      98             : 
      99             :     ChipLogDetail(Discovery, "%s", logString.c_str());
     100             : }
     101             : #else
     102          24 : void LogQuery(const QueryData & data) {}
     103             : #endif
     104             : 
     105             : // Max number of records for operational = PTR, SRV, TXT, A, AAAA, I subtype.
     106             : constexpr size_t kMaxOperationalRecords = 6;
     107             : 
     108             : /// Represents an allocated operational responder.
     109             : ///
     110             : /// Wraps a QueryResponderAllocator.
     111             : class OperationalQueryAllocator : public chip::IntrusiveListNodeBase<>
     112             : {
     113             : public:
     114             :     using Allocator = QueryResponderAllocator<kMaxOperationalRecords>;
     115             : 
     116             :     /// Prefer to use `::New` for allocations instead of this direct call
     117           7 :     explicit OperationalQueryAllocator(Allocator * allocator) : mAllocator(allocator) {}
     118           7 :     ~OperationalQueryAllocator()
     119             :     {
     120           7 :         chip::Platform::Delete(mAllocator);
     121           7 :         mAllocator = nullptr;
     122           7 :     }
     123             : 
     124         125 :     Allocator * GetAllocator() { return mAllocator; }
     125             :     const Allocator * GetAllocator() const { return mAllocator; }
     126             : 
     127             :     /// Allocate a new entry for this type.
     128             :     ///
     129             :     /// May return null on allocation failures.
     130           7 :     static OperationalQueryAllocator * New()
     131             :     {
     132           7 :         Allocator * allocator = chip::Platform::New<Allocator>();
     133             : 
     134           7 :         if (allocator == nullptr)
     135             :         {
     136           0 :             return nullptr;
     137             :         }
     138             : 
     139           7 :         OperationalQueryAllocator * result = chip::Platform::New<OperationalQueryAllocator>(allocator);
     140           7 :         if (result == nullptr)
     141             :         {
     142           0 :             chip::Platform::Delete(allocator);
     143           0 :             return nullptr;
     144             :         }
     145             : 
     146           7 :         return result;
     147             :     }
     148             : 
     149             : private:
     150             :     Allocator * mAllocator = nullptr;
     151             : };
     152             : 
     153             : enum BroadcastAdvertiseType
     154             : {
     155             :     kStarted,     // Advertise at startup of all records added, as required by RFC 6762.
     156             :     kRemovingAll, // sent a TTL 0 for all records, as records are removed
     157             : };
     158             : 
     159             : class AdvertiserMinMdns : public ServiceAdvertiser,
     160             :                           public MdnsPacketDelegate, // receive query packets
     161             :                           public ParserDelegate      // parses queries
     162             : {
     163             : public:
     164           2 :     AdvertiserMinMdns() : mResponseSender(&GlobalMinimalMdnsServer::Server())
     165             :     {
     166           2 :         GlobalMinimalMdnsServer::Instance().SetQueryDelegate(this);
     167             : 
     168           2 :         CHIP_ERROR err = mResponseSender.AddQueryResponder(mQueryResponderAllocatorCommissionable.GetQueryResponder());
     169             : 
     170           2 :         if (err != CHIP_NO_ERROR)
     171             :         {
     172           0 :             ChipLogError(Discovery, "Failed to set up commissionable responder: %" CHIP_ERROR_FORMAT, err.Format());
     173             :         }
     174             : 
     175           2 :         err = mResponseSender.AddQueryResponder(mQueryResponderAllocatorCommissioner.GetQueryResponder());
     176           2 :         if (err != CHIP_NO_ERROR)
     177             :         {
     178           0 :             ChipLogError(Discovery, "Failed to set up commissioner responder: %" CHIP_ERROR_FORMAT, err.Format());
     179             :         }
     180           2 :     }
     181           2 :     ~AdvertiserMinMdns() override { ClearServices(); }
     182             : 
     183             :     // Service advertiser
     184             :     CHIP_ERROR Init(chip::Inet::EndPointManager<chip::Inet::UDPEndPoint> * udpEndPointManager) override;
     185           0 :     bool IsInitialized() override { return mIsInitialized; }
     186             :     void Shutdown() override;
     187             :     CHIP_ERROR RemoveServices() override;
     188             :     CHIP_ERROR Advertise(const OperationalAdvertisingParameters & params) override;
     189             :     CHIP_ERROR Advertise(const CommissionAdvertisingParameters & params) override;
     190             :     CHIP_ERROR FinalizeServiceUpdate() override;
     191             :     CHIP_ERROR GetCommissionableInstanceName(char * instanceName, size_t maxLength) const override;
     192             :     CHIP_ERROR UpdateCommissionableInstanceName() override;
     193             : 
     194             :     // MdnsPacketDelegate
     195             :     void OnMdnsPacketData(const BytesRange & data, const chip::Inet::IPPacketInfo * info) override;
     196             : 
     197             :     // ParserDelegate
     198          24 :     void OnHeader(ConstHeaderRef & header) override { mMessageId = header.GetMessageId(); }
     199           0 :     void OnResource(ResourceType type, const ResourceData & data) override {}
     200             :     void OnQuery(const QueryData & data) override;
     201             : 
     202             : private:
     203             :     /// Advertise available records configured within the server.
     204             :     ///
     205             :     /// Establishes a type of 'Advertise all currently configured items'
     206             :     /// for a specific purpose (e.g. boot time advertises everything, shut-down
     207             :     /// removes all records by advertising a 0 TTL)
     208             :     void AdvertiseRecords(BroadcastAdvertiseType type);
     209             : 
     210             :     FullQName GetCommissioningTxtEntries(const CommissionAdvertisingParameters & params);
     211             :     FullQName GetOperationalTxtEntries(OperationalQueryAllocator::Allocator * allocator,
     212             :                                        const OperationalAdvertisingParameters & params);
     213             : 
     214             :     struct CommonTxtEntryStorage
     215             :     {
     216             :         // +2 for all to account for '=' and terminating nullchar
     217             :         char sessionIdleIntervalBuf[KeySize(TxtFieldKey::kSessionIdleInterval) + ValSize(TxtFieldKey::kSessionIdleInterval) + 2];
     218             :         char sessionActiveIntervalBuf[KeySize(TxtFieldKey::kSessionActiveInterval) + ValSize(TxtFieldKey::kSessionActiveInterval) +
     219             :                                       2];
     220             :         char sessionActiveThresholdBuf[KeySize(TxtFieldKey::kSessionActiveThreshold) +
     221             :                                        ValSize(TxtFieldKey::kSessionActiveThreshold) + 2];
     222             :         char tcpSupportedBuf[KeySize(TxtFieldKey::kTcpSupported) + ValSize(TxtFieldKey::kTcpSupported) + 2];
     223             :         char operatingICDAsLITBuf[KeySize(TxtFieldKey::kLongIdleTimeICD) + ValSize(TxtFieldKey::kLongIdleTimeICD) + 2];
     224             :     };
     225             :     template <class Derived>
     226          20 :     CHIP_ERROR AddCommonTxtEntries(const BaseAdvertisingParams<Derived> & params, CommonTxtEntryStorage & storage,
     227             :                                    char ** txtFields, size_t & numTxtFields)
     228             :     {
     229          20 :         auto optionalMrp = params.GetLocalMRPConfig();
     230             : 
     231          20 :         if (optionalMrp.HasValue())
     232             :         {
     233           5 :             auto mrp = optionalMrp.Value();
     234             : 
     235             :             // An ICD operating as a LIT shall not advertise its slow polling interval.
     236             :             // Don't include the SII key in the advertisement when operating as so.
     237           5 :             if (params.GetICDModeToAdvertise() != ICDModeAdvertise::kLIT)
     238             :             {
     239           5 :                 if (mrp.mIdleRetransTimeout > kMaxRetryInterval)
     240             :                 {
     241           0 :                     ChipLogProgress(Discovery,
     242             :                                     "MRP retry interval idle value exceeds allowed range of 1 hour, using maximum available");
     243           0 :                     mrp.mIdleRetransTimeout = kMaxRetryInterval;
     244             :                 }
     245           5 :                 size_t writtenCharactersNumber =
     246           5 :                     static_cast<size_t>(snprintf(storage.sessionIdleIntervalBuf, sizeof(storage.sessionIdleIntervalBuf),
     247             :                                                  "SII=%" PRIu32, mrp.mIdleRetransTimeout.count()));
     248           5 :                 VerifyOrReturnError((writtenCharactersNumber > 0) &&
     249             :                                         (writtenCharactersNumber < sizeof(storage.sessionIdleIntervalBuf)),
     250             :                                     CHIP_ERROR_INVALID_STRING_LENGTH);
     251             : 
     252           5 :                 txtFields[numTxtFields++] = storage.sessionIdleIntervalBuf;
     253             :             }
     254             : 
     255             :             {
     256           5 :                 if (mrp.mActiveRetransTimeout > kMaxRetryInterval)
     257             :                 {
     258           2 :                     ChipLogProgress(Discovery,
     259             :                                     "MRP retry interval active value exceeds allowed range of 1 hour, using maximum available");
     260           2 :                     mrp.mActiveRetransTimeout = kMaxRetryInterval;
     261             :                 }
     262           5 :                 size_t writtenCharactersNumber =
     263           5 :                     static_cast<size_t>(snprintf(storage.sessionActiveIntervalBuf, sizeof(storage.sessionActiveIntervalBuf),
     264             :                                                  "SAI=%" PRIu32, mrp.mActiveRetransTimeout.count()));
     265           5 :                 VerifyOrReturnError((writtenCharactersNumber > 0) &&
     266             :                                         (writtenCharactersNumber < sizeof(storage.sessionActiveIntervalBuf)),
     267             :                                     CHIP_ERROR_INVALID_STRING_LENGTH);
     268           5 :                 txtFields[numTxtFields++] = storage.sessionActiveIntervalBuf;
     269             :             }
     270             : 
     271             :             {
     272           5 :                 size_t writtenCharactersNumber =
     273           5 :                     static_cast<size_t>(snprintf(storage.sessionActiveThresholdBuf, sizeof(storage.sessionActiveThresholdBuf),
     274           5 :                                                  "SAT=%u", mrp.mActiveThresholdTime.count()));
     275           5 :                 VerifyOrReturnError((writtenCharactersNumber > 0) &&
     276             :                                         (writtenCharactersNumber < sizeof(storage.sessionActiveThresholdBuf)),
     277             :                                     CHIP_ERROR_INVALID_STRING_LENGTH);
     278           5 :                 txtFields[numTxtFields++] = storage.sessionActiveThresholdBuf;
     279             :             }
     280             :         }
     281          20 :         if (params.GetTcpSupported().HasValue())
     282             :         {
     283           0 :             size_t writtenCharactersNumber = static_cast<size_t>(
     284           0 :                 snprintf(storage.tcpSupportedBuf, sizeof(storage.tcpSupportedBuf), "T=%d", params.GetTcpSupported().Value()));
     285           0 :             VerifyOrReturnError((writtenCharactersNumber > 0) && (writtenCharactersNumber < sizeof(storage.tcpSupportedBuf)),
     286             :                                 CHIP_ERROR_INVALID_STRING_LENGTH);
     287           0 :             txtFields[numTxtFields++] = storage.tcpSupportedBuf;
     288             :         }
     289          20 :         if (params.GetICDModeToAdvertise() != ICDModeAdvertise::kNone)
     290             :         {
     291           2 :             size_t writtenCharactersNumber =
     292           2 :                 static_cast<size_t>(snprintf(storage.operatingICDAsLITBuf, sizeof(storage.operatingICDAsLITBuf), "ICD=%d",
     293           2 :                                              (params.GetICDModeToAdvertise() == ICDModeAdvertise::kLIT)));
     294           2 :             VerifyOrReturnError((writtenCharactersNumber > 0) && (writtenCharactersNumber < sizeof(storage.operatingICDAsLITBuf)),
     295             :                                 CHIP_ERROR_INVALID_STRING_LENGTH);
     296           2 :             txtFields[numTxtFields++] = storage.operatingICDAsLITBuf;
     297             :         }
     298          20 :         return CHIP_NO_ERROR;
     299          20 :     }
     300             : 
     301             :     IntrusiveList<OperationalQueryAllocator> mOperationalResponders;
     302             : 
     303             :     // Max number of records for commissionable = 7 x PTR (base + 6 sub types - _S, _L, _D, _T, _C, _A), SRV, TXT, A, AAAA
     304             :     static constexpr size_t kMaxCommissionRecords = 11;
     305             :     QueryResponderAllocator<kMaxCommissionRecords> mQueryResponderAllocatorCommissionable;
     306             :     QueryResponderAllocator<kMaxCommissionRecords> mQueryResponderAllocatorCommissioner;
     307             : 
     308             :     OperationalQueryAllocator::Allocator * FindOperationalAllocator(const FullQName & qname);
     309             :     OperationalQueryAllocator::Allocator * FindEmptyOperationalAllocator();
     310             : 
     311             :     void ClearServices();
     312             : 
     313             :     ResponseSender mResponseSender;
     314             :     uint8_t mCommissionableInstanceName[sizeof(uint64_t)];
     315             : 
     316             :     bool mIsInitialized = false;
     317             : 
     318             :     // current request handling
     319             :     const chip::Inet::IPPacketInfo * mCurrentSource = nullptr;
     320             :     uint16_t mMessageId                             = 0;
     321             : 
     322             :     const char * mEmptyTextEntries[1] = {
     323             :         "=",
     324             :     };
     325             : };
     326             : 
     327          24 : void AdvertiserMinMdns::OnMdnsPacketData(const BytesRange & data, const chip::Inet::IPPacketInfo * info)
     328             : {
     329             : #ifdef DETAIL_LOGGING
     330             :     char srcAddressString[chip::Inet::IPAddress::kMaxStringLength];
     331             :     VerifyOrDie(info->SrcAddress.ToString(srcAddressString) != nullptr);
     332             :     ChipLogDetail(Discovery, "Received an mDNS query from %s", srcAddressString);
     333             : #endif
     334             : 
     335          24 :     mCurrentSource = info;
     336          24 :     if (!ParsePacket(data, this))
     337             :     {
     338           0 :         ChipLogError(Discovery, "Failed to parse mDNS query");
     339             :     }
     340          24 :     mCurrentSource = nullptr;
     341          24 : }
     342             : 
     343          24 : void AdvertiserMinMdns::OnQuery(const QueryData & data)
     344             : {
     345          24 :     if (mCurrentSource == nullptr)
     346             :     {
     347           0 :         ChipLogError(Discovery, "INTERNAL CONSISTENCY ERROR: missing query source");
     348           0 :         return;
     349             :     }
     350             : 
     351          24 :     LogQuery(data);
     352             : 
     353          24 :     const ResponseConfiguration defaultResponseConfiguration;
     354          24 :     CHIP_ERROR err = mResponseSender.Respond(mMessageId, data, mCurrentSource, defaultResponseConfiguration);
     355          24 :     if (err != CHIP_NO_ERROR)
     356             :     {
     357           0 :         ChipLogError(Discovery, "Failed to reply to query: %" CHIP_ERROR_FORMAT, err.Format());
     358             :     }
     359          24 : }
     360             : 
     361          16 : CHIP_ERROR AdvertiserMinMdns::Init(chip::Inet::EndPointManager<chip::Inet::UDPEndPoint> * udpEndPointManager)
     362             : {
     363             :     // TODO: Per API documentation, Init() should be a no-op if mIsInitialized
     364             :     // is true.  But we don't handle updates to our set of interfaces right now,
     365             :     // so rely on the logic in this function to shut down and restart the
     366             :     // GlobalMinimalMdnsServer to handle that.
     367          16 :     GlobalMinimalMdnsServer::Server().ShutdownEndpoints();
     368             : 
     369          16 :     if (!mIsInitialized)
     370             :     {
     371           2 :         UpdateCommissionableInstanceName();
     372             :     }
     373             : 
     374             :     // Re-set the server in the response sender in case this has been swapped in the
     375             :     // GlobalMinimalMdnsServer (used for testing).
     376          16 :     mResponseSender.SetServer(&GlobalMinimalMdnsServer::Server());
     377             : 
     378          16 :     ReturnErrorOnFailure(GlobalMinimalMdnsServer::Instance().StartServer(udpEndPointManager, kMdnsPort));
     379             : 
     380          16 :     ChipLogProgress(Discovery, "CHIP minimal mDNS started advertising.");
     381             : 
     382          16 :     AdvertiseRecords(BroadcastAdvertiseType::kStarted);
     383             : 
     384          16 :     mIsInitialized = true;
     385             : 
     386          16 :     return CHIP_NO_ERROR;
     387             : }
     388             : 
     389           3 : void AdvertiserMinMdns::Shutdown()
     390             : {
     391           3 :     VerifyOrReturn(mIsInitialized);
     392             : 
     393           2 :     AdvertiseRecords(BroadcastAdvertiseType::kRemovingAll);
     394             : 
     395           2 :     GlobalMinimalMdnsServer::Server().Shutdown();
     396           2 :     mIsInitialized = false;
     397             : }
     398             : 
     399          20 : CHIP_ERROR AdvertiserMinMdns::RemoveServices()
     400             : {
     401          20 :     VerifyOrReturnError(mIsInitialized, CHIP_ERROR_INCORRECT_STATE);
     402             : 
     403             :     // Send a "goodbye" packet for each RR being removed, as defined in RFC 6762.
     404             :     // This allows mDNS clients to remove stale cached records which may not be re-added with
     405             :     // subsequent Advertise() calls. In the case the same records are re-added, this extra
     406             :     // is not harmful though suboptimal, so this is a subject to improvement in the future.
     407          19 :     AdvertiseRecords(BroadcastAdvertiseType::kRemovingAll);
     408          19 :     ClearServices();
     409             : 
     410          19 :     return CHIP_NO_ERROR;
     411             : }
     412             : 
     413          21 : void AdvertiserMinMdns::ClearServices()
     414             : {
     415          28 :     while (mOperationalResponders.begin() != mOperationalResponders.end())
     416             :     {
     417           7 :         auto it = mOperationalResponders.begin();
     418             : 
     419             :         // Need to free the memory once it is out of the list
     420           7 :         OperationalQueryAllocator * ptr = &*it;
     421             : 
     422             :         // Mark as unused
     423           7 :         ptr->GetAllocator()->Clear();
     424             : 
     425           7 :         CHIP_ERROR err = mResponseSender.RemoveQueryResponder(ptr->GetAllocator()->GetQueryResponder());
     426           7 :         if (err != CHIP_NO_ERROR)
     427             :         {
     428           0 :             ChipLogError(Discovery, "Failed to remove query responder: %" CHIP_ERROR_FORMAT, err.Format());
     429             :         }
     430             : 
     431           7 :         mOperationalResponders.Remove(ptr);
     432             : 
     433             :         // Finally release the memory
     434           7 :         chip::Platform::Delete(ptr);
     435             :     }
     436             : 
     437          21 :     mQueryResponderAllocatorCommissionable.Clear();
     438          21 :     mQueryResponderAllocatorCommissioner.Clear();
     439          21 : }
     440             : 
     441           8 : OperationalQueryAllocator::Allocator * AdvertiserMinMdns::FindOperationalAllocator(const FullQName & qname)
     442             : {
     443          19 :     for (auto & it : mOperationalResponders)
     444             :     {
     445          12 :         if (it.GetAllocator()->GetResponder(QType::SRV, qname) != nullptr)
     446             :         {
     447           1 :             return it.GetAllocator();
     448             :         }
     449             :     }
     450             : 
     451           7 :     return nullptr;
     452             : }
     453             : 
     454           7 : OperationalQueryAllocator::Allocator * AdvertiserMinMdns::FindEmptyOperationalAllocator()
     455             : {
     456           7 :     OperationalQueryAllocator * result = OperationalQueryAllocator::New();
     457             : 
     458           7 :     if (result == nullptr)
     459             :     {
     460           0 :         return nullptr;
     461             :     }
     462             : 
     463           7 :     CHIP_ERROR err = mResponseSender.AddQueryResponder(result->GetAllocator()->GetQueryResponder());
     464             : 
     465           7 :     if (err != CHIP_NO_ERROR)
     466             :     {
     467           0 :         ChipLogError(Discovery, "Failed to register query responder: %" CHIP_ERROR_FORMAT, err.Format());
     468           0 :         Platform::Delete(result);
     469           0 :         return nullptr;
     470             :     }
     471             : 
     472           7 :     mOperationalResponders.PushBack(result);
     473           7 :     return result->GetAllocator();
     474             : }
     475             : 
     476           8 : CHIP_ERROR AdvertiserMinMdns::Advertise(const OperationalAdvertisingParameters & params)
     477             : {
     478           8 :     VerifyOrReturnError(mIsInitialized, CHIP_ERROR_INCORRECT_STATE);
     479             : 
     480           8 :     char nameBuffer[Operational::kInstanceNameMaxLength + 1] = "";
     481             : 
     482             :     // need to set server name
     483           8 :     ReturnErrorOnFailure(MakeInstanceName(nameBuffer, sizeof(nameBuffer), params.GetPeerId()));
     484             : 
     485           8 :     QNamePart nameCheckParts[]  = { nameBuffer, kOperationalServiceName, kOperationalProtocol, kLocalDomain };
     486           8 :     FullQName nameCheck         = FullQName(nameCheckParts);
     487           8 :     auto * operationalAllocator = FindOperationalAllocator(nameCheck);
     488           8 :     if (operationalAllocator != nullptr)
     489             :     {
     490           1 :         operationalAllocator->Clear();
     491             :     }
     492             :     else
     493             :     {
     494           7 :         operationalAllocator = FindEmptyOperationalAllocator();
     495           7 :         if (operationalAllocator == nullptr)
     496             :         {
     497           0 :             ChipLogError(Discovery, "Failed to find an open operational allocator");
     498           0 :             return CHIP_ERROR_NO_MEMORY;
     499             :         }
     500             :     }
     501             : 
     502           8 :     FullQName serviceName = operationalAllocator->AllocateQName(kOperationalServiceName, kOperationalProtocol, kLocalDomain);
     503             :     FullQName instanceName =
     504           8 :         operationalAllocator->AllocateQName(nameBuffer, kOperationalServiceName, kOperationalProtocol, kLocalDomain);
     505             : 
     506           8 :     ReturnErrorOnFailure(MakeHostName(nameBuffer, sizeof(nameBuffer), params.GetMac()));
     507           8 :     FullQName hostName = operationalAllocator->AllocateQName(nameBuffer, kLocalDomain);
     508             : 
     509           8 :     if ((serviceName.nameCount == 0) || (instanceName.nameCount == 0) || (hostName.nameCount == 0))
     510             :     {
     511           0 :         ChipLogError(Discovery, "Failed to allocate QNames.");
     512           0 :         return CHIP_ERROR_NO_MEMORY;
     513             :     }
     514             : 
     515          16 :     if (!operationalAllocator->AddResponder<PtrResponder>(serviceName, instanceName)
     516           8 :              .SetReportAdditional(instanceName)
     517           8 :              .SetReportInServiceListing(true)
     518           8 :              .IsValid())
     519             :     {
     520           0 :         ChipLogError(Discovery, "Failed to add service PTR record mDNS responder");
     521           0 :         return CHIP_ERROR_NO_MEMORY;
     522             :     }
     523             : 
     524             :     // We are the sole owner of our instanceName, so records keyed on the
     525             :     // instanceName should have the cache-flush bit set.
     526           8 :     SrvResourceRecord srvRecord(instanceName, hostName, params.GetPort());
     527           8 :     srvRecord.SetCacheFlush(true);
     528           8 :     if (!operationalAllocator->AddResponder<SrvResponder>(srvRecord).SetReportAdditional(hostName).IsValid())
     529             :     {
     530           0 :         ChipLogError(Discovery, "Failed to add SRV record mDNS responder");
     531           0 :         return CHIP_ERROR_NO_MEMORY;
     532             :     }
     533             : 
     534           8 :     TxtResourceRecord txtRecord(instanceName, GetOperationalTxtEntries(operationalAllocator, params));
     535           8 :     txtRecord.SetCacheFlush(true);
     536           8 :     if (!operationalAllocator->AddResponder<TxtResponder>(txtRecord).SetReportAdditional(hostName).IsValid())
     537             :     {
     538           0 :         ChipLogError(Discovery, "Failed to add TXT record mDNS responder");
     539           0 :         return CHIP_ERROR_NO_MEMORY;
     540             :     }
     541             : 
     542           8 :     if (!operationalAllocator->AddResponder<IPv6Responder>(hostName).IsValid())
     543             :     {
     544           0 :         ChipLogError(Discovery, "Failed to add IPv6 mDNS responder");
     545           0 :         return CHIP_ERROR_NO_MEMORY;
     546             :     }
     547             : 
     548           8 :     if (params.IsIPv4Enabled())
     549             :     {
     550           8 :         if (!operationalAllocator->AddResponder<IPv4Responder>(hostName).IsValid())
     551             :         {
     552           0 :             ChipLogError(Discovery, "Failed to add IPv4 mDNS responder");
     553           0 :             return CHIP_ERROR_NO_MEMORY;
     554             :         }
     555             :     }
     556           8 :     MakeServiceSubtype(nameBuffer, sizeof(nameBuffer),
     557           8 :                        DiscoveryFilter(DiscoveryFilterType::kCompressedFabricId, params.GetPeerId().GetCompressedFabricId()));
     558           8 :     FullQName compressedFabricIdSubtype = operationalAllocator->AllocateQName(
     559             :         nameBuffer, kSubtypeServiceNamePart, kOperationalServiceName, kOperationalProtocol, kLocalDomain);
     560           8 :     ReturnErrorCodeIf(compressedFabricIdSubtype.nameCount == 0, CHIP_ERROR_NO_MEMORY);
     561             : 
     562          16 :     if (!operationalAllocator->AddResponder<PtrResponder>(compressedFabricIdSubtype, instanceName)
     563           8 :              .SetReportAdditional(instanceName)
     564           8 :              .SetReportInServiceListing(true)
     565           8 :              .IsValid())
     566             :     {
     567           0 :         ChipLogError(Discovery, "Failed to add device type PTR record mDNS responder");
     568           0 :         return CHIP_ERROR_NO_MEMORY;
     569             :     }
     570             : 
     571           8 :     ChipLogProgress(Discovery, "CHIP minimal mDNS configured as 'Operational device'; instance name: %s.", instanceName.names[0]);
     572             : 
     573           8 :     AdvertiseRecords(BroadcastAdvertiseType::kStarted);
     574             : 
     575           8 :     ChipLogProgress(Discovery, "mDNS service published: %s.%s", StringOrNullMarker(instanceName.names[1]),
     576             :                     StringOrNullMarker(instanceName.names[2]));
     577             : 
     578           8 :     return CHIP_NO_ERROR;
     579           8 : }
     580             : 
     581          22 : CHIP_ERROR AdvertiserMinMdns::FinalizeServiceUpdate()
     582             : {
     583          22 :     VerifyOrReturnError(mIsInitialized, CHIP_ERROR_INCORRECT_STATE);
     584          22 :     return CHIP_NO_ERROR;
     585             : }
     586             : 
     587          13 : CHIP_ERROR AdvertiserMinMdns::GetCommissionableInstanceName(char * instanceName, size_t maxLength) const
     588             : {
     589          13 :     if (maxLength < (Commission::kInstanceNameMaxLength + 1))
     590             :     {
     591           0 :         return CHIP_ERROR_NO_MEMORY;
     592             :     }
     593             : 
     594          13 :     return chip::Encoding::BytesToUppercaseHexString(&mCommissionableInstanceName[0], sizeof(mCommissionableInstanceName),
     595          13 :                                                      instanceName, maxLength);
     596             : }
     597             : 
     598           8 : CHIP_ERROR AdvertiserMinMdns::UpdateCommissionableInstanceName()
     599             : {
     600           8 :     uint64_t random_instance_name = chip::Crypto::GetRandU64();
     601             :     static_assert(sizeof(mCommissionableInstanceName) == sizeof(random_instance_name), "Not copying the right amount of data");
     602           8 :     memcpy(&mCommissionableInstanceName[0], &random_instance_name, sizeof(mCommissionableInstanceName));
     603           8 :     return CHIP_NO_ERROR;
     604             : }
     605             : 
     606          12 : CHIP_ERROR AdvertiserMinMdns::Advertise(const CommissionAdvertisingParameters & params)
     607             : {
     608          12 :     VerifyOrReturnError(mIsInitialized, CHIP_ERROR_INCORRECT_STATE);
     609             : 
     610          12 :     if (params.GetCommissionAdvertiseMode() == CommssionAdvertiseMode::kCommissionableNode)
     611             :     {
     612          12 :         mQueryResponderAllocatorCommissionable.Clear();
     613             :     }
     614             :     else
     615             :     {
     616           0 :         mQueryResponderAllocatorCommissioner.Clear();
     617             :     }
     618             : 
     619             :     // TODO: need to detect colisions here
     620          12 :     char nameBuffer[64] = "";
     621          12 :     ReturnErrorOnFailure(GetCommissionableInstanceName(nameBuffer, sizeof(nameBuffer)));
     622             : 
     623             :     QueryResponderAllocator<kMaxCommissionRecords> * allocator =
     624          12 :         params.GetCommissionAdvertiseMode() == CommssionAdvertiseMode::kCommissionableNode ? &mQueryResponderAllocatorCommissionable
     625          12 :                                                                                            : &mQueryResponderAllocatorCommissioner;
     626          12 :     const char * serviceType = params.GetCommissionAdvertiseMode() == CommssionAdvertiseMode::kCommissionableNode
     627             :         ? kCommissionableServiceName
     628          12 :         : kCommissionerServiceName;
     629             : 
     630          12 :     FullQName serviceName  = allocator->AllocateQName(serviceType, kCommissionProtocol, kLocalDomain);
     631          12 :     FullQName instanceName = allocator->AllocateQName(nameBuffer, serviceType, kCommissionProtocol, kLocalDomain);
     632             : 
     633          12 :     ReturnErrorOnFailure(MakeHostName(nameBuffer, sizeof(nameBuffer), params.GetMac()));
     634          12 :     FullQName hostName = allocator->AllocateQName(nameBuffer, kLocalDomain);
     635             : 
     636          12 :     if ((serviceName.nameCount == 0) || (instanceName.nameCount == 0) || (hostName.nameCount == 0))
     637             :     {
     638           0 :         ChipLogError(Discovery, "Failed to allocate QNames.");
     639           0 :         return CHIP_ERROR_NO_MEMORY;
     640             :     }
     641             : 
     642          24 :     if (!allocator->AddResponder<PtrResponder>(serviceName, instanceName)
     643          12 :              .SetReportAdditional(instanceName)
     644          12 :              .SetReportInServiceListing(true)
     645          12 :              .IsValid())
     646             :     {
     647           0 :         ChipLogError(Discovery, "Failed to add service PTR record mDNS responder");
     648           0 :         return CHIP_ERROR_NO_MEMORY;
     649             :     }
     650             : 
     651          24 :     if (!allocator->AddResponder<SrvResponder>(SrvResourceRecord(instanceName, hostName, params.GetPort()))
     652          12 :              .SetReportAdditional(hostName)
     653          12 :              .IsValid())
     654             :     {
     655           0 :         ChipLogError(Discovery, "Failed to add SRV record mDNS responder");
     656           0 :         return CHIP_ERROR_NO_MEMORY;
     657             :     }
     658          12 :     if (!allocator->AddResponder<IPv6Responder>(hostName).IsValid())
     659             :     {
     660           0 :         ChipLogError(Discovery, "Failed to add IPv6 mDNS responder");
     661           0 :         return CHIP_ERROR_NO_MEMORY;
     662             :     }
     663             : 
     664          12 :     if (params.IsIPv4Enabled())
     665             :     {
     666          12 :         if (!allocator->AddResponder<IPv4Responder>(hostName).IsValid())
     667             :         {
     668           0 :             ChipLogError(Discovery, "Failed to add IPv4 mDNS responder");
     669           0 :             return CHIP_ERROR_NO_MEMORY;
     670             :         }
     671             :     }
     672             : 
     673          12 :     if (params.GetVendorId().HasValue())
     674             :     {
     675          11 :         MakeServiceSubtype(nameBuffer, sizeof(nameBuffer),
     676          22 :                            DiscoveryFilter(DiscoveryFilterType::kVendorId, params.GetVendorId().Value()));
     677             :         FullQName vendorServiceName =
     678          11 :             allocator->AllocateQName(nameBuffer, kSubtypeServiceNamePart, serviceType, kCommissionProtocol, kLocalDomain);
     679          11 :         ReturnErrorCodeIf(vendorServiceName.nameCount == 0, CHIP_ERROR_NO_MEMORY);
     680             : 
     681          22 :         if (!allocator->AddResponder<PtrResponder>(vendorServiceName, instanceName)
     682          11 :                  .SetReportAdditional(instanceName)
     683          11 :                  .SetReportInServiceListing(true)
     684          11 :                  .IsValid())
     685             :         {
     686           0 :             ChipLogError(Discovery, "Failed to add vendor PTR record mDNS responder");
     687           0 :             return CHIP_ERROR_NO_MEMORY;
     688             :         }
     689             :     }
     690             : 
     691          12 :     if (params.GetDeviceType().HasValue())
     692             :     {
     693           3 :         MakeServiceSubtype(nameBuffer, sizeof(nameBuffer),
     694           6 :                            DiscoveryFilter(DiscoveryFilterType::kDeviceType, params.GetDeviceType().Value()));
     695             :         FullQName vendorServiceName =
     696           3 :             allocator->AllocateQName(nameBuffer, kSubtypeServiceNamePart, serviceType, kCommissionProtocol, kLocalDomain);
     697           3 :         ReturnErrorCodeIf(vendorServiceName.nameCount == 0, CHIP_ERROR_NO_MEMORY);
     698             : 
     699           6 :         if (!allocator->AddResponder<PtrResponder>(vendorServiceName, instanceName)
     700           3 :                  .SetReportAdditional(instanceName)
     701           3 :                  .SetReportInServiceListing(true)
     702           3 :                  .IsValid())
     703             :         {
     704           0 :             ChipLogError(Discovery, "Failed to add device type PTR record mDNS responder");
     705           0 :             return CHIP_ERROR_NO_MEMORY;
     706             :         }
     707             :     }
     708             : 
     709             :     // the following sub types only apply to commissionable node advertisements
     710          12 :     if (params.GetCommissionAdvertiseMode() == CommssionAdvertiseMode::kCommissionableNode)
     711             :     {
     712             :         {
     713          12 :             MakeServiceSubtype(nameBuffer, sizeof(nameBuffer),
     714          12 :                                DiscoveryFilter(DiscoveryFilterType::kShortDiscriminator, params.GetShortDiscriminator()));
     715             :             FullQName shortServiceName =
     716          12 :                 allocator->AllocateQName(nameBuffer, kSubtypeServiceNamePart, serviceType, kCommissionProtocol, kLocalDomain);
     717          12 :             ReturnErrorCodeIf(shortServiceName.nameCount == 0, CHIP_ERROR_NO_MEMORY);
     718             : 
     719          24 :             if (!allocator->AddResponder<PtrResponder>(shortServiceName, instanceName)
     720          12 :                      .SetReportAdditional(instanceName)
     721          12 :                      .SetReportInServiceListing(true)
     722          12 :                      .IsValid())
     723             :             {
     724           0 :                 ChipLogError(Discovery, "Failed to add short discriminator PTR record mDNS responder");
     725           0 :                 return CHIP_ERROR_NO_MEMORY;
     726             :             }
     727             :         }
     728             : 
     729             :         {
     730          12 :             MakeServiceSubtype(nameBuffer, sizeof(nameBuffer),
     731          12 :                                DiscoveryFilter(DiscoveryFilterType::kLongDiscriminator, params.GetLongDiscriminator()));
     732             :             FullQName longServiceName =
     733          12 :                 allocator->AllocateQName(nameBuffer, kSubtypeServiceNamePart, serviceType, kCommissionProtocol, kLocalDomain);
     734          12 :             ReturnErrorCodeIf(longServiceName.nameCount == 0, CHIP_ERROR_NO_MEMORY);
     735          24 :             if (!allocator->AddResponder<PtrResponder>(longServiceName, instanceName)
     736          12 :                      .SetReportAdditional(instanceName)
     737          12 :                      .SetReportInServiceListing(true)
     738          12 :                      .IsValid())
     739             :             {
     740           0 :                 ChipLogError(Discovery, "Failed to add long discriminator PTR record mDNS responder");
     741           0 :                 return CHIP_ERROR_NO_MEMORY;
     742             :             }
     743             :         }
     744             : 
     745          12 :         if (params.GetCommissioningMode() != CommissioningMode::kDisabled)
     746             :         {
     747          11 :             MakeServiceSubtype(nameBuffer, sizeof(nameBuffer), DiscoveryFilter(DiscoveryFilterType::kCommissioningMode));
     748             :             FullQName longServiceName =
     749          11 :                 allocator->AllocateQName(nameBuffer, kSubtypeServiceNamePart, serviceType, kCommissionProtocol, kLocalDomain);
     750          11 :             ReturnErrorCodeIf(longServiceName.nameCount == 0, CHIP_ERROR_NO_MEMORY);
     751          22 :             if (!allocator->AddResponder<PtrResponder>(longServiceName, instanceName)
     752          11 :                      .SetReportAdditional(instanceName)
     753          11 :                      .SetReportInServiceListing(true)
     754          11 :                      .IsValid())
     755             :             {
     756           0 :                 ChipLogError(Discovery, "Failed to add commissioning mode PTR record mDNS responder");
     757           0 :                 return CHIP_ERROR_NO_MEMORY;
     758             :             }
     759             :         }
     760             :     }
     761             : 
     762          24 :     if (!allocator->AddResponder<TxtResponder>(TxtResourceRecord(instanceName, GetCommissioningTxtEntries(params)))
     763          12 :              .SetReportAdditional(hostName)
     764          12 :              .IsValid())
     765             :     {
     766           0 :         ChipLogError(Discovery, "Failed to add TXT record mDNS responder");
     767           0 :         return CHIP_ERROR_NO_MEMORY;
     768             :     }
     769             : 
     770          12 :     if (params.GetCommissionAdvertiseMode() == CommssionAdvertiseMode::kCommissionableNode)
     771             :     {
     772          12 :         ChipLogProgress(Discovery, "CHIP minimal mDNS configured as 'Commissionable node device'; instance name: %s.",
     773             :                         StringOrNullMarker(instanceName.names[0]));
     774             :     }
     775             :     else
     776             :     {
     777           0 :         ChipLogProgress(Discovery, "CHIP minimal mDNS configured as 'Commissioner device'; instance name: %s.",
     778             :                         StringOrNullMarker(instanceName.names[0]));
     779             :     }
     780             : 
     781          12 :     AdvertiseRecords(BroadcastAdvertiseType::kStarted);
     782             : 
     783          12 :     ChipLogProgress(Discovery, "mDNS service published: %s.%s", StringOrNullMarker(instanceName.names[1]),
     784             :                     StringOrNullMarker(instanceName.names[2]));
     785             : 
     786          12 :     return CHIP_NO_ERROR;
     787             : }
     788             : 
     789           8 : FullQName AdvertiserMinMdns::GetOperationalTxtEntries(OperationalQueryAllocator::Allocator * allocator,
     790             :                                                       const OperationalAdvertisingParameters & params)
     791             : {
     792             :     char * txtFields[OperationalAdvertisingParameters::kTxtMaxNumber];
     793           8 :     size_t numTxtFields = 0;
     794             : 
     795             :     struct CommonTxtEntryStorage commonStorage;
     796           8 :     AddCommonTxtEntries<OperationalAdvertisingParameters>(params, commonStorage, txtFields, numTxtFields);
     797           8 :     if (numTxtFields == 0)
     798             :     {
     799           5 :         return allocator->AllocateQNameFromArray(mEmptyTextEntries, 1);
     800             :     }
     801             : 
     802           3 :     return allocator->AllocateQNameFromArray(txtFields, numTxtFields);
     803             : }
     804             : 
     805          12 : FullQName AdvertiserMinMdns::GetCommissioningTxtEntries(const CommissionAdvertisingParameters & params)
     806             : {
     807             :     char * txtFields[CommissionAdvertisingParameters::kTxtMaxNumber];
     808          12 :     size_t numTxtFields = 0;
     809             : 
     810             :     QueryResponderAllocator<kMaxCommissionRecords> * allocator =
     811          12 :         params.GetCommissionAdvertiseMode() == CommssionAdvertiseMode::kCommissionableNode ? &mQueryResponderAllocatorCommissionable
     812          12 :                                                                                            : &mQueryResponderAllocatorCommissioner;
     813             : 
     814             :     char txtVidPid[chip::Dnssd::kKeyVendorProductMaxLength + 4];
     815          12 :     if (params.GetProductId().HasValue() && params.GetVendorId().HasValue())
     816             :     {
     817          11 :         snprintf(txtVidPid, sizeof(txtVidPid), "VP=%d+%d", params.GetVendorId().Value(), params.GetProductId().Value());
     818          11 :         txtFields[numTxtFields++] = txtVidPid;
     819             :     }
     820           1 :     else if (params.GetVendorId().HasValue())
     821             :     {
     822           0 :         snprintf(txtVidPid, sizeof(txtVidPid), "VP=%d", params.GetVendorId().Value());
     823           0 :         txtFields[numTxtFields++] = txtVidPid;
     824             :     }
     825             : 
     826             :     char txtDeviceType[chip::Dnssd::kKeyDeviceTypeMaxLength + 4];
     827          12 :     if (params.GetDeviceType().HasValue())
     828             :     {
     829           3 :         snprintf(txtDeviceType, sizeof(txtDeviceType), "DT=%" PRIu32, params.GetDeviceType().Value());
     830           3 :         txtFields[numTxtFields++] = txtDeviceType;
     831             :     }
     832             : 
     833             :     char txtDeviceName[chip::Dnssd::kKeyDeviceNameMaxLength + 4];
     834          12 :     if (params.GetDeviceName().HasValue())
     835             :     {
     836           3 :         snprintf(txtDeviceName, sizeof(txtDeviceName), "DN=%s", params.GetDeviceName().Value());
     837           3 :         txtFields[numTxtFields++] = txtDeviceName;
     838             :     }
     839             :     CommonTxtEntryStorage commonStorage;
     840          12 :     AddCommonTxtEntries<CommissionAdvertisingParameters>(params, commonStorage, txtFields, numTxtFields);
     841             : 
     842             :     // the following sub types only apply to commissionable node advertisements
     843             :     char txtDiscriminator[chip::Dnssd::kKeyLongDiscriminatorMaxLength + 3];
     844             :     char txtCommissioningMode[chip::Dnssd::kKeyCommissioningModeMaxLength + 4];
     845             :     char txtRotatingDeviceId[chip::Dnssd::kKeyRotatingDeviceIdMaxLength + 4];
     846             :     char txtPairingHint[chip::Dnssd::kKeyPairingInstructionMaxLength + 4];
     847             :     char txtPairingInstr[chip::Dnssd::kKeyPairingInstructionMaxLength + 4];
     848             : 
     849             :     // the following sub types only apply to commissioner discovery advertisements
     850             :     char txtCommissionerPasscode[chip::Dnssd::kKeyCommissionerPasscodeMaxLength + 4];
     851             : 
     852          12 :     if (params.GetCommissionAdvertiseMode() == CommssionAdvertiseMode::kCommissionableNode)
     853             :     {
     854             :         // a discriminator always exists
     855          12 :         snprintf(txtDiscriminator, sizeof(txtDiscriminator), "D=%d", params.GetLongDiscriminator());
     856          12 :         txtFields[numTxtFields++] = txtDiscriminator;
     857             : 
     858          12 :         snprintf(txtCommissioningMode, sizeof(txtCommissioningMode), "CM=%d", static_cast<int>(params.GetCommissioningMode()));
     859          12 :         txtFields[numTxtFields++] = txtCommissioningMode;
     860             : 
     861          12 :         if (params.GetRotatingDeviceId().HasValue())
     862             :         {
     863           3 :             snprintf(txtRotatingDeviceId, sizeof(txtRotatingDeviceId), "RI=%s", params.GetRotatingDeviceId().Value());
     864           3 :             txtFields[numTxtFields++] = txtRotatingDeviceId;
     865             :         }
     866             : 
     867          12 :         if (params.GetPairingHint().HasValue())
     868             :         {
     869          11 :             snprintf(txtPairingHint, sizeof(txtPairingHint), "PH=%d", params.GetPairingHint().Value());
     870          11 :             txtFields[numTxtFields++] = txtPairingHint;
     871             :         }
     872             : 
     873          12 :         if (params.GetPairingInstruction().HasValue())
     874             :         {
     875          11 :             snprintf(txtPairingInstr, sizeof(txtPairingInstr), "PI=%s", params.GetPairingInstruction().Value());
     876          11 :             txtFields[numTxtFields++] = txtPairingInstr;
     877             :         }
     878             :     }
     879             :     else
     880             :     {
     881           0 :         if (params.GetCommissionerPasscodeSupported().ValueOr(false))
     882             :         {
     883           0 :             snprintf(txtCommissionerPasscode, sizeof(txtCommissionerPasscode), "CP=%d", static_cast<int>(1));
     884           0 :             txtFields[numTxtFields++] = txtCommissionerPasscode;
     885             :         }
     886             :     }
     887          12 :     if (numTxtFields == 0)
     888             :     {
     889           0 :         return allocator->AllocateQNameFromArray(mEmptyTextEntries, 1);
     890             :     }
     891             : 
     892          12 :     return allocator->AllocateQNameFromArray(txtFields, numTxtFields);
     893             : }
     894             : 
     895          57 : void AdvertiserMinMdns::AdvertiseRecords(BroadcastAdvertiseType type)
     896             : {
     897          57 :     ResponseConfiguration responseConfiguration;
     898          57 :     if (type == BroadcastAdvertiseType::kRemovingAll)
     899             :     {
     900             :         // make a "remove all records now" broadcast
     901          21 :         responseConfiguration.SetTtlSecondsOverride(0);
     902             :     }
     903             : 
     904          57 :     UniquePtr<ListenIterator> allInterfaces = GetAddressPolicy()->GetListenEndpoints();
     905          57 :     VerifyOrDieWithMsg(allInterfaces != nullptr, Discovery, "Failed to allocate memory for endpoints.");
     906             : 
     907          57 :     chip::Inet::InterfaceId interfaceId;
     908             :     chip::Inet::IPAddressType addressType;
     909             : 
     910         171 :     while (allInterfaces->Next(&interfaceId, &addressType))
     911             :     {
     912         114 :         UniquePtr<IpAddressIterator> allIps = GetAddressPolicy()->GetIpAddressesForEndpoint(interfaceId, addressType);
     913         114 :         VerifyOrDieWithMsg(allIps != nullptr, Discovery, "Failed to allocate memory for ip addresses.");
     914             : 
     915         114 :         chip::Inet::IPPacketInfo packetInfo;
     916             : 
     917         114 :         packetInfo.Clear();
     918             : 
     919             :         // advertising on every interface requires a valid IP address
     920             :         // since we use "BROADCAST" (unicast is false), we do not actually care about
     921             :         // the source IP address value, just that it has the right "type"
     922             :         //
     923             :         // NOTE: cannot use Broadcast address as the source as they have the type kAny.
     924             :         //
     925             :         // TODO: ideally we may want to have a destination that is explicit as "unicast/destIp"
     926             :         //       vs "multicast/addressType". Such a change requires larger code updates.
     927         114 :         packetInfo.SrcAddress  = chip::Inet::IPAddress::Loopback(addressType);
     928         114 :         packetInfo.DestAddress = BroadcastIpAddresses::Get(addressType);
     929         114 :         packetInfo.SrcPort     = kMdnsPort;
     930         114 :         packetInfo.DestPort    = kMdnsPort;
     931         114 :         packetInfo.Interface   = interfaceId;
     932             : 
     933             :         // Advertise all records
     934             :         //
     935             :         // TODO: Consider advertising delta changes.
     936             :         //
     937             :         // Current advertisement does not have a concept of "delta" to only
     938             :         // advertise changes. Current implementation is to always
     939             :         //    1. advertise TTL=0 (clear all caches)
     940             :         //    2. advertise available records (with longer TTL)
     941             :         //
     942             :         // It would be nice if we could selectively advertise what changes, like
     943             :         // send TTL=0 for anything removed/about to be removed (and only those),
     944             :         // then only advertise new items added.
     945             :         //
     946             :         // This optimization likely will take more logic and state storage, so
     947             :         // for now it is not done.
     948         114 :         QueryData queryData(QType::PTR, QClass::IN, false /* unicast */);
     949         114 :         queryData.SetIsAnnounceBroadcast(true);
     950             : 
     951         170 :         for (auto & it : mOperationalResponders)
     952             :         {
     953          56 :             it.GetAllocator()->GetQueryResponder()->ClearBroadcastThrottle();
     954             :         }
     955         114 :         mQueryResponderAllocatorCommissionable.GetQueryResponder()->ClearBroadcastThrottle();
     956         114 :         mQueryResponderAllocatorCommissioner.GetQueryResponder()->ClearBroadcastThrottle();
     957             : 
     958         114 :         CHIP_ERROR err = mResponseSender.Respond(0, queryData, &packetInfo, responseConfiguration);
     959             : 
     960         114 :         if (err != CHIP_NO_ERROR)
     961             :         {
     962          30 :             ChipLogError(Discovery, "Failed to advertise records: %" CHIP_ERROR_FORMAT, err.Format());
     963             :         }
     964         114 :     }
     965             : 
     966             :     // Once all automatic broadcasts are done, allow immediate replies once.
     967          85 :     for (auto & it : mOperationalResponders)
     968             :     {
     969          28 :         it.GetAllocator()->GetQueryResponder()->ClearBroadcastThrottle();
     970             :     }
     971          57 :     mQueryResponderAllocatorCommissionable.GetQueryResponder()->ClearBroadcastThrottle();
     972          57 :     mQueryResponderAllocatorCommissioner.GetQueryResponder()->ClearBroadcastThrottle();
     973          57 : }
     974             : 
     975             : AdvertiserMinMdns gAdvertiser;
     976             : } // namespace
     977             : 
     978          65 : ServiceAdvertiser & ServiceAdvertiser::Instance()
     979             : {
     980          65 :     return gAdvertiser;
     981             : }
     982             : 
     983             : } // namespace Dnssd
     984             : } // namespace chip

Generated by: LCOV version 1.14