Matter SDK Coverage Report
Current view: top level - lib/dnssd - Advertiser_ImplMinimalMdns.cpp (source / functions) Coverage Total Hit
Test: SHA:e98a48c2e59f85a25417956e1d105721433aa5d1 Lines: 82.8 % 378 313
Test Date: 2026-01-09 16:53:50 Functions: 90.0 % 30 27

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

Generated by: LCOV version 2.0-1