1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "discovery/mdns/mdns_responder.h"
6 
7 #include <array>
8 #include <string>
9 #include <utility>
10 
11 #include "discovery/common/config.h"
12 #include "discovery/mdns/mdns_probe_manager.h"
13 #include "discovery/mdns/mdns_publisher.h"
14 #include "discovery/mdns/mdns_querier.h"
15 #include "discovery/mdns/mdns_random.h"
16 #include "discovery/mdns/mdns_receiver.h"
17 #include "discovery/mdns/mdns_sender.h"
18 #include "platform/api/task_runner.h"
19 
20 namespace openscreen {
21 namespace discovery {
22 namespace {
23 
24 constexpr std::array<const char*, 3> kServiceEnumerationDomainLabels{
25     "_services", "_dns-sd", "_udp"};
26 
27 enum AddResult { kNonePresent = 0, kAdded, kAlreadyKnown };
28 
GetTtlForNsecTargetingType(DnsType type)29 std::chrono::seconds GetTtlForNsecTargetingType(DnsType type) {
30   // NOTE: A 'default' switch statement has intentionally been avoided below to
31   // enforce that new DnsTypes added must be added below through a compile-time
32   // check.
33   switch (type) {
34     case DnsType::kA:
35       return kARecordTtl;
36     case DnsType::kAAAA:
37       return kAAAARecordTtl;
38     case DnsType::kPTR:
39       return kPtrRecordTtl;
40     case DnsType::kSRV:
41       return kSrvRecordTtl;
42     case DnsType::kTXT:
43       return kTXTRecordTtl;
44     case DnsType::kANY:
45       // If no records are present, re-querying should happen at the minimum
46       // of any record that might be retrieved at that time.
47       return kSrvRecordTtl;
48     case DnsType::kNSEC:
49     case DnsType::kOPT:
50       // Neither of these types should ever be hit. We should never be creating
51       // an NSEC record for type NSEC, and OPT record querying is not supported,
52       // so creating NSEC records for type OPT is not valid.
53       break;
54   }
55 
56   OSP_NOTREACHED();
57 }
58 
CreateNsecRecord(DomainName target_name,DnsType target_type,DnsClass target_class)59 MdnsRecord CreateNsecRecord(DomainName target_name,
60                             DnsType target_type,
61                             DnsClass target_class) {
62   auto rdata = NsecRecordRdata(target_name, target_type);
63   std::chrono::seconds ttl = GetTtlForNsecTargetingType(target_type);
64   return MdnsRecord(std::move(target_name), DnsType::kNSEC, target_class,
65                     RecordType::kUnique, ttl, std::move(rdata));
66 }
67 
IsValidAdditionalRecordType(DnsType type)68 inline bool IsValidAdditionalRecordType(DnsType type) {
69   return type == DnsType::kSRV || type == DnsType::kTXT ||
70          type == DnsType::kA || type == DnsType::kAAAA;
71 }
72 
AddRecords(std::function<void (MdnsRecord record)> add_func,MdnsResponder::RecordHandler * record_handler,const DomainName & domain,const std::vector<MdnsRecord> & known_answers,DnsType type,DnsClass clazz,bool add_negative_on_unknown)73 AddResult AddRecords(std::function<void(MdnsRecord record)> add_func,
74                      MdnsResponder::RecordHandler* record_handler,
75                      const DomainName& domain,
76                      const std::vector<MdnsRecord>& known_answers,
77                      DnsType type,
78                      DnsClass clazz,
79                      bool add_negative_on_unknown) {
80   auto records = record_handler->GetRecords(domain, type, clazz);
81   if (records.empty()) {
82     if (add_negative_on_unknown) {
83       // TODO(rwkeane): Aggregate all NSEC records together into a single NSEC
84       // record to reduce traffic.
85       add_func(CreateNsecRecord(domain, type, clazz));
86     }
87     return AddResult::kNonePresent;
88   } else {
89     bool added_any_records = false;
90     for (auto it = records.begin(); it != records.end(); it++) {
91       if (std::find(known_answers.begin(), known_answers.end(), *it) ==
92           known_answers.end()) {
93         added_any_records = true;
94         add_func(std::move(*it));
95       }
96     }
97     return added_any_records ? AddResult::kAdded : AddResult::kAlreadyKnown;
98   }
99 }
100 
AddAdditionalRecords(MdnsMessage * message,MdnsResponder::RecordHandler * record_handler,const DomainName & domain,const std::vector<MdnsRecord> & known_answers,DnsType type,DnsClass clazz,bool add_negative_on_unknown)101 inline AddResult AddAdditionalRecords(
102     MdnsMessage* message,
103     MdnsResponder::RecordHandler* record_handler,
104     const DomainName& domain,
105     const std::vector<MdnsRecord>& known_answers,
106     DnsType type,
107     DnsClass clazz,
108     bool add_negative_on_unknown) {
109   OSP_DCHECK(IsValidAdditionalRecordType(type));
110 
111   auto add_func = [message](MdnsRecord record) {
112     message->AddAdditionalRecord(std::move(record));
113   };
114   return AddRecords(std::move(add_func), record_handler, domain, known_answers,
115                     type, clazz, add_negative_on_unknown);
116 }
117 
AddResponseRecords(MdnsMessage * message,MdnsResponder::RecordHandler * record_handler,const DomainName & domain,const std::vector<MdnsRecord> & known_answers,DnsType type,DnsClass clazz,bool add_negative_on_unknown)118 inline AddResult AddResponseRecords(
119     MdnsMessage* message,
120     MdnsResponder::RecordHandler* record_handler,
121     const DomainName& domain,
122     const std::vector<MdnsRecord>& known_answers,
123     DnsType type,
124     DnsClass clazz,
125     bool add_negative_on_unknown) {
126   auto add_func = [message](MdnsRecord record) {
127     message->AddAnswer(std::move(record));
128   };
129   return AddRecords(std::move(add_func), record_handler, domain, known_answers,
130                     type, clazz, add_negative_on_unknown);
131 }
132 
ApplyQueryResults(MdnsMessage * message,MdnsResponder::RecordHandler * record_handler,const DomainName & domain,const std::vector<MdnsRecord> & known_answers,DnsType type,DnsClass clazz,bool is_exclusive_owner)133 void ApplyQueryResults(MdnsMessage* message,
134                        MdnsResponder::RecordHandler* record_handler,
135                        const DomainName& domain,
136                        const std::vector<MdnsRecord>& known_answers,
137                        DnsType type,
138                        DnsClass clazz,
139                        bool is_exclusive_owner) {
140   OSP_DCHECK(type != DnsType::kNSEC);
141 
142   // All records matching the provided query which have been published by this
143   // host should be added to the response message per RFC 6762 section 6. If
144   // this host is the exclusive owner of the queried domain name, then a
145   // negative response NSEC record should be added in the case where the queried
146   // record does not exist, per RFC 6762 section 6.1.
147   if (AddResponseRecords(message, record_handler, domain, known_answers, type,
148                          clazz, is_exclusive_owner) != AddResult::kAdded) {
149     return;
150   }
151 
152   // Per RFC 6763 section 12.1, when querying for a PTR record, all SRV records
153   // and TXT records named in the PTR record's rdata should be added to the
154   // messages additional records, as well as the address records of types A and
155   // AAAA associated with the added SRV records. Per RFC 6762 section 6.1,
156   // records with names matching those of reverse address mappings for PTR
157   // records may be added as negative response NSEC records if they do not
158   // exist.
159   if (type == DnsType::kPTR) {
160     // Add all SRV and TXT records to the additional records section.
161     for (const MdnsRecord& record : message->answers()) {
162       OSP_DCHECK(record.dns_type() == DnsType::kPTR);
163 
164       const DomainName& target =
165           absl::get<PtrRecordRdata>(record.rdata()).ptr_domain();
166       AddAdditionalRecords(message, record_handler, target, known_answers,
167                            DnsType::kSRV, clazz, true);
168       AddAdditionalRecords(message, record_handler, target, known_answers,
169                            DnsType::kTXT, clazz, true);
170     }
171 
172     // Add A and AAAA records associated with an added SRV record to the
173     // additional records section.
174     const int max = message->additional_records().size();
175     for (int i = 0; i < max; i++) {
176       if (message->additional_records()[i].dns_type() != DnsType::kSRV) {
177         continue;
178       }
179 
180       {
181         const MdnsRecord& srv_record = message->additional_records()[i];
182         const DomainName& target =
183             absl::get<SrvRecordRdata>(srv_record.rdata()).target();
184         AddAdditionalRecords(message, record_handler, target, known_answers,
185                              DnsType::kA, clazz, target == domain);
186       }
187 
188       // Must re-calculate the |srv_record|, |target| refs in case a resize of
189       // the additional_records() vector has invalidated them.
190       {
191         const MdnsRecord& srv_record = message->additional_records()[i];
192         const DomainName& target =
193             absl::get<SrvRecordRdata>(srv_record.rdata()).target();
194         AddAdditionalRecords(message, record_handler, target, known_answers,
195                              DnsType::kAAAA, clazz, target == domain);
196       }
197     }
198   } else if (type == DnsType::kSRV) {
199     // Per RFC 6763 section 12.2, when querying for an SRV record, all address
200     // records of type A and AAAA should be added to the additional records
201     // section. Per RFC 6762 section 6.1, if these records are not present and
202     // their name and class match that which is being queried for, a negative
203     // response NSEC record may be added to show their non-existence.
204     for (const auto& srv_record : message->answers()) {
205       OSP_DCHECK(srv_record.dns_type() == DnsType::kSRV);
206 
207       const DomainName& target =
208           absl::get<SrvRecordRdata>(srv_record.rdata()).target();
209       AddAdditionalRecords(message, record_handler, target, known_answers,
210                            DnsType::kA, clazz, target == domain);
211       AddAdditionalRecords(message, record_handler, target, known_answers,
212                            DnsType::kAAAA, clazz, target == domain);
213     }
214   } else if (type == DnsType::kA) {
215     // Per RFC 6762 section 6.2, when querying for an address record of type A
216     // or AAAA, the record of the opposite type should be added to the
217     // additional records section if present. Else, a negative response NSEC
218     // record should be added to show its non-existence.
219     AddAdditionalRecords(message, record_handler, domain, known_answers,
220                          DnsType::kAAAA, clazz, true);
221   } else if (type == DnsType::kAAAA) {
222     AddAdditionalRecords(message, record_handler, domain, known_answers,
223                          DnsType::kA, clazz, true);
224   }
225 
226   // The remaining supported records types are TXT, NSEC, and ANY. RFCs 6762 and
227   // 6763 do not recommend sending any records in the additional records section
228   // for queries of types TXT or ANY, and NSEC records are not supported for
229   // queries.
230 }
231 
232 // Determines if the provided query is a type enumeration query as described in
233 // RFC 6763 section 9.
IsServiceTypeEnumerationQuery(const MdnsQuestion & question)234 bool IsServiceTypeEnumerationQuery(const MdnsQuestion& question) {
235   if (question.dns_type() != DnsType::kPTR) {
236     return false;
237   }
238 
239   if (question.name().labels().size() <
240       kServiceEnumerationDomainLabels.size()) {
241     return false;
242   }
243 
244   const auto question_it = question.name().labels().begin();
245   return std::equal(question_it,
246                     question_it + kServiceEnumerationDomainLabels.size(),
247                     kServiceEnumerationDomainLabels.begin(),
248                     kServiceEnumerationDomainLabels.end());
249 }
250 
251 // Creates the expected response to a type enumeration query as described in RFC
252 // 6763 section 9.
ApplyServiceTypeEnumerationResults(MdnsMessage * message,MdnsResponder::RecordHandler * record_handler,const DomainName & name,DnsClass clazz)253 void ApplyServiceTypeEnumerationResults(
254     MdnsMessage* message,
255     MdnsResponder::RecordHandler* record_handler,
256     const DomainName& name,
257     DnsClass clazz) {
258   if (name.labels().size() < kServiceEnumerationDomainLabels.size()) {
259     return;
260   }
261 
262   std::vector<MdnsRecord::ConstRef> records =
263       record_handler->GetPtrRecords(clazz);
264 
265   // skip "_services._dns-sd._udp." which was already checked for in above
266   // method and just use the domain.
267   const auto domain_it =
268       name.labels().begin() + kServiceEnumerationDomainLabels.size();
269   for (const MdnsRecord& record : records) {
270     // Skip the 2 label service name in the PTR record's name.
271     const auto record_it = record.name().labels().begin() + 2;
272     if (std::equal(domain_it, name.labels().end(), record_it,
273                    record.name().labels().end())) {
274       message->AddAnswer(MdnsRecord(name, DnsType::kPTR, record.dns_class(),
275                                     RecordType::kShared, record.ttl(),
276                                     PtrRecordRdata(record.name())));
277     }
278   }
279 }
280 
IsMultiPacketTruncatedQueryMessage(const MdnsMessage & message)281 bool IsMultiPacketTruncatedQueryMessage(const MdnsMessage& message) {
282   return message.is_truncated() || message.questions().empty();
283 }
284 
285 }  // namespace
286 
287 MdnsResponder::RecordHandler::~RecordHandler() = default;
288 
TruncatedQuery(MdnsResponder * responder,TaskRunner * task_runner,ClockNowFunctionPtr now_function,IPEndpoint src,const MdnsMessage & message,const Config & config)289 MdnsResponder::TruncatedQuery::TruncatedQuery(MdnsResponder* responder,
290                                               TaskRunner* task_runner,
291                                               ClockNowFunctionPtr now_function,
292                                               IPEndpoint src,
293                                               const MdnsMessage& message,
294                                               const Config& config)
295     : max_allowed_messages_(config.maximum_truncated_messages_per_query),
296       max_allowed_records_(config.maximum_known_answer_records_per_query),
297       src_(std::move(src)),
298       responder_(responder),
299       questions_(message.questions()),
300       known_answers_(message.answers()),
301       alarm_(now_function, task_runner) {
302   OSP_DCHECK(responder_);
303   OSP_DCHECK_GT(max_allowed_messages_, 0);
304   OSP_DCHECK_GT(max_allowed_records_, 0);
305 
306   RescheduleSend();
307 }
308 
SetQuery(const MdnsMessage & message)309 void MdnsResponder::TruncatedQuery::SetQuery(const MdnsMessage& message) {
310   OSP_DCHECK(questions_.empty());
311   questions_.insert(questions_.end(), message.questions().begin(),
312                     message.questions().end());
313 
314   // |messages_received_so_far| does not need to be validated here because it is
315   // checked as part of RescheduleSend().
316   known_answers_.insert(known_answers_.end(), message.answers().begin(),
317                         message.answers().end());
318   messages_received_so_far++;
319 
320   RescheduleSend();
321 }
322 
AddKnownAnswers(const std::vector<MdnsRecord> & records)323 void MdnsResponder::TruncatedQuery::AddKnownAnswers(
324     const std::vector<MdnsRecord>& records) {
325   // |messages_received_so_far| does not need to be validated here because it is
326   // checked as part of RescheduleSend().
327   known_answers_.insert(known_answers_.end(), records.begin(), records.end());
328   messages_received_so_far++;
329 
330   RescheduleSend();
331 }
332 
RescheduleSend()333 void MdnsResponder::TruncatedQuery::RescheduleSend() {
334   alarm_.Cancel();
335 
336   Clock::duration send_delay;
337   if (messages_received_so_far >= max_allowed_messages_) {
338     // Maximum number of truncated messages have already been received for this
339     // query.
340     send_delay = Clock::duration(0);
341   } else if (known_answers_.size() >=
342              static_cast<size_t>(max_allowed_records_)) {
343     // Maximum number of known answer records have already been received for
344     // this query.
345     send_delay = Clock::duration(0);
346   } else {
347     // Reschedule to send after a random delay, per RFC 6762.
348     send_delay = responder_->random_delay_->GetTruncatedQueryResponseDelay();
349   }
350 
351   alarm_.ScheduleFromNow([this]() { SendResponse(); }, send_delay);
352 }
353 
SendResponse()354 void MdnsResponder::TruncatedQuery::SendResponse() {
355   alarm_.Cancel();
356 
357   if (questions_.empty()) {
358     OSP_DVLOG << "Known answers received for unknown query, and non received "
359                  "after delay. Dropping them...";
360     return;
361   }
362 
363   responder_->RespondToTruncatedQuery(this);
364 }
365 
MdnsResponder(RecordHandler * record_handler,MdnsProbeManager * ownership_handler,MdnsSender * sender,MdnsReceiver * receiver,TaskRunner * task_runner,ClockNowFunctionPtr now_function,MdnsRandom * random_delay,const Config & config)366 MdnsResponder::MdnsResponder(RecordHandler* record_handler,
367                              MdnsProbeManager* ownership_handler,
368                              MdnsSender* sender,
369                              MdnsReceiver* receiver,
370                              TaskRunner* task_runner,
371                              ClockNowFunctionPtr now_function,
372                              MdnsRandom* random_delay,
373                              const Config& config)
374     : record_handler_(record_handler),
375       ownership_handler_(ownership_handler),
376       sender_(sender),
377       receiver_(receiver),
378       task_runner_(task_runner),
379       now_function_(now_function),
380       random_delay_(random_delay),
381       config_(config) {
382   OSP_DCHECK(record_handler_);
383   OSP_DCHECK(ownership_handler_);
384   OSP_DCHECK(sender_);
385   OSP_DCHECK(receiver_);
386   OSP_DCHECK(task_runner_);
387   OSP_DCHECK(random_delay_);
388   OSP_DCHECK_GT(config_.maximum_truncated_messages_per_query, 0);
389   OSP_DCHECK_GT(config_.maximum_concurrent_truncated_queries_per_interface, 0);
390 
391   auto func = [this](const MdnsMessage& message, const IPEndpoint& src) {
392     OnMessageReceived(message, src);
393   };
394   receiver_->SetQueryCallback(std::move(func));
395 }
396 
~MdnsResponder()397 MdnsResponder::~MdnsResponder() {
398   receiver_->SetQueryCallback(nullptr);
399 }
400 
OnMessageReceived(const MdnsMessage & message,const IPEndpoint & src)401 void MdnsResponder::OnMessageReceived(const MdnsMessage& message,
402                                       const IPEndpoint& src) {
403   OSP_DCHECK(task_runner_->IsRunningOnTaskRunner());
404   OSP_DCHECK(message.type() == MessageType::Query);
405 
406   // Handle multi-packet known answer suppression.
407   if (IsMultiPacketTruncatedQueryMessage(message)) {
408     // If there have been an excessive number of known answers received already,
409     // then skip them. This would most likely mean that:
410     // - A host on the network is misbehaving.
411     // - There is a malicious actor on the network.
412     // In either of these cases, optimize for this host's resource usage.
413     if (truncated_queries_.size() >
414         static_cast<size_t>(
415             config_.maximum_concurrent_truncated_queries_per_interface)) {
416       OSP_DVLOG << "Too many truncated queries have been received. Treating "
417                    "new multi-packet known answer message as normal query";
418     } else {
419       ProcessMultiPacketTruncatedMessage(message, src);
420       return;
421     }
422   }
423 
424   // If the query is a probe query, it will be handled separately by the
425   // MdnsProbeManager. Ignore it here.
426   if (message.IsProbeQuery()) {
427     ownership_handler_->RespondToProbeQuery(message, src);
428     return;
429   }
430 
431   // Else, this is a normal query. Process it as such.
432   // This is the case that should be hit 95+% of the time.
433   OSP_DVLOG << "Received mDNS Query with " << message.questions().size()
434             << " questions. Processing...";
435   const std::vector<MdnsRecord>& known_answers = message.answers();
436   const std::vector<MdnsQuestion>& questions = message.questions();
437   ProcessQueries(src, questions, known_answers);
438 }
439 
ProcessMultiPacketTruncatedMessage(const MdnsMessage & message,const IPEndpoint & src)440 void MdnsResponder::ProcessMultiPacketTruncatedMessage(
441     const MdnsMessage& message,
442     const IPEndpoint& src) {
443   OSP_DVLOG << "Multi-packet truncated message received. Processing...";
444 
445   const bool message_has_question = !message.questions().empty();
446   const bool message_is_truncated = message.is_truncated();
447   OSP_DCHECK(!message_has_question || message_is_truncated);
448 
449   auto pair =
450       truncated_queries_.emplace(src, std::unique_ptr<TruncatedQuery>());
451   std::unique_ptr<TruncatedQuery>& stored_query = pair.first->second;
452 
453   // First, handle the case where this host doesn't have a known answer query
454   // tracked yet. In this case, start tracking the new query.
455   if (pair.second) {
456     // Create a new query and swap it with the old one to save an extra lookup.
457     auto new_query = std::make_unique<TruncatedQuery>(
458         this, task_runner_, now_function_, src, message, config_);
459     stored_query.swap(new_query);
460     return;
461   }
462 
463   // Else, there was already a message received from this host.
464   const bool are_questions_already_stored = !stored_query->questions().empty();
465 
466   // If the new message doesn't have a question, then it must be additional
467   // known answers. Add them to the set of known answers for this truncated
468   // query.
469   if (!message_has_question) {
470     stored_query->AddKnownAnswers(message.answers());
471     return;
472   }
473 
474   // Alternatively, if a record for this host existed, it might be because the
475   // messages were received out-of-order and known answers have already been
476   // received. In this case, associate the new message's query with the known
477   // answers already received.
478   if (!are_questions_already_stored) {
479     stored_query->SetQuery(message);
480     return;
481   }
482 
483   // Else, an ongoing truncated query is already associated with this host and a
484   // new one has also been received. This implies one of the following occurred:
485   // - The sender must have finished sending packets.
486   // - The known answers completing this query somehow got lost on the network.
487   // - A second truncated query was started by the same host, and this host
488   //   won't be able to differentiate which query future known answers are
489   //   associated with.
490   // In any of these cases, there's no reason to continue tracking the old
491   // query. So process it.
492   //
493   // Create a new query and swap it with the old one to save an extra lookup.
494   auto new_query = std::make_unique<TruncatedQuery>(
495       this, task_runner_, now_function_, src, message, config_);
496   stored_query.swap(new_query);
497 
498   // Now that the pointers have been swapped, process the previously stored
499   // query.
500   new_query->SendResponse();
501 }
502 
RespondToTruncatedQuery(TruncatedQuery * query)503 void MdnsResponder::RespondToTruncatedQuery(TruncatedQuery* query) {
504   ProcessQueries(query->src(), query->questions(), query->known_answers());
505   auto it = truncated_queries_.find(query->src());
506 
507   if (it == truncated_queries_.end()) {
508     return;
509   }
510 
511   // If a second query for this same host arrives, then the question found may
512   // not match what is being sent due to the swap done in OnMessageReceived().
513   if (it->second.get() == query) {
514     truncated_queries_.erase(it);
515   }
516 }
517 
ProcessQueries(const IPEndpoint & src,const std::vector<MdnsQuestion> & questions,const std::vector<MdnsRecord> & known_answers)518 void MdnsResponder::ProcessQueries(
519     const IPEndpoint& src,
520     const std::vector<MdnsQuestion>& questions,
521     const std::vector<MdnsRecord>& known_answers) {
522   for (const auto& question : questions) {
523     OSP_DVLOG << "\tProcessing mDNS Query for domain: '"
524               << question.name().ToString() << "', type: '"
525               << question.dns_type() << "' from '" << src << "'";
526 
527     // NSEC records should not be queried for.
528     if (question.dns_type() == DnsType::kNSEC) {
529       continue;
530     }
531 
532     // Only respond to queries for which one of the following is true:
533     // - This host is the sole owner of that domain.
534     // - A record corresponding to this question has been published.
535     // - The query is a service enumeration query.
536     const bool is_service_enumeration = IsServiceTypeEnumerationQuery(question);
537     const bool is_exclusive_owner =
538         ownership_handler_->IsDomainClaimed(question.name());
539     if (!is_service_enumeration && !is_exclusive_owner &&
540         !record_handler_->HasRecords(question.name(), question.dns_type(),
541                                      question.dns_class())) {
542       OSP_DVLOG << "\tmDNS Query processed and no relevant records found!";
543       continue;
544     } else if (is_service_enumeration) {
545       OSP_DVLOG << "\tmDNS Query is for service type enumeration!";
546     }
547 
548     // Relevant records are published, so send them out using the response type
549     // dictated in the question.
550     std::function<void(const MdnsMessage&)> send_response;
551     if (question.response_type() == ResponseType::kMulticast) {
552       send_response = [this](const MdnsMessage& message) {
553         sender_->SendMulticast(message);
554       };
555     } else {
556       OSP_DCHECK(question.response_type() == ResponseType::kUnicast);
557       send_response = [this, src](const MdnsMessage& message) {
558         sender_->SendMessage(message, src);
559       };
560     }
561 
562     // If this host is the exclusive owner, respond immediately. Else, there may
563     // be network contention if all hosts respond simultaneously, so delay the
564     // response as dictated by RFC 6762.
565     if (is_exclusive_owner) {
566       SendResponse(question, known_answers, send_response, is_exclusive_owner);
567     } else {
568       const auto delay = random_delay_->GetSharedRecordResponseDelay();
569       std::function<void()> response = [this, question, known_answers,
570                                         send_response, is_exclusive_owner]() {
571         SendResponse(question, known_answers, send_response,
572                      is_exclusive_owner);
573       };
574       task_runner_->PostTaskWithDelay(response, delay);
575     }
576   }
577 }
578 
SendResponse(const MdnsQuestion & question,const std::vector<MdnsRecord> & known_answers,std::function<void (const MdnsMessage &)> send_response,bool is_exclusive_owner)579 void MdnsResponder::SendResponse(
580     const MdnsQuestion& question,
581     const std::vector<MdnsRecord>& known_answers,
582     std::function<void(const MdnsMessage&)> send_response,
583     bool is_exclusive_owner) {
584   OSP_DCHECK(task_runner_->IsRunningOnTaskRunner());
585 
586   MdnsMessage message(CreateMessageId(), MessageType::Response);
587 
588   if (IsServiceTypeEnumerationQuery(question)) {
589     // This is a special case defined in RFC 6763 section 9, so handle it
590     // separately.
591     ApplyServiceTypeEnumerationResults(&message, record_handler_,
592                                        question.name(), question.dns_class());
593   } else {
594     // NOTE: The exclusive ownership of this record cannot change before this
595     // method is called. Exclusive ownership cannot be gained for a record which
596     // has previously been published, and if this host is the exclusive owner
597     // then this method will have been called without any delay on the task
598     // runner.
599     ApplyQueryResults(&message, record_handler_, question.name(), known_answers,
600                       question.dns_type(), question.dns_class(),
601                       is_exclusive_owner);
602   }
603 
604   // Send the response only if it contains answers to the query.
605   OSP_DVLOG << "\tCompleted Processing mDNS Query for domain: '"
606             << question.name().ToString() << "', type: '" << question.dns_type()
607             << "', with " << message.answers().size() << " results:";
608   for (const auto& record : message.answers()) {
609     OSP_DVLOG << "\t\tanswer (" << record.ToString() << ")";
610   }
611   for (const auto& record : message.additional_records()) {
612     OSP_DVLOG << "\t\tadditional record ('" << record.ToString() << ")";
613   }
614 
615   if (!message.answers().empty()) {
616     send_response(message);
617   }
618 }
619 
620 }  // namespace discovery
621 }  // namespace openscreen
622