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/dnssd/impl/publisher_impl.h"
6*3f982cf4SFabien Sanglard
7*3f982cf4SFabien Sanglard #include <map>
8*3f982cf4SFabien Sanglard #include <string>
9*3f982cf4SFabien Sanglard #include <utility>
10*3f982cf4SFabien Sanglard #include <vector>
11*3f982cf4SFabien Sanglard
12*3f982cf4SFabien Sanglard #include "absl/types/optional.h"
13*3f982cf4SFabien Sanglard #include "discovery/common/reporting_client.h"
14*3f982cf4SFabien Sanglard #include "discovery/dnssd/impl/conversion_layer.h"
15*3f982cf4SFabien Sanglard #include "discovery/dnssd/impl/instance_key.h"
16*3f982cf4SFabien Sanglard #include "discovery/dnssd/impl/network_interface_config.h"
17*3f982cf4SFabien Sanglard #include "discovery/mdns/public/mdns_constants.h"
18*3f982cf4SFabien Sanglard #include "platform/api/task_runner.h"
19*3f982cf4SFabien Sanglard #include "platform/base/error.h"
20*3f982cf4SFabien Sanglard #include "util/trace_logging.h"
21*3f982cf4SFabien Sanglard
22*3f982cf4SFabien Sanglard namespace openscreen {
23*3f982cf4SFabien Sanglard namespace discovery {
24*3f982cf4SFabien Sanglard namespace {
25*3f982cf4SFabien Sanglard
CreateEndpoint(DnsSdInstance instance,InstanceKey key,const NetworkInterfaceConfig & network_config)26*3f982cf4SFabien Sanglard DnsSdInstanceEndpoint CreateEndpoint(
27*3f982cf4SFabien Sanglard DnsSdInstance instance,
28*3f982cf4SFabien Sanglard InstanceKey key,
29*3f982cf4SFabien Sanglard const NetworkInterfaceConfig& network_config) {
30*3f982cf4SFabien Sanglard std::vector<IPEndpoint> endpoints;
31*3f982cf4SFabien Sanglard if (network_config.HasAddressV4()) {
32*3f982cf4SFabien Sanglard endpoints.push_back({network_config.address_v4(), instance.port()});
33*3f982cf4SFabien Sanglard }
34*3f982cf4SFabien Sanglard if (network_config.HasAddressV6()) {
35*3f982cf4SFabien Sanglard endpoints.push_back({network_config.address_v6(), instance.port()});
36*3f982cf4SFabien Sanglard }
37*3f982cf4SFabien Sanglard return DnsSdInstanceEndpoint(
38*3f982cf4SFabien Sanglard key.instance_id(), key.service_id(), key.domain_id(), instance.txt(),
39*3f982cf4SFabien Sanglard network_config.network_interface(), std::move(endpoints));
40*3f982cf4SFabien Sanglard }
41*3f982cf4SFabien Sanglard
UpdateDomain(const DomainName & name,DnsSdInstance instance,const NetworkInterfaceConfig & network_config)42*3f982cf4SFabien Sanglard DnsSdInstanceEndpoint UpdateDomain(
43*3f982cf4SFabien Sanglard const DomainName& name,
44*3f982cf4SFabien Sanglard DnsSdInstance instance,
45*3f982cf4SFabien Sanglard const NetworkInterfaceConfig& network_config) {
46*3f982cf4SFabien Sanglard return CreateEndpoint(std::move(instance), InstanceKey(name), network_config);
47*3f982cf4SFabien Sanglard }
48*3f982cf4SFabien Sanglard
CreateEndpoint(DnsSdInstance instance,const NetworkInterfaceConfig & network_config)49*3f982cf4SFabien Sanglard DnsSdInstanceEndpoint CreateEndpoint(
50*3f982cf4SFabien Sanglard DnsSdInstance instance,
51*3f982cf4SFabien Sanglard const NetworkInterfaceConfig& network_config) {
52*3f982cf4SFabien Sanglard InstanceKey key(instance);
53*3f982cf4SFabien Sanglard return CreateEndpoint(std::move(instance), std::move(key), network_config);
54*3f982cf4SFabien Sanglard }
55*3f982cf4SFabien Sanglard
56*3f982cf4SFabien Sanglard template <typename T>
FindKey(std::map<DnsSdInstance,T> * instances,const InstanceKey & key)57*3f982cf4SFabien Sanglard inline typename std::map<DnsSdInstance, T>::iterator FindKey(
58*3f982cf4SFabien Sanglard std::map<DnsSdInstance, T>* instances,
59*3f982cf4SFabien Sanglard const InstanceKey& key) {
60*3f982cf4SFabien Sanglard return std::find_if(instances->begin(), instances->end(),
61*3f982cf4SFabien Sanglard [&key](const std::pair<DnsSdInstance, T>& pair) {
62*3f982cf4SFabien Sanglard return key == InstanceKey(pair.first);
63*3f982cf4SFabien Sanglard });
64*3f982cf4SFabien Sanglard }
65*3f982cf4SFabien Sanglard
66*3f982cf4SFabien Sanglard template <typename T>
EraseInstancesWithServiceId(std::map<DnsSdInstance,T> * instances,const std::string & service_id)67*3f982cf4SFabien Sanglard int EraseInstancesWithServiceId(std::map<DnsSdInstance, T>* instances,
68*3f982cf4SFabien Sanglard const std::string& service_id) {
69*3f982cf4SFabien Sanglard int removed_count = 0;
70*3f982cf4SFabien Sanglard for (auto it = instances->begin(); it != instances->end();) {
71*3f982cf4SFabien Sanglard if (it->first.service_id() == service_id) {
72*3f982cf4SFabien Sanglard removed_count++;
73*3f982cf4SFabien Sanglard it = instances->erase(it);
74*3f982cf4SFabien Sanglard } else {
75*3f982cf4SFabien Sanglard it++;
76*3f982cf4SFabien Sanglard }
77*3f982cf4SFabien Sanglard }
78*3f982cf4SFabien Sanglard
79*3f982cf4SFabien Sanglard return removed_count;
80*3f982cf4SFabien Sanglard }
81*3f982cf4SFabien Sanglard
82*3f982cf4SFabien Sanglard } // namespace
83*3f982cf4SFabien Sanglard
PublisherImpl(MdnsService * publisher,ReportingClient * reporting_client,TaskRunner * task_runner,const NetworkInterfaceConfig * network_config)84*3f982cf4SFabien Sanglard PublisherImpl::PublisherImpl(MdnsService* publisher,
85*3f982cf4SFabien Sanglard ReportingClient* reporting_client,
86*3f982cf4SFabien Sanglard TaskRunner* task_runner,
87*3f982cf4SFabien Sanglard const NetworkInterfaceConfig* network_config)
88*3f982cf4SFabien Sanglard : mdns_publisher_(publisher),
89*3f982cf4SFabien Sanglard reporting_client_(reporting_client),
90*3f982cf4SFabien Sanglard task_runner_(task_runner),
91*3f982cf4SFabien Sanglard network_config_(network_config) {
92*3f982cf4SFabien Sanglard OSP_DCHECK(mdns_publisher_);
93*3f982cf4SFabien Sanglard OSP_DCHECK(reporting_client_);
94*3f982cf4SFabien Sanglard OSP_DCHECK(task_runner_);
95*3f982cf4SFabien Sanglard }
96*3f982cf4SFabien Sanglard
97*3f982cf4SFabien Sanglard PublisherImpl::~PublisherImpl() = default;
98*3f982cf4SFabien Sanglard
Register(const DnsSdInstance & instance,Client * client)99*3f982cf4SFabien Sanglard Error PublisherImpl::Register(const DnsSdInstance& instance, Client* client) {
100*3f982cf4SFabien Sanglard OSP_DCHECK(task_runner_->IsRunningOnTaskRunner());
101*3f982cf4SFabien Sanglard OSP_DCHECK(client != nullptr);
102*3f982cf4SFabien Sanglard
103*3f982cf4SFabien Sanglard if (published_instances_.find(instance) != published_instances_.end()) {
104*3f982cf4SFabien Sanglard UpdateRegistration(instance);
105*3f982cf4SFabien Sanglard } else if (pending_instances_.find(instance) != pending_instances_.end()) {
106*3f982cf4SFabien Sanglard return Error::Code::kOperationInProgress;
107*3f982cf4SFabien Sanglard }
108*3f982cf4SFabien Sanglard
109*3f982cf4SFabien Sanglard InstanceKey key(instance);
110*3f982cf4SFabien Sanglard const IPAddress& address = network_config_->GetAddress();
111*3f982cf4SFabien Sanglard OSP_DCHECK(address);
112*3f982cf4SFabien Sanglard pending_instances_.emplace(CreateEndpoint(instance, *network_config_),
113*3f982cf4SFabien Sanglard client);
114*3f982cf4SFabien Sanglard
115*3f982cf4SFabien Sanglard OSP_DVLOG << "Registering instance '" << instance.instance_id() << "'";
116*3f982cf4SFabien Sanglard
117*3f982cf4SFabien Sanglard return mdns_publisher_->StartProbe(this, GetDomainName(key), address);
118*3f982cf4SFabien Sanglard }
119*3f982cf4SFabien Sanglard
UpdateRegistration(const DnsSdInstance & instance)120*3f982cf4SFabien Sanglard Error PublisherImpl::UpdateRegistration(const DnsSdInstance& instance) {
121*3f982cf4SFabien Sanglard OSP_DCHECK(task_runner_->IsRunningOnTaskRunner());
122*3f982cf4SFabien Sanglard
123*3f982cf4SFabien Sanglard // Check if the instance is still pending publication.
124*3f982cf4SFabien Sanglard auto it = FindKey(&pending_instances_, InstanceKey(instance));
125*3f982cf4SFabien Sanglard
126*3f982cf4SFabien Sanglard OSP_DVLOG << "Updating instance '" << instance.instance_id() << "'";
127*3f982cf4SFabien Sanglard
128*3f982cf4SFabien Sanglard // If it is a pending instance, update it. Else, try to update a published
129*3f982cf4SFabien Sanglard // instance.
130*3f982cf4SFabien Sanglard if (it != pending_instances_.end()) {
131*3f982cf4SFabien Sanglard // The instance, service, and domain ids have not changed, so only the
132*3f982cf4SFabien Sanglard // remaining data needs to change. The ongoing probe does not need to be
133*3f982cf4SFabien Sanglard // modified.
134*3f982cf4SFabien Sanglard Client* const client = it->second;
135*3f982cf4SFabien Sanglard pending_instances_.erase(it);
136*3f982cf4SFabien Sanglard pending_instances_.emplace(CreateEndpoint(instance, *network_config_),
137*3f982cf4SFabien Sanglard client);
138*3f982cf4SFabien Sanglard return Error::None();
139*3f982cf4SFabien Sanglard } else {
140*3f982cf4SFabien Sanglard return UpdatePublishedRegistration(instance);
141*3f982cf4SFabien Sanglard }
142*3f982cf4SFabien Sanglard }
143*3f982cf4SFabien Sanglard
UpdatePublishedRegistration(const DnsSdInstance & instance)144*3f982cf4SFabien Sanglard Error PublisherImpl::UpdatePublishedRegistration(
145*3f982cf4SFabien Sanglard const DnsSdInstance& instance) {
146*3f982cf4SFabien Sanglard OSP_DCHECK(task_runner_->IsRunningOnTaskRunner());
147*3f982cf4SFabien Sanglard
148*3f982cf4SFabien Sanglard auto published_instance_it =
149*3f982cf4SFabien Sanglard FindKey(&published_instances_, InstanceKey(instance));
150*3f982cf4SFabien Sanglard
151*3f982cf4SFabien Sanglard // Check preconditions called out in header. Specifically, the updated
152*3f982cf4SFabien Sanglard // instance must be making changes to an already published instance.
153*3f982cf4SFabien Sanglard if (published_instance_it == published_instances_.end()) {
154*3f982cf4SFabien Sanglard return Error::Code::kParameterInvalid;
155*3f982cf4SFabien Sanglard }
156*3f982cf4SFabien Sanglard
157*3f982cf4SFabien Sanglard const DnsSdInstanceEndpoint updated_endpoint =
158*3f982cf4SFabien Sanglard UpdateDomain(GetDomainName(InstanceKey(published_instance_it->second)),
159*3f982cf4SFabien Sanglard instance, *network_config_);
160*3f982cf4SFabien Sanglard if (published_instance_it->second == updated_endpoint) {
161*3f982cf4SFabien Sanglard return Error::Code::kParameterInvalid;
162*3f982cf4SFabien Sanglard }
163*3f982cf4SFabien Sanglard
164*3f982cf4SFabien Sanglard // Get all instances which have changed. By design, there an only be one
165*3f982cf4SFabien Sanglard // instance of each DnsType, so use that here to simplify this step. First in
166*3f982cf4SFabien Sanglard // each pair is the old instances, second is the new instance.
167*3f982cf4SFabien Sanglard std::map<DnsType,
168*3f982cf4SFabien Sanglard std::pair<absl::optional<MdnsRecord>, absl::optional<MdnsRecord>>>
169*3f982cf4SFabien Sanglard changed_records;
170*3f982cf4SFabien Sanglard const std::vector<MdnsRecord> old_records =
171*3f982cf4SFabien Sanglard GetDnsRecords(published_instance_it->second);
172*3f982cf4SFabien Sanglard const std::vector<MdnsRecord> new_records = GetDnsRecords(updated_endpoint);
173*3f982cf4SFabien Sanglard
174*3f982cf4SFabien Sanglard // Populate the first part of each pair in |changed_instances|.
175*3f982cf4SFabien Sanglard for (size_t i = 0; i < old_records.size(); i++) {
176*3f982cf4SFabien Sanglard const auto key = old_records[i].dns_type();
177*3f982cf4SFabien Sanglard OSP_DCHECK(changed_records.find(key) == changed_records.end());
178*3f982cf4SFabien Sanglard auto value = std::make_pair(std::move(old_records[i]), absl::nullopt);
179*3f982cf4SFabien Sanglard changed_records.emplace(key, std::move(value));
180*3f982cf4SFabien Sanglard }
181*3f982cf4SFabien Sanglard
182*3f982cf4SFabien Sanglard // Populate the second part of each pair in |changed_records|.
183*3f982cf4SFabien Sanglard for (size_t i = 0; i < new_records.size(); i++) {
184*3f982cf4SFabien Sanglard const auto key = new_records[i].dns_type();
185*3f982cf4SFabien Sanglard auto find_it = changed_records.find(key);
186*3f982cf4SFabien Sanglard if (find_it == changed_records.end()) {
187*3f982cf4SFabien Sanglard std::pair<absl::optional<MdnsRecord>, absl::optional<MdnsRecord>> value(
188*3f982cf4SFabien Sanglard absl::nullopt, std::move(new_records[i]));
189*3f982cf4SFabien Sanglard changed_records.emplace(key, std::move(value));
190*3f982cf4SFabien Sanglard } else {
191*3f982cf4SFabien Sanglard find_it->second.second = std::move(new_records[i]);
192*3f982cf4SFabien Sanglard }
193*3f982cf4SFabien Sanglard }
194*3f982cf4SFabien Sanglard
195*3f982cf4SFabien Sanglard // Apply changes called out in |changed_records|.
196*3f982cf4SFabien Sanglard Error total_result = Error::None();
197*3f982cf4SFabien Sanglard for (const auto& pair : changed_records) {
198*3f982cf4SFabien Sanglard OSP_DCHECK(pair.second.first != absl::nullopt ||
199*3f982cf4SFabien Sanglard pair.second.second != absl::nullopt);
200*3f982cf4SFabien Sanglard if (pair.second.first == absl::nullopt) {
201*3f982cf4SFabien Sanglard TRACE_SCOPED(TraceCategory::kDiscovery, "mdns.RegisterRecord");
202*3f982cf4SFabien Sanglard auto error = mdns_publisher_->RegisterRecord(pair.second.second.value());
203*3f982cf4SFabien Sanglard TRACE_SET_RESULT(error);
204*3f982cf4SFabien Sanglard if (!error.ok()) {
205*3f982cf4SFabien Sanglard total_result = error;
206*3f982cf4SFabien Sanglard }
207*3f982cf4SFabien Sanglard } else if (pair.second.second == absl::nullopt) {
208*3f982cf4SFabien Sanglard TRACE_SCOPED(TraceCategory::kDiscovery, "mdns.UnregisterRecord");
209*3f982cf4SFabien Sanglard auto error = mdns_publisher_->UnregisterRecord(pair.second.first.value());
210*3f982cf4SFabien Sanglard TRACE_SET_RESULT(error);
211*3f982cf4SFabien Sanglard if (!error.ok()) {
212*3f982cf4SFabien Sanglard total_result = error;
213*3f982cf4SFabien Sanglard }
214*3f982cf4SFabien Sanglard } else if (pair.second.first.value() != pair.second.second.value()) {
215*3f982cf4SFabien Sanglard TRACE_SCOPED(TraceCategory::kDiscovery, "mdns.UpdateRegisteredRecord");
216*3f982cf4SFabien Sanglard auto error = mdns_publisher_->UpdateRegisteredRecord(
217*3f982cf4SFabien Sanglard pair.second.first.value(), pair.second.second.value());
218*3f982cf4SFabien Sanglard TRACE_SET_RESULT(error);
219*3f982cf4SFabien Sanglard if (!error.ok()) {
220*3f982cf4SFabien Sanglard total_result = error;
221*3f982cf4SFabien Sanglard }
222*3f982cf4SFabien Sanglard }
223*3f982cf4SFabien Sanglard }
224*3f982cf4SFabien Sanglard
225*3f982cf4SFabien Sanglard // Replace the old instances with the new ones.
226*3f982cf4SFabien Sanglard published_instances_.erase(published_instance_it);
227*3f982cf4SFabien Sanglard published_instances_.emplace(instance, std::move(updated_endpoint));
228*3f982cf4SFabien Sanglard
229*3f982cf4SFabien Sanglard return total_result;
230*3f982cf4SFabien Sanglard }
231*3f982cf4SFabien Sanglard
DeregisterAll(const std::string & service)232*3f982cf4SFabien Sanglard ErrorOr<int> PublisherImpl::DeregisterAll(const std::string& service) {
233*3f982cf4SFabien Sanglard OSP_DCHECK(task_runner_->IsRunningOnTaskRunner());
234*3f982cf4SFabien Sanglard
235*3f982cf4SFabien Sanglard OSP_DVLOG << "Deregistering all instances";
236*3f982cf4SFabien Sanglard
237*3f982cf4SFabien Sanglard int removed_count = 0;
238*3f982cf4SFabien Sanglard Error error = Error::None();
239*3f982cf4SFabien Sanglard for (auto it = published_instances_.begin();
240*3f982cf4SFabien Sanglard it != published_instances_.end();) {
241*3f982cf4SFabien Sanglard if (it->second.service_id() == service) {
242*3f982cf4SFabien Sanglard for (const auto& mdns_record : GetDnsRecords(it->second)) {
243*3f982cf4SFabien Sanglard TRACE_SCOPED(TraceCategory::kDiscovery, "mdns.UnregisterRecord");
244*3f982cf4SFabien Sanglard auto publisher_error = mdns_publisher_->UnregisterRecord(mdns_record);
245*3f982cf4SFabien Sanglard TRACE_SET_RESULT(error);
246*3f982cf4SFabien Sanglard if (!publisher_error.ok()) {
247*3f982cf4SFabien Sanglard error = publisher_error;
248*3f982cf4SFabien Sanglard }
249*3f982cf4SFabien Sanglard }
250*3f982cf4SFabien Sanglard removed_count++;
251*3f982cf4SFabien Sanglard it = published_instances_.erase(it);
252*3f982cf4SFabien Sanglard } else {
253*3f982cf4SFabien Sanglard it++;
254*3f982cf4SFabien Sanglard }
255*3f982cf4SFabien Sanglard }
256*3f982cf4SFabien Sanglard
257*3f982cf4SFabien Sanglard removed_count += EraseInstancesWithServiceId(&pending_instances_, service);
258*3f982cf4SFabien Sanglard
259*3f982cf4SFabien Sanglard if (!error.ok()) {
260*3f982cf4SFabien Sanglard return error;
261*3f982cf4SFabien Sanglard } else {
262*3f982cf4SFabien Sanglard return removed_count;
263*3f982cf4SFabien Sanglard }
264*3f982cf4SFabien Sanglard }
265*3f982cf4SFabien Sanglard
OnDomainFound(const DomainName & requested_name,const DomainName & confirmed_name)266*3f982cf4SFabien Sanglard void PublisherImpl::OnDomainFound(const DomainName& requested_name,
267*3f982cf4SFabien Sanglard const DomainName& confirmed_name) {
268*3f982cf4SFabien Sanglard TRACE_DEFAULT_SCOPED(TraceCategory::kDiscovery);
269*3f982cf4SFabien Sanglard OSP_DCHECK(task_runner_->IsRunningOnTaskRunner());
270*3f982cf4SFabien Sanglard
271*3f982cf4SFabien Sanglard OSP_DVLOG << "Domain successfully claimed: '" << confirmed_name.ToString()
272*3f982cf4SFabien Sanglard << "' based on requested name: '" << requested_name.ToString()
273*3f982cf4SFabien Sanglard << "'";
274*3f982cf4SFabien Sanglard
275*3f982cf4SFabien Sanglard auto it = FindKey(&pending_instances_, InstanceKey(requested_name));
276*3f982cf4SFabien Sanglard
277*3f982cf4SFabien Sanglard if (it == pending_instances_.end()) {
278*3f982cf4SFabien Sanglard // This will be hit if the instance was deregister'd before the probe phase
279*3f982cf4SFabien Sanglard // was completed.
280*3f982cf4SFabien Sanglard return;
281*3f982cf4SFabien Sanglard }
282*3f982cf4SFabien Sanglard
283*3f982cf4SFabien Sanglard DnsSdInstance requested_instance = std::move(it->first);
284*3f982cf4SFabien Sanglard DnsSdInstanceEndpoint endpoint =
285*3f982cf4SFabien Sanglard CreateEndpoint(requested_instance, *network_config_);
286*3f982cf4SFabien Sanglard Client* const client = it->second;
287*3f982cf4SFabien Sanglard pending_instances_.erase(it);
288*3f982cf4SFabien Sanglard
289*3f982cf4SFabien Sanglard InstanceKey requested_key(requested_instance);
290*3f982cf4SFabien Sanglard
291*3f982cf4SFabien Sanglard if (requested_name != confirmed_name) {
292*3f982cf4SFabien Sanglard OSP_DCHECK(HasValidDnsRecordAddress(confirmed_name));
293*3f982cf4SFabien Sanglard endpoint =
294*3f982cf4SFabien Sanglard UpdateDomain(confirmed_name, requested_instance, *network_config_);
295*3f982cf4SFabien Sanglard }
296*3f982cf4SFabien Sanglard
297*3f982cf4SFabien Sanglard for (const auto& mdns_record : GetDnsRecords(endpoint)) {
298*3f982cf4SFabien Sanglard TRACE_SCOPED(TraceCategory::kDiscovery, "mdns.RegisterRecord");
299*3f982cf4SFabien Sanglard Error result = mdns_publisher_->RegisterRecord(mdns_record);
300*3f982cf4SFabien Sanglard if (!result.ok()) {
301*3f982cf4SFabien Sanglard reporting_client_->OnRecoverableError(
302*3f982cf4SFabien Sanglard Error(Error::Code::kRecordPublicationError, result.ToString()));
303*3f982cf4SFabien Sanglard }
304*3f982cf4SFabien Sanglard }
305*3f982cf4SFabien Sanglard
306*3f982cf4SFabien Sanglard auto pair = published_instances_.emplace(std::move(requested_instance),
307*3f982cf4SFabien Sanglard std::move(endpoint));
308*3f982cf4SFabien Sanglard client->OnEndpointClaimed(pair.first->first, pair.first->second);
309*3f982cf4SFabien Sanglard }
310*3f982cf4SFabien Sanglard
311*3f982cf4SFabien Sanglard } // namespace discovery
312*3f982cf4SFabien Sanglard } // namespace openscreen
313