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_probe.h"
6
7 #include <utility>
8
9 #include "discovery/mdns/mdns_random.h"
10 #include "discovery/mdns/mdns_sender.h"
11 #include "discovery/mdns/public/mdns_constants.h"
12 #include "platform/api/task_runner.h"
13 #include "platform/api/time.h"
14
15 namespace openscreen {
16 namespace discovery {
17
MdnsProbe(DomainName target_name,IPAddress address)18 MdnsProbe::MdnsProbe(DomainName target_name, IPAddress address)
19 : target_name_(std::move(target_name)),
20 address_(std::move(address)),
21 address_record_(CreateAddressRecord(target_name_, address_)) {}
22
23 MdnsProbe::~MdnsProbe() = default;
24
25 MdnsProbeImpl::Observer::~Observer() = default;
26
MdnsProbeImpl(MdnsSender * sender,MdnsReceiver * receiver,MdnsRandom * random_delay,TaskRunner * task_runner,ClockNowFunctionPtr now_function,Observer * observer,DomainName target_name,IPAddress address)27 MdnsProbeImpl::MdnsProbeImpl(MdnsSender* sender,
28 MdnsReceiver* receiver,
29 MdnsRandom* random_delay,
30 TaskRunner* task_runner,
31 ClockNowFunctionPtr now_function,
32 Observer* observer,
33 DomainName target_name,
34 IPAddress address)
35 : MdnsProbe(std::move(target_name), std::move(address)),
36 random_delay_(random_delay),
37 task_runner_(task_runner),
38 now_function_(now_function),
39 alarm_(now_function_, task_runner_),
40 sender_(sender),
41 receiver_(receiver),
42 observer_(observer) {
43 OSP_DCHECK(sender_);
44 OSP_DCHECK(receiver_);
45 OSP_DCHECK(random_delay_);
46 OSP_DCHECK(task_runner_);
47 OSP_DCHECK(observer_);
48
49 receiver_->AddResponseCallback(this);
50 alarm_.ScheduleFromNow([this]() { ProbeOnce(); },
51 random_delay_->GetInitialProbeDelay());
52 }
53
~MdnsProbeImpl()54 MdnsProbeImpl::~MdnsProbeImpl() {
55 OSP_DCHECK(task_runner_->IsRunningOnTaskRunner());
56
57 Stop();
58 }
59
ProbeOnce()60 void MdnsProbeImpl::ProbeOnce() {
61 OSP_DCHECK(task_runner_->IsRunningOnTaskRunner());
62
63 if (successful_probe_queries_++ < kProbeIterationCountBeforeSuccess) {
64 // MdnsQuerier cannot be used, because probe queries cannot use the cache,
65 // so instead directly send the query through the MdnsSender.
66 MdnsMessage probe_query(CreateMessageId(), MessageType::Query);
67 MdnsQuestion probe_question(target_name(), DnsType::kANY, DnsClass::kIN,
68 ResponseType::kUnicast);
69 probe_query.AddQuestion(probe_question);
70 probe_query.AddAuthorityRecord(address_record());
71 sender_->SendMulticast(probe_query);
72
73 alarm_.ScheduleFromNow([this]() { ProbeOnce(); },
74 kDelayBetweenProbeQueries);
75 } else {
76 Stop();
77 observer_->OnProbeSuccess(this);
78 }
79 }
80
Stop()81 void MdnsProbeImpl::Stop() {
82 OSP_DCHECK(task_runner_->IsRunningOnTaskRunner());
83
84 if (is_running_) {
85 alarm_.Cancel();
86 receiver_->RemoveResponseCallback(this);
87 is_running_ = false;
88 }
89 }
90
Postpone(std::chrono::seconds delay)91 void MdnsProbeImpl::Postpone(std::chrono::seconds delay) {
92 OSP_DCHECK(task_runner_->IsRunningOnTaskRunner());
93
94 successful_probe_queries_ = 0;
95 alarm_.Cancel();
96 alarm_.ScheduleFromNow([this]() { ProbeOnce(); }, Clock::to_duration(delay));
97 }
98
OnMessageReceived(const MdnsMessage & message)99 void MdnsProbeImpl::OnMessageReceived(const MdnsMessage& message) {
100 OSP_DCHECK(task_runner_->IsRunningOnTaskRunner());
101 OSP_DCHECK(message.type() == MessageType::Response);
102
103 for (const auto& record : message.answers()) {
104 if (record.name() == target_name()) {
105 Stop();
106 observer_->OnProbeFailure(this);
107 }
108 }
109 }
110
111 } // namespace discovery
112 } // namespace openscreen
113