xref: /aosp_15_r20/external/openscreen/discovery/mdns/mdns_probe_manager.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_probe_manager.h"
6*3f982cf4SFabien Sanglard 
7*3f982cf4SFabien Sanglard #include <string>
8*3f982cf4SFabien Sanglard #include <utility>
9*3f982cf4SFabien Sanglard 
10*3f982cf4SFabien Sanglard #include "discovery/mdns/mdns_sender.h"
11*3f982cf4SFabien Sanglard #include "platform/api/task_runner.h"
12*3f982cf4SFabien Sanglard 
13*3f982cf4SFabien Sanglard namespace openscreen {
14*3f982cf4SFabien Sanglard namespace discovery {
15*3f982cf4SFabien Sanglard namespace {
16*3f982cf4SFabien Sanglard 
17*3f982cf4SFabien Sanglard // The timespan by which to delay subsequent mDNS Probe queries for the same
18*3f982cf4SFabien Sanglard // domain name when a simultaneous query from another host is detected, as
19*3f982cf4SFabien Sanglard // described in RFC 6762 section 8.2
20*3f982cf4SFabien Sanglard constexpr std::chrono::seconds kSimultaneousProbeDelay =
21*3f982cf4SFabien Sanglard     std::chrono::seconds(1);
22*3f982cf4SFabien Sanglard 
CreateRetryDomainName(const DomainName & name,int attempt)23*3f982cf4SFabien Sanglard DomainName CreateRetryDomainName(const DomainName& name, int attempt) {
24*3f982cf4SFabien Sanglard   OSP_DCHECK(name.labels().size());
25*3f982cf4SFabien Sanglard   std::vector<std::string> labels = name.labels();
26*3f982cf4SFabien Sanglard   std::string& label = labels[0];
27*3f982cf4SFabien Sanglard   std::string attempts_str = std::to_string(attempt);
28*3f982cf4SFabien Sanglard   if (label.size() + attempts_str.size() >= kMaxLabelLength) {
29*3f982cf4SFabien Sanglard     label = label.substr(0, kMaxLabelLength - attempts_str.size());
30*3f982cf4SFabien Sanglard   }
31*3f982cf4SFabien Sanglard   label.append(attempts_str);
32*3f982cf4SFabien Sanglard 
33*3f982cf4SFabien Sanglard   return DomainName(std::move(labels));
34*3f982cf4SFabien Sanglard }
35*3f982cf4SFabien Sanglard 
36*3f982cf4SFabien Sanglard }  // namespace
37*3f982cf4SFabien Sanglard 
38*3f982cf4SFabien Sanglard MdnsProbeManager::~MdnsProbeManager() = default;
39*3f982cf4SFabien Sanglard 
MdnsProbeManagerImpl(MdnsSender * sender,MdnsReceiver * receiver,MdnsRandom * random_delay,TaskRunner * task_runner,ClockNowFunctionPtr now_function)40*3f982cf4SFabien Sanglard MdnsProbeManagerImpl::MdnsProbeManagerImpl(MdnsSender* sender,
41*3f982cf4SFabien Sanglard                                            MdnsReceiver* receiver,
42*3f982cf4SFabien Sanglard                                            MdnsRandom* random_delay,
43*3f982cf4SFabien Sanglard                                            TaskRunner* task_runner,
44*3f982cf4SFabien Sanglard                                            ClockNowFunctionPtr now_function)
45*3f982cf4SFabien Sanglard     : sender_(sender),
46*3f982cf4SFabien Sanglard       receiver_(receiver),
47*3f982cf4SFabien Sanglard       random_delay_(random_delay),
48*3f982cf4SFabien Sanglard       task_runner_(task_runner),
49*3f982cf4SFabien Sanglard       now_function_(now_function) {
50*3f982cf4SFabien Sanglard   OSP_DCHECK(sender_);
51*3f982cf4SFabien Sanglard   OSP_DCHECK(receiver_);
52*3f982cf4SFabien Sanglard   OSP_DCHECK(task_runner_);
53*3f982cf4SFabien Sanglard   OSP_DCHECK(random_delay_);
54*3f982cf4SFabien Sanglard }
55*3f982cf4SFabien Sanglard 
56*3f982cf4SFabien Sanglard MdnsProbeManagerImpl::~MdnsProbeManagerImpl() = default;
57*3f982cf4SFabien Sanglard 
StartProbe(MdnsDomainConfirmedProvider * callback,DomainName requested_name,IPAddress address)58*3f982cf4SFabien Sanglard Error MdnsProbeManagerImpl::StartProbe(MdnsDomainConfirmedProvider* callback,
59*3f982cf4SFabien Sanglard                                        DomainName requested_name,
60*3f982cf4SFabien Sanglard                                        IPAddress address) {
61*3f982cf4SFabien Sanglard   // Check if |requested_name| is already being queried for.
62*3f982cf4SFabien Sanglard   if (FindOngoingProbe(requested_name) != ongoing_probes_.end()) {
63*3f982cf4SFabien Sanglard     return Error::Code::kOperationInProgress;
64*3f982cf4SFabien Sanglard   }
65*3f982cf4SFabien Sanglard 
66*3f982cf4SFabien Sanglard   // Check if |requested_name| is already claimed.
67*3f982cf4SFabien Sanglard   if (IsDomainClaimed(requested_name)) {
68*3f982cf4SFabien Sanglard     return Error::Code::kItemAlreadyExists;
69*3f982cf4SFabien Sanglard   }
70*3f982cf4SFabien Sanglard 
71*3f982cf4SFabien Sanglard   OSP_DVLOG << "Starting new mDNS Probe for domain '"
72*3f982cf4SFabien Sanglard             << requested_name.ToString() << "'";
73*3f982cf4SFabien Sanglard 
74*3f982cf4SFabien Sanglard   // Begin a new probe.
75*3f982cf4SFabien Sanglard   auto probe = CreateProbe(requested_name, std::move(address));
76*3f982cf4SFabien Sanglard   ongoing_probes_.emplace_back(std::move(probe), std::move(requested_name),
77*3f982cf4SFabien Sanglard                                callback);
78*3f982cf4SFabien Sanglard   return Error::None();
79*3f982cf4SFabien Sanglard }
80*3f982cf4SFabien Sanglard 
StopProbe(const DomainName & requested_name)81*3f982cf4SFabien Sanglard Error MdnsProbeManagerImpl::StopProbe(const DomainName& requested_name) {
82*3f982cf4SFabien Sanglard   auto it = FindOngoingProbe(requested_name);
83*3f982cf4SFabien Sanglard   if (it == ongoing_probes_.end()) {
84*3f982cf4SFabien Sanglard     return Error::Code::kItemNotFound;
85*3f982cf4SFabien Sanglard   }
86*3f982cf4SFabien Sanglard 
87*3f982cf4SFabien Sanglard   ongoing_probes_.erase(it);
88*3f982cf4SFabien Sanglard   return Error::None();
89*3f982cf4SFabien Sanglard }
90*3f982cf4SFabien Sanglard 
IsDomainClaimed(const DomainName & domain) const91*3f982cf4SFabien Sanglard bool MdnsProbeManagerImpl::IsDomainClaimed(const DomainName& domain) const {
92*3f982cf4SFabien Sanglard   return FindCompletedProbe(domain) != completed_probes_.end();
93*3f982cf4SFabien Sanglard }
94*3f982cf4SFabien Sanglard 
RespondToProbeQuery(const MdnsMessage & message,const IPEndpoint & src)95*3f982cf4SFabien Sanglard void MdnsProbeManagerImpl::RespondToProbeQuery(const MdnsMessage& message,
96*3f982cf4SFabien Sanglard                                                const IPEndpoint& src) {
97*3f982cf4SFabien Sanglard   OSP_DCHECK(!message.questions().empty());
98*3f982cf4SFabien Sanglard 
99*3f982cf4SFabien Sanglard   const std::vector<MdnsQuestion>& questions = message.questions();
100*3f982cf4SFabien Sanglard   MdnsMessage send_message(CreateMessageId(), MessageType::Response);
101*3f982cf4SFabien Sanglard 
102*3f982cf4SFabien Sanglard   // Iterate across all questions asked and all completed probes and add A or
103*3f982cf4SFabien Sanglard   // AAAA records associated with the endpoints for which the names match.
104*3f982cf4SFabien Sanglard   // |questions| is expected to be of size 1 and |completed_probes| should be
105*3f982cf4SFabien Sanglard   // small (generally size 1), so this should be fast.
106*3f982cf4SFabien Sanglard   for (const auto& question : questions) {
107*3f982cf4SFabien Sanglard     for (auto it = completed_probes_.begin(); it != completed_probes_.end();
108*3f982cf4SFabien Sanglard          it++) {
109*3f982cf4SFabien Sanglard       if (question.name() == (*it)->target_name()) {
110*3f982cf4SFabien Sanglard         send_message.AddAnswer((*it)->address_record());
111*3f982cf4SFabien Sanglard         break;
112*3f982cf4SFabien Sanglard       }
113*3f982cf4SFabien Sanglard     }
114*3f982cf4SFabien Sanglard   }
115*3f982cf4SFabien Sanglard 
116*3f982cf4SFabien Sanglard   if (!send_message.answers().empty()) {
117*3f982cf4SFabien Sanglard     sender_->SendMessage(send_message, src);
118*3f982cf4SFabien Sanglard   } else {
119*3f982cf4SFabien Sanglard     // If the name isn't already claimed, check to see if a probe is ongoing. If
120*3f982cf4SFabien Sanglard     // so, compare the address record for that probe with the one in the
121*3f982cf4SFabien Sanglard     // received message and resolve as specified in RFC 6762 section 8.2.
122*3f982cf4SFabien Sanglard     TiebreakSimultaneousProbes(message);
123*3f982cf4SFabien Sanglard   }
124*3f982cf4SFabien Sanglard }
125*3f982cf4SFabien Sanglard 
TiebreakSimultaneousProbes(const MdnsMessage & message)126*3f982cf4SFabien Sanglard void MdnsProbeManagerImpl::TiebreakSimultaneousProbes(
127*3f982cf4SFabien Sanglard     const MdnsMessage& message) {
128*3f982cf4SFabien Sanglard   OSP_DCHECK(!message.questions().empty());
129*3f982cf4SFabien Sanglard   OSP_DCHECK(!message.authority_records().empty());
130*3f982cf4SFabien Sanglard 
131*3f982cf4SFabien Sanglard   for (const auto& question : message.questions()) {
132*3f982cf4SFabien Sanglard     for (auto it = ongoing_probes_.begin(); it != ongoing_probes_.end(); it++) {
133*3f982cf4SFabien Sanglard       if (it->probe->target_name() == question.name()) {
134*3f982cf4SFabien Sanglard         // When a host is probing for a set of records with the same name, or a
135*3f982cf4SFabien Sanglard         // message is received containing multiple tiebreaker records answering
136*3f982cf4SFabien Sanglard         // a given probe question in the Question Section, the host's records
137*3f982cf4SFabien Sanglard         // and the tiebreaker records from the message are each sorted into
138*3f982cf4SFabien Sanglard         // order, and then compared pairwise, using the same comparison
139*3f982cf4SFabien Sanglard         // technique described above, until a difference is found. Because the
140*3f982cf4SFabien Sanglard         // probe object is guaranteed to only have the address record, only the
141*3f982cf4SFabien Sanglard         // lowest authority record is needed.
142*3f982cf4SFabien Sanglard         auto lowest_record_it =
143*3f982cf4SFabien Sanglard             std::min_element(message.authority_records().begin(),
144*3f982cf4SFabien Sanglard                              message.authority_records().end());
145*3f982cf4SFabien Sanglard 
146*3f982cf4SFabien Sanglard         // If this host finds that its own data is lexicographically later, it
147*3f982cf4SFabien Sanglard         // simply ignores the other host's probe. The other host will have
148*3f982cf4SFabien Sanglard         // receive this host's probe simultaneously, and will reject its own
149*3f982cf4SFabien Sanglard         // probe through this same calculation.
150*3f982cf4SFabien Sanglard         const MdnsRecord& probe_record = it->probe->address_record();
151*3f982cf4SFabien Sanglard         if (probe_record > *lowest_record_it) {
152*3f982cf4SFabien Sanglard           break;
153*3f982cf4SFabien Sanglard         }
154*3f982cf4SFabien Sanglard 
155*3f982cf4SFabien Sanglard         // If the probe query is only of size one and the record received is
156*3f982cf4SFabien Sanglard         // equal to this record, then the received query is the same as what
157*3f982cf4SFabien Sanglard         // this probe is sending out. In this case, nothing needs to be done.
158*3f982cf4SFabien Sanglard         if (message.authority_records().size() == 1 &&
159*3f982cf4SFabien Sanglard             !(probe_record < *lowest_record_it)) {
160*3f982cf4SFabien Sanglard           break;
161*3f982cf4SFabien Sanglard         }
162*3f982cf4SFabien Sanglard 
163*3f982cf4SFabien Sanglard         // At this point, one of the following must be true:
164*3f982cf4SFabien Sanglard         // - The query's lowest record is greater than this probe's record
165*3f982cf4SFabien Sanglard         // - The query's lowest record equals this probe's record but it also
166*3f982cf4SFabien Sanglard         //   has additional records.
167*3f982cf4SFabien Sanglard         // In either case, the query must take priority over this probe. This
168*3f982cf4SFabien Sanglard         // host defers to the winning host by waiting one second, and then
169*3f982cf4SFabien Sanglard         // begins probing for this record again. See RFC 6762 section 8.2 for
170*3f982cf4SFabien Sanglard         // the logic behind waiting one second.
171*3f982cf4SFabien Sanglard         it->probe->Postpone(kSimultaneousProbeDelay);
172*3f982cf4SFabien Sanglard         break;
173*3f982cf4SFabien Sanglard       }
174*3f982cf4SFabien Sanglard     }
175*3f982cf4SFabien Sanglard   }
176*3f982cf4SFabien Sanglard }
177*3f982cf4SFabien Sanglard 
OnProbeSuccess(MdnsProbe * probe)178*3f982cf4SFabien Sanglard void MdnsProbeManagerImpl::OnProbeSuccess(MdnsProbe* probe) {
179*3f982cf4SFabien Sanglard   auto it = FindOngoingProbe(probe);
180*3f982cf4SFabien Sanglard   if (it != ongoing_probes_.end()) {
181*3f982cf4SFabien Sanglard     DomainName target_name = it->probe->target_name();
182*3f982cf4SFabien Sanglard     completed_probes_.push_back(std::move(it->probe));
183*3f982cf4SFabien Sanglard     DomainName requested = std::move(it->requested_name);
184*3f982cf4SFabien Sanglard     MdnsDomainConfirmedProvider* callback = it->callback;
185*3f982cf4SFabien Sanglard     ongoing_probes_.erase(it);
186*3f982cf4SFabien Sanglard     callback->OnDomainFound(std::move(requested), std::move(target_name));
187*3f982cf4SFabien Sanglard   }
188*3f982cf4SFabien Sanglard }
189*3f982cf4SFabien Sanglard 
OnProbeFailure(MdnsProbe * probe)190*3f982cf4SFabien Sanglard void MdnsProbeManagerImpl::OnProbeFailure(MdnsProbe* probe) {
191*3f982cf4SFabien Sanglard   auto ongoing_it = FindOngoingProbe(probe);
192*3f982cf4SFabien Sanglard   if (ongoing_it == ongoing_probes_.end()) {
193*3f982cf4SFabien Sanglard     // This means that the probe was canceled.
194*3f982cf4SFabien Sanglard     return;
195*3f982cf4SFabien Sanglard   }
196*3f982cf4SFabien Sanglard 
197*3f982cf4SFabien Sanglard   OSP_DVLOG << "Probe for domain '"
198*3f982cf4SFabien Sanglard             << CreateRetryDomainName(ongoing_it->requested_name,
199*3f982cf4SFabien Sanglard                                      ongoing_it->num_probes_failed)
200*3f982cf4SFabien Sanglard                    .ToString()
201*3f982cf4SFabien Sanglard             << "' failed. Trying new domain...";
202*3f982cf4SFabien Sanglard 
203*3f982cf4SFabien Sanglard   // Create a new probe with a modified domain name.
204*3f982cf4SFabien Sanglard   DomainName new_name = CreateRetryDomainName(ongoing_it->requested_name,
205*3f982cf4SFabien Sanglard                                               ++ongoing_it->num_probes_failed);
206*3f982cf4SFabien Sanglard 
207*3f982cf4SFabien Sanglard   // If this domain has already been claimed, skip ahead to knowing it's
208*3f982cf4SFabien Sanglard   // claimed.
209*3f982cf4SFabien Sanglard   auto completed_it = FindCompletedProbe(new_name);
210*3f982cf4SFabien Sanglard   if (completed_it != completed_probes_.end()) {
211*3f982cf4SFabien Sanglard     DomainName requested_name = std::move(ongoing_it->requested_name);
212*3f982cf4SFabien Sanglard     MdnsDomainConfirmedProvider* callback = ongoing_it->callback;
213*3f982cf4SFabien Sanglard     ongoing_probes_.erase(ongoing_it);
214*3f982cf4SFabien Sanglard     callback->OnDomainFound(requested_name, (*completed_it)->target_name());
215*3f982cf4SFabien Sanglard   } else {
216*3f982cf4SFabien Sanglard     std::unique_ptr<MdnsProbe> new_probe =
217*3f982cf4SFabien Sanglard         CreateProbe(std::move(new_name), ongoing_it->probe->address());
218*3f982cf4SFabien Sanglard     ongoing_it->probe = std::move(new_probe);
219*3f982cf4SFabien Sanglard   }
220*3f982cf4SFabien Sanglard }
221*3f982cf4SFabien Sanglard 
222*3f982cf4SFabien Sanglard std::vector<std::unique_ptr<MdnsProbe>>::const_iterator
FindCompletedProbe(const DomainName & name) const223*3f982cf4SFabien Sanglard MdnsProbeManagerImpl::FindCompletedProbe(const DomainName& name) const {
224*3f982cf4SFabien Sanglard   return std::find_if(completed_probes_.begin(), completed_probes_.end(),
225*3f982cf4SFabien Sanglard                       [&name](const std::unique_ptr<MdnsProbe>& completed) {
226*3f982cf4SFabien Sanglard                         return completed->target_name() == name;
227*3f982cf4SFabien Sanglard                       });
228*3f982cf4SFabien Sanglard }
229*3f982cf4SFabien Sanglard 
230*3f982cf4SFabien Sanglard std::vector<MdnsProbeManagerImpl::OngoingProbe>::iterator
FindOngoingProbe(const DomainName & name)231*3f982cf4SFabien Sanglard MdnsProbeManagerImpl::FindOngoingProbe(const DomainName& name) {
232*3f982cf4SFabien Sanglard   return std::find_if(ongoing_probes_.begin(), ongoing_probes_.end(),
233*3f982cf4SFabien Sanglard                       [&name](const OngoingProbe& ongoing) {
234*3f982cf4SFabien Sanglard                         return ongoing.requested_name == name;
235*3f982cf4SFabien Sanglard                       });
236*3f982cf4SFabien Sanglard }
237*3f982cf4SFabien Sanglard 
238*3f982cf4SFabien Sanglard std::vector<MdnsProbeManagerImpl::OngoingProbe>::iterator
FindOngoingProbe(MdnsProbe * probe)239*3f982cf4SFabien Sanglard MdnsProbeManagerImpl::FindOngoingProbe(MdnsProbe* probe) {
240*3f982cf4SFabien Sanglard   return std::find_if(ongoing_probes_.begin(), ongoing_probes_.end(),
241*3f982cf4SFabien Sanglard                       [&probe](const OngoingProbe& ongoing) {
242*3f982cf4SFabien Sanglard                         return ongoing.probe.get() == probe;
243*3f982cf4SFabien Sanglard                       });
244*3f982cf4SFabien Sanglard }
245*3f982cf4SFabien Sanglard 
OngoingProbe(std::unique_ptr<MdnsProbe> probe,DomainName name,MdnsDomainConfirmedProvider * callback)246*3f982cf4SFabien Sanglard MdnsProbeManagerImpl::OngoingProbe::OngoingProbe(
247*3f982cf4SFabien Sanglard     std::unique_ptr<MdnsProbe> probe,
248*3f982cf4SFabien Sanglard     DomainName name,
249*3f982cf4SFabien Sanglard     MdnsDomainConfirmedProvider* callback)
250*3f982cf4SFabien Sanglard     : probe(std::move(probe)),
251*3f982cf4SFabien Sanglard       requested_name(std::move(name)),
252*3f982cf4SFabien Sanglard       callback(callback) {}
253*3f982cf4SFabien Sanglard 
254*3f982cf4SFabien Sanglard }  // namespace discovery
255*3f982cf4SFabien Sanglard }  // namespace openscreen
256