xref: /aosp_15_r20/external/openscreen/discovery/mdns/mdns_publisher.cc (revision 3f982cf4871df8771c9d4abe6e9a6f8d829b2736)
1*3f982cf4SFabien Sanglard // Copyright 2019 The Chromium Authors. All rights reserved.
2*3f982cf4SFabien Sanglard // Use of this source code is governed by a BSD-style license that can be
3*3f982cf4SFabien Sanglard // found in the LICENSE file.
4*3f982cf4SFabien Sanglard 
5*3f982cf4SFabien Sanglard #include "discovery/mdns/mdns_publisher.h"
6*3f982cf4SFabien Sanglard 
7*3f982cf4SFabien Sanglard #include <chrono>
8*3f982cf4SFabien Sanglard #include <cmath>
9*3f982cf4SFabien Sanglard 
10*3f982cf4SFabien Sanglard #include "discovery/common/config.h"
11*3f982cf4SFabien Sanglard #include "discovery/mdns/mdns_probe_manager.h"
12*3f982cf4SFabien Sanglard #include "discovery/mdns/mdns_records.h"
13*3f982cf4SFabien Sanglard #include "discovery/mdns/mdns_sender.h"
14*3f982cf4SFabien Sanglard #include "platform/api/task_runner.h"
15*3f982cf4SFabien Sanglard #include "platform/base/trivial_clock_traits.h"
16*3f982cf4SFabien Sanglard 
17*3f982cf4SFabien Sanglard namespace openscreen {
18*3f982cf4SFabien Sanglard namespace discovery {
19*3f982cf4SFabien Sanglard namespace {
20*3f982cf4SFabien Sanglard 
21*3f982cf4SFabien Sanglard // Minimum delay between announcements of a given record in seconds.
22*3f982cf4SFabien Sanglard constexpr std::chrono::seconds kMinAnnounceDelay{1};
23*3f982cf4SFabien Sanglard 
24*3f982cf4SFabien Sanglard // Intervals between successive announcements must increase by at least a
25*3f982cf4SFabien Sanglard // factor of 2.
26*3f982cf4SFabien Sanglard constexpr int kIntervalIncreaseFactor = 2;
27*3f982cf4SFabien Sanglard 
28*3f982cf4SFabien Sanglard // TTL for a goodbye record in seconds. This constant is called out in RFC 6762
29*3f982cf4SFabien Sanglard // section 10.1.
30*3f982cf4SFabien Sanglard constexpr std::chrono::seconds kGoodbyeTtl{0};
31*3f982cf4SFabien Sanglard 
32*3f982cf4SFabien Sanglard // Timespan between sending batches of announcement and goodbye records, in
33*3f982cf4SFabien Sanglard // microseconds.
34*3f982cf4SFabien Sanglard constexpr Clock::duration kDelayBetweenBatchedRecords =
35*3f982cf4SFabien Sanglard     std::chrono::milliseconds(20);
36*3f982cf4SFabien Sanglard 
CreateGoodbyeRecord(const MdnsRecord & record)37*3f982cf4SFabien Sanglard inline MdnsRecord CreateGoodbyeRecord(const MdnsRecord& record) {
38*3f982cf4SFabien Sanglard   if (record.ttl() == kGoodbyeTtl) {
39*3f982cf4SFabien Sanglard     return record;
40*3f982cf4SFabien Sanglard   }
41*3f982cf4SFabien Sanglard   return MdnsRecord(record.name(), record.dns_type(), record.dns_class(),
42*3f982cf4SFabien Sanglard                     record.record_type(), kGoodbyeTtl, record.rdata());
43*3f982cf4SFabien Sanglard }
44*3f982cf4SFabien Sanglard 
45*3f982cf4SFabien Sanglard }  // namespace
46*3f982cf4SFabien Sanglard 
MdnsPublisher(MdnsSender * sender,MdnsProbeManager * ownership_manager,TaskRunner * task_runner,ClockNowFunctionPtr now_function,const Config & config)47*3f982cf4SFabien Sanglard MdnsPublisher::MdnsPublisher(MdnsSender* sender,
48*3f982cf4SFabien Sanglard                              MdnsProbeManager* ownership_manager,
49*3f982cf4SFabien Sanglard                              TaskRunner* task_runner,
50*3f982cf4SFabien Sanglard                              ClockNowFunctionPtr now_function,
51*3f982cf4SFabien Sanglard                              const Config& config)
52*3f982cf4SFabien Sanglard     : sender_(sender),
53*3f982cf4SFabien Sanglard       ownership_manager_(ownership_manager),
54*3f982cf4SFabien Sanglard       task_runner_(task_runner),
55*3f982cf4SFabien Sanglard       now_function_(now_function),
56*3f982cf4SFabien Sanglard       max_announcement_attempts_(config.new_record_announcement_count) {
57*3f982cf4SFabien Sanglard   OSP_DCHECK(ownership_manager_);
58*3f982cf4SFabien Sanglard   OSP_DCHECK(sender_);
59*3f982cf4SFabien Sanglard   OSP_DCHECK(task_runner_);
60*3f982cf4SFabien Sanglard   OSP_DCHECK_GE(max_announcement_attempts_, 0);
61*3f982cf4SFabien Sanglard }
62*3f982cf4SFabien Sanglard 
~MdnsPublisher()63*3f982cf4SFabien Sanglard MdnsPublisher::~MdnsPublisher() {
64*3f982cf4SFabien Sanglard   if (batch_records_alarm_.has_value()) {
65*3f982cf4SFabien Sanglard     batch_records_alarm_.value().Cancel();
66*3f982cf4SFabien Sanglard     ProcessRecordQueue();
67*3f982cf4SFabien Sanglard   }
68*3f982cf4SFabien Sanglard }
69*3f982cf4SFabien Sanglard 
RegisterRecord(const MdnsRecord & record)70*3f982cf4SFabien Sanglard Error MdnsPublisher::RegisterRecord(const MdnsRecord& record) {
71*3f982cf4SFabien Sanglard   OSP_DCHECK(task_runner_->IsRunningOnTaskRunner());
72*3f982cf4SFabien Sanglard   OSP_DCHECK(record.dns_class() != DnsClass::kANY);
73*3f982cf4SFabien Sanglard 
74*3f982cf4SFabien Sanglard   if (!CanBePublished(record.dns_type())) {
75*3f982cf4SFabien Sanglard     return Error::Code::kParameterInvalid;
76*3f982cf4SFabien Sanglard   }
77*3f982cf4SFabien Sanglard 
78*3f982cf4SFabien Sanglard   if (!IsRecordNameClaimed(record)) {
79*3f982cf4SFabien Sanglard     return Error::Code::kParameterInvalid;
80*3f982cf4SFabien Sanglard   }
81*3f982cf4SFabien Sanglard 
82*3f982cf4SFabien Sanglard   const DomainName& name = record.name();
83*3f982cf4SFabien Sanglard   auto it = records_.emplace(name, std::vector<RecordAnnouncerPtr>{}).first;
84*3f982cf4SFabien Sanglard   for (const RecordAnnouncerPtr& publisher : it->second) {
85*3f982cf4SFabien Sanglard     if (publisher->record() == record) {
86*3f982cf4SFabien Sanglard       return Error::Code::kItemAlreadyExists;
87*3f982cf4SFabien Sanglard     }
88*3f982cf4SFabien Sanglard   }
89*3f982cf4SFabien Sanglard 
90*3f982cf4SFabien Sanglard   OSP_DVLOG << "Registering record of type '" << record.dns_type() << "'";
91*3f982cf4SFabien Sanglard 
92*3f982cf4SFabien Sanglard   it->second.push_back(CreateAnnouncer(record));
93*3f982cf4SFabien Sanglard 
94*3f982cf4SFabien Sanglard   return Error::None();
95*3f982cf4SFabien Sanglard }
96*3f982cf4SFabien Sanglard 
UnregisterRecord(const MdnsRecord & record)97*3f982cf4SFabien Sanglard Error MdnsPublisher::UnregisterRecord(const MdnsRecord& record) {
98*3f982cf4SFabien Sanglard   OSP_DCHECK(task_runner_->IsRunningOnTaskRunner());
99*3f982cf4SFabien Sanglard   OSP_DCHECK(record.dns_class() != DnsClass::kANY);
100*3f982cf4SFabien Sanglard 
101*3f982cf4SFabien Sanglard   if (!CanBePublished(record.dns_type())) {
102*3f982cf4SFabien Sanglard     return Error::Code::kParameterInvalid;
103*3f982cf4SFabien Sanglard   }
104*3f982cf4SFabien Sanglard 
105*3f982cf4SFabien Sanglard   OSP_DVLOG << "Unregistering record of type '" << record.dns_type() << "'";
106*3f982cf4SFabien Sanglard 
107*3f982cf4SFabien Sanglard   return RemoveRecord(record, true);
108*3f982cf4SFabien Sanglard }
109*3f982cf4SFabien Sanglard 
UpdateRegisteredRecord(const MdnsRecord & old_record,const MdnsRecord & new_record)110*3f982cf4SFabien Sanglard Error MdnsPublisher::UpdateRegisteredRecord(const MdnsRecord& old_record,
111*3f982cf4SFabien Sanglard                                             const MdnsRecord& new_record) {
112*3f982cf4SFabien Sanglard   OSP_DCHECK(task_runner_->IsRunningOnTaskRunner());
113*3f982cf4SFabien Sanglard 
114*3f982cf4SFabien Sanglard   if (!CanBePublished(new_record.dns_type())) {
115*3f982cf4SFabien Sanglard     return Error::Code::kParameterInvalid;
116*3f982cf4SFabien Sanglard   }
117*3f982cf4SFabien Sanglard 
118*3f982cf4SFabien Sanglard   if (old_record.dns_type() == DnsType::kPTR) {
119*3f982cf4SFabien Sanglard     return Error::Code::kParameterInvalid;
120*3f982cf4SFabien Sanglard   }
121*3f982cf4SFabien Sanglard 
122*3f982cf4SFabien Sanglard   // Check that the old record and new record are compatible.
123*3f982cf4SFabien Sanglard   if (old_record.name() != new_record.name() ||
124*3f982cf4SFabien Sanglard       old_record.dns_type() != new_record.dns_type() ||
125*3f982cf4SFabien Sanglard       old_record.dns_class() != new_record.dns_class() ||
126*3f982cf4SFabien Sanglard       old_record.record_type() != new_record.record_type()) {
127*3f982cf4SFabien Sanglard     return Error::Code::kParameterInvalid;
128*3f982cf4SFabien Sanglard   }
129*3f982cf4SFabien Sanglard 
130*3f982cf4SFabien Sanglard   OSP_DVLOG << "Updating record of type '" << new_record.dns_type() << "'";
131*3f982cf4SFabien Sanglard 
132*3f982cf4SFabien Sanglard   // Remove the old record. Per RFC 6762 section 8.4, a goodbye message will not
133*3f982cf4SFabien Sanglard   // be sent, as all records which can be removed here are unique records, which
134*3f982cf4SFabien Sanglard   // will be overwritten during the announcement phase when the updated record
135*3f982cf4SFabien Sanglard   // is re-registered due to the cache-flush-bit's presence.
136*3f982cf4SFabien Sanglard   const Error remove_result = RemoveRecord(old_record, false);
137*3f982cf4SFabien Sanglard   if (!remove_result.ok()) {
138*3f982cf4SFabien Sanglard     return remove_result;
139*3f982cf4SFabien Sanglard   }
140*3f982cf4SFabien Sanglard 
141*3f982cf4SFabien Sanglard   // Register the new record.
142*3f982cf4SFabien Sanglard   return RegisterRecord(new_record);
143*3f982cf4SFabien Sanglard }
144*3f982cf4SFabien Sanglard 
GetRecordCount() const145*3f982cf4SFabien Sanglard size_t MdnsPublisher::GetRecordCount() const {
146*3f982cf4SFabien Sanglard   OSP_DCHECK(task_runner_->IsRunningOnTaskRunner());
147*3f982cf4SFabien Sanglard 
148*3f982cf4SFabien Sanglard   size_t count = 0;
149*3f982cf4SFabien Sanglard   for (const auto& pair : records_) {
150*3f982cf4SFabien Sanglard     count += pair.second.size();
151*3f982cf4SFabien Sanglard   }
152*3f982cf4SFabien Sanglard 
153*3f982cf4SFabien Sanglard   return count;
154*3f982cf4SFabien Sanglard }
155*3f982cf4SFabien Sanglard 
HasRecords(const DomainName & name,DnsType type,DnsClass clazz)156*3f982cf4SFabien Sanglard bool MdnsPublisher::HasRecords(const DomainName& name,
157*3f982cf4SFabien Sanglard                                DnsType type,
158*3f982cf4SFabien Sanglard                                DnsClass clazz) {
159*3f982cf4SFabien Sanglard   return !GetRecords(name, type, clazz).empty();
160*3f982cf4SFabien Sanglard }
161*3f982cf4SFabien Sanglard 
GetRecords(const DomainName & name,DnsType type,DnsClass clazz)162*3f982cf4SFabien Sanglard std::vector<MdnsRecord::ConstRef> MdnsPublisher::GetRecords(
163*3f982cf4SFabien Sanglard     const DomainName& name,
164*3f982cf4SFabien Sanglard     DnsType type,
165*3f982cf4SFabien Sanglard     DnsClass clazz) {
166*3f982cf4SFabien Sanglard   OSP_DCHECK(task_runner_->IsRunningOnTaskRunner());
167*3f982cf4SFabien Sanglard 
168*3f982cf4SFabien Sanglard   std::vector<MdnsRecord::ConstRef> records;
169*3f982cf4SFabien Sanglard   auto it = records_.find(name);
170*3f982cf4SFabien Sanglard   if (it != records_.end()) {
171*3f982cf4SFabien Sanglard     for (const RecordAnnouncerPtr& announcer : it->second) {
172*3f982cf4SFabien Sanglard       OSP_DCHECK(announcer.get());
173*3f982cf4SFabien Sanglard       const DnsType record_dns_type = announcer->record().dns_type();
174*3f982cf4SFabien Sanglard       const DnsClass record_dns_class = announcer->record().dns_class();
175*3f982cf4SFabien Sanglard       if ((type == DnsType::kANY || type == record_dns_type) &&
176*3f982cf4SFabien Sanglard           (clazz == DnsClass::kANY || clazz == record_dns_class)) {
177*3f982cf4SFabien Sanglard         records.push_back(announcer->record());
178*3f982cf4SFabien Sanglard       }
179*3f982cf4SFabien Sanglard     }
180*3f982cf4SFabien Sanglard   }
181*3f982cf4SFabien Sanglard 
182*3f982cf4SFabien Sanglard   return records;
183*3f982cf4SFabien Sanglard }
184*3f982cf4SFabien Sanglard 
GetPtrRecords(DnsClass clazz)185*3f982cf4SFabien Sanglard std::vector<MdnsRecord::ConstRef> MdnsPublisher::GetPtrRecords(DnsClass clazz) {
186*3f982cf4SFabien Sanglard   std::vector<MdnsRecord::ConstRef> records;
187*3f982cf4SFabien Sanglard 
188*3f982cf4SFabien Sanglard   // There should be few records associated with any given domain name, so it is
189*3f982cf4SFabien Sanglard   // simpler and less error prone to iterate across all records than to check
190*3f982cf4SFabien Sanglard   // the domain name against format '[^.]+\.(_tcp)|(_udp)\..*''
191*3f982cf4SFabien Sanglard   for (auto it = records_.begin(); it != records_.end(); it++) {
192*3f982cf4SFabien Sanglard     for (const RecordAnnouncerPtr& announcer : it->second) {
193*3f982cf4SFabien Sanglard       OSP_DCHECK(announcer.get());
194*3f982cf4SFabien Sanglard       const DnsType record_dns_type = announcer->record().dns_type();
195*3f982cf4SFabien Sanglard       if (record_dns_type != DnsType::kPTR) {
196*3f982cf4SFabien Sanglard         continue;
197*3f982cf4SFabien Sanglard       }
198*3f982cf4SFabien Sanglard 
199*3f982cf4SFabien Sanglard       const DnsClass record_dns_class = announcer->record().dns_class();
200*3f982cf4SFabien Sanglard       if ((clazz == DnsClass::kANY || clazz == record_dns_class)) {
201*3f982cf4SFabien Sanglard         records.push_back(announcer->record());
202*3f982cf4SFabien Sanglard       }
203*3f982cf4SFabien Sanglard     }
204*3f982cf4SFabien Sanglard   }
205*3f982cf4SFabien Sanglard 
206*3f982cf4SFabien Sanglard   return records;
207*3f982cf4SFabien Sanglard }
208*3f982cf4SFabien Sanglard 
RemoveRecord(const MdnsRecord & record,bool should_announce_deletion)209*3f982cf4SFabien Sanglard Error MdnsPublisher::RemoveRecord(const MdnsRecord& record,
210*3f982cf4SFabien Sanglard                                   bool should_announce_deletion) {
211*3f982cf4SFabien Sanglard   const DomainName& name = record.name();
212*3f982cf4SFabien Sanglard 
213*3f982cf4SFabien Sanglard   // Check for the domain and fail if it's not found.
214*3f982cf4SFabien Sanglard   const auto it = records_.find(name);
215*3f982cf4SFabien Sanglard   if (it == records_.end()) {
216*3f982cf4SFabien Sanglard     return Error::Code::kItemNotFound;
217*3f982cf4SFabien Sanglard   }
218*3f982cf4SFabien Sanglard 
219*3f982cf4SFabien Sanglard   // Check for the record to be removed.
220*3f982cf4SFabien Sanglard   const auto records_it =
221*3f982cf4SFabien Sanglard       std::find_if(it->second.begin(), it->second.end(),
222*3f982cf4SFabien Sanglard                    [&record](const RecordAnnouncerPtr& publisher) {
223*3f982cf4SFabien Sanglard                      return publisher->record() == record;
224*3f982cf4SFabien Sanglard                    });
225*3f982cf4SFabien Sanglard   if (records_it == it->second.end()) {
226*3f982cf4SFabien Sanglard     return Error::Code::kItemNotFound;
227*3f982cf4SFabien Sanglard   }
228*3f982cf4SFabien Sanglard   if (!should_announce_deletion) {
229*3f982cf4SFabien Sanglard     (*records_it)->DisableGoodbyeMessageTransmission();
230*3f982cf4SFabien Sanglard   }
231*3f982cf4SFabien Sanglard 
232*3f982cf4SFabien Sanglard   it->second.erase(records_it);
233*3f982cf4SFabien Sanglard   if (it->second.empty()) {
234*3f982cf4SFabien Sanglard     records_.erase(it);
235*3f982cf4SFabien Sanglard   }
236*3f982cf4SFabien Sanglard 
237*3f982cf4SFabien Sanglard   return Error::None();
238*3f982cf4SFabien Sanglard }
239*3f982cf4SFabien Sanglard 
IsRecordNameClaimed(const MdnsRecord & record) const240*3f982cf4SFabien Sanglard bool MdnsPublisher::IsRecordNameClaimed(const MdnsRecord& record) const {
241*3f982cf4SFabien Sanglard   const DomainName& name =
242*3f982cf4SFabien Sanglard       record.dns_type() == DnsType::kPTR
243*3f982cf4SFabien Sanglard           ? absl::get<PtrRecordRdata>(record.rdata()).ptr_domain()
244*3f982cf4SFabien Sanglard           : record.name();
245*3f982cf4SFabien Sanglard   return ownership_manager_->IsDomainClaimed(name);
246*3f982cf4SFabien Sanglard }
247*3f982cf4SFabien Sanglard 
RecordAnnouncer(MdnsRecord record,MdnsPublisher * publisher,TaskRunner * task_runner,ClockNowFunctionPtr now_function,int target_announcement_attempts)248*3f982cf4SFabien Sanglard MdnsPublisher::RecordAnnouncer::RecordAnnouncer(
249*3f982cf4SFabien Sanglard     MdnsRecord record,
250*3f982cf4SFabien Sanglard     MdnsPublisher* publisher,
251*3f982cf4SFabien Sanglard     TaskRunner* task_runner,
252*3f982cf4SFabien Sanglard     ClockNowFunctionPtr now_function,
253*3f982cf4SFabien Sanglard     int target_announcement_attempts)
254*3f982cf4SFabien Sanglard     : publisher_(publisher),
255*3f982cf4SFabien Sanglard       task_runner_(task_runner),
256*3f982cf4SFabien Sanglard       now_function_(now_function),
257*3f982cf4SFabien Sanglard       record_(std::move(record)),
258*3f982cf4SFabien Sanglard       alarm_(now_function_, task_runner_),
259*3f982cf4SFabien Sanglard       target_announcement_attempts_(target_announcement_attempts) {
260*3f982cf4SFabien Sanglard   OSP_DCHECK(publisher_);
261*3f982cf4SFabien Sanglard   OSP_DCHECK(task_runner_);
262*3f982cf4SFabien Sanglard   OSP_DCHECK(record_.ttl() != Clock::duration::zero());
263*3f982cf4SFabien Sanglard 
264*3f982cf4SFabien Sanglard   QueueAnnouncement();
265*3f982cf4SFabien Sanglard }
266*3f982cf4SFabien Sanglard 
~RecordAnnouncer()267*3f982cf4SFabien Sanglard MdnsPublisher::RecordAnnouncer::~RecordAnnouncer() {
268*3f982cf4SFabien Sanglard   alarm_.Cancel();
269*3f982cf4SFabien Sanglard   if (should_send_goodbye_message_) {
270*3f982cf4SFabien Sanglard     QueueGoodbye();
271*3f982cf4SFabien Sanglard   }
272*3f982cf4SFabien Sanglard }
273*3f982cf4SFabien Sanglard 
QueueGoodbye()274*3f982cf4SFabien Sanglard void MdnsPublisher::RecordAnnouncer::QueueGoodbye() {
275*3f982cf4SFabien Sanglard   OSP_DCHECK(task_runner_->IsRunningOnTaskRunner());
276*3f982cf4SFabien Sanglard 
277*3f982cf4SFabien Sanglard   publisher_->QueueRecord(CreateGoodbyeRecord(record_));
278*3f982cf4SFabien Sanglard }
279*3f982cf4SFabien Sanglard 
QueueAnnouncement()280*3f982cf4SFabien Sanglard void MdnsPublisher::RecordAnnouncer::QueueAnnouncement() {
281*3f982cf4SFabien Sanglard   OSP_DCHECK(task_runner_->IsRunningOnTaskRunner());
282*3f982cf4SFabien Sanglard 
283*3f982cf4SFabien Sanglard   if (attempts_ >= target_announcement_attempts_) {
284*3f982cf4SFabien Sanglard     return;
285*3f982cf4SFabien Sanglard   }
286*3f982cf4SFabien Sanglard 
287*3f982cf4SFabien Sanglard   publisher_->QueueRecord(record_);
288*3f982cf4SFabien Sanglard 
289*3f982cf4SFabien Sanglard   const Clock::duration new_delay = GetNextAnnounceDelay();
290*3f982cf4SFabien Sanglard   attempts_++;
291*3f982cf4SFabien Sanglard   alarm_.ScheduleFromNow([this]() { QueueAnnouncement(); }, new_delay);
292*3f982cf4SFabien Sanglard }
293*3f982cf4SFabien Sanglard 
QueueRecord(MdnsRecord record)294*3f982cf4SFabien Sanglard void MdnsPublisher::QueueRecord(MdnsRecord record) {
295*3f982cf4SFabien Sanglard   if (!batch_records_alarm_.has_value()) {
296*3f982cf4SFabien Sanglard     OSP_DCHECK(records_to_send_.empty());
297*3f982cf4SFabien Sanglard     batch_records_alarm_.emplace(now_function_, task_runner_);
298*3f982cf4SFabien Sanglard     batch_records_alarm_.value().ScheduleFromNow(
299*3f982cf4SFabien Sanglard         [this]() { ProcessRecordQueue(); }, kDelayBetweenBatchedRecords);
300*3f982cf4SFabien Sanglard   }
301*3f982cf4SFabien Sanglard 
302*3f982cf4SFabien Sanglard   // Check that we aren't announcing and goodbye'ing a record in the same batch.
303*3f982cf4SFabien Sanglard   // We expect to be sending no more than 5 records at a time, so don't worry
304*3f982cf4SFabien Sanglard   // about iterating across this vector for each insert.
305*3f982cf4SFabien Sanglard   auto goodbye = CreateGoodbyeRecord(record);
306*3f982cf4SFabien Sanglard   auto existing_record_it =
307*3f982cf4SFabien Sanglard       std::find_if(records_to_send_.begin(), records_to_send_.end(),
308*3f982cf4SFabien Sanglard                    [&goodbye](const MdnsRecord& record) {
309*3f982cf4SFabien Sanglard                      return goodbye == CreateGoodbyeRecord(record);
310*3f982cf4SFabien Sanglard                    });
311*3f982cf4SFabien Sanglard 
312*3f982cf4SFabien Sanglard   // If we didn't find it, simply add it to the queue. Else, only send the
313*3f982cf4SFabien Sanglard   // goodbye record.
314*3f982cf4SFabien Sanglard   if (existing_record_it == records_to_send_.end()) {
315*3f982cf4SFabien Sanglard     records_to_send_.push_back(std::move(record));
316*3f982cf4SFabien Sanglard   } else if (*existing_record_it == goodbye) {
317*3f982cf4SFabien Sanglard     // This means that the goodbye record is already queued to be sent. This
318*3f982cf4SFabien Sanglard     // means that there is no reason to also announce it, so exit early.
319*3f982cf4SFabien Sanglard     return;
320*3f982cf4SFabien Sanglard   } else if (record == goodbye) {
321*3f982cf4SFabien Sanglard     // This means that we are sending a goodbye record right as it would also
322*3f982cf4SFabien Sanglard     // be announced. Skip the announcement since the record is being
323*3f982cf4SFabien Sanglard     // unregistered.
324*3f982cf4SFabien Sanglard     *existing_record_it = std::move(record);
325*3f982cf4SFabien Sanglard   } else if (record == *existing_record_it) {
326*3f982cf4SFabien Sanglard     // This case shouldn't happen, but there is no work to do if it does. Log
327*3f982cf4SFabien Sanglard     // to surface that something weird is going on.
328*3f982cf4SFabien Sanglard     OSP_LOG_INFO << "Same record being announced multiple times.";
329*3f982cf4SFabien Sanglard   } else {
330*3f982cf4SFabien Sanglard     // This case should never occur. Support it just in case, but log to
331*3f982cf4SFabien Sanglard     // surface that something weird is happening.
332*3f982cf4SFabien Sanglard     OSP_LOG_INFO << "Updating the same record multiple times with multiple "
333*3f982cf4SFabien Sanglard                     "TTL values.";
334*3f982cf4SFabien Sanglard   }
335*3f982cf4SFabien Sanglard }
336*3f982cf4SFabien Sanglard 
ProcessRecordQueue()337*3f982cf4SFabien Sanglard void MdnsPublisher::ProcessRecordQueue() {
338*3f982cf4SFabien Sanglard   OSP_DCHECK(task_runner_->IsRunningOnTaskRunner());
339*3f982cf4SFabien Sanglard 
340*3f982cf4SFabien Sanglard   if (records_to_send_.empty()) {
341*3f982cf4SFabien Sanglard     return;
342*3f982cf4SFabien Sanglard   }
343*3f982cf4SFabien Sanglard 
344*3f982cf4SFabien Sanglard   MdnsMessage message(CreateMessageId(), MessageType::Response);
345*3f982cf4SFabien Sanglard   for (auto it = records_to_send_.begin(); it != records_to_send_.end();) {
346*3f982cf4SFabien Sanglard     if (message.CanAddRecord(*it)) {
347*3f982cf4SFabien Sanglard       message.AddAnswer(std::move(*it++));
348*3f982cf4SFabien Sanglard     } else if (message.answers().empty()) {
349*3f982cf4SFabien Sanglard       // This case should never happen, because it means a record is too large
350*3f982cf4SFabien Sanglard       // to fit into its own message.
351*3f982cf4SFabien Sanglard       OSP_LOG_INFO
352*3f982cf4SFabien Sanglard           << "Encountered unreasonably large message in cache. Skipping "
353*3f982cf4SFabien Sanglard           << "known answer in suppressions...";
354*3f982cf4SFabien Sanglard       it++;
355*3f982cf4SFabien Sanglard     } else {
356*3f982cf4SFabien Sanglard       sender_->SendMulticast(message);
357*3f982cf4SFabien Sanglard       message = MdnsMessage(CreateMessageId(), MessageType::Response);
358*3f982cf4SFabien Sanglard     }
359*3f982cf4SFabien Sanglard   }
360*3f982cf4SFabien Sanglard 
361*3f982cf4SFabien Sanglard   if (!message.answers().empty()) {
362*3f982cf4SFabien Sanglard     sender_->SendMulticast(message);
363*3f982cf4SFabien Sanglard   }
364*3f982cf4SFabien Sanglard 
365*3f982cf4SFabien Sanglard   batch_records_alarm_ = absl::nullopt;
366*3f982cf4SFabien Sanglard   records_to_send_.clear();
367*3f982cf4SFabien Sanglard }
368*3f982cf4SFabien Sanglard 
GetNextAnnounceDelay()369*3f982cf4SFabien Sanglard Clock::duration MdnsPublisher::RecordAnnouncer::GetNextAnnounceDelay() {
370*3f982cf4SFabien Sanglard   return Clock::to_duration(kMinAnnounceDelay *
371*3f982cf4SFabien Sanglard                             pow(kIntervalIncreaseFactor, attempts_));
372*3f982cf4SFabien Sanglard }
373*3f982cf4SFabien Sanglard 
374*3f982cf4SFabien Sanglard }  // namespace discovery
375*3f982cf4SFabien Sanglard }  // namespace openscreen
376