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