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
|