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