• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 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 "osp/impl/mdns_responder_service.h"
6 
7 #include <algorithm>
8 #include <memory>
9 #include <utility>
10 
11 #include "osp/impl/internal_services.h"
12 #include "platform/base/error.h"
13 #include "util/osp_logging.h"
14 #include "util/trace_logging.h"
15 
16 namespace openscreen {
17 namespace osp {
18 namespace {
19 
20 // TODO(btolsch): This should probably at least also contain network identity
21 // information.
ServiceIdFromServiceInstanceName(const DomainName & service_instance)22 std::string ServiceIdFromServiceInstanceName(
23     const DomainName& service_instance) {
24   std::string service_id;
25   service_id.assign(
26       reinterpret_cast<const char*>(service_instance.domain_name().data()),
27       service_instance.domain_name().size());
28   return service_id;
29 }
30 
31 }  // namespace
32 
MdnsResponderService(ClockNowFunctionPtr now_function,TaskRunner * task_runner,const std::string & service_name,const std::string & service_protocol,std::unique_ptr<MdnsResponderAdapterFactory> mdns_responder_factory,std::unique_ptr<MdnsPlatformService> platform)33 MdnsResponderService::MdnsResponderService(
34     ClockNowFunctionPtr now_function,
35     TaskRunner* task_runner,
36     const std::string& service_name,
37     const std::string& service_protocol,
38     std::unique_ptr<MdnsResponderAdapterFactory> mdns_responder_factory,
39     std::unique_ptr<MdnsPlatformService> platform)
40     : service_type_{{service_name, service_protocol}},
41       mdns_responder_factory_(std::move(mdns_responder_factory)),
42       platform_(std::move(platform)),
43       task_runner_(task_runner),
44       background_tasks_alarm_(now_function, task_runner) {}
45 
46 MdnsResponderService::~MdnsResponderService() = default;
47 
SetServiceConfig(const std::string & hostname,const std::string & instance,uint16_t port,const std::vector<NetworkInterfaceIndex> allowlist,const std::map<std::string,std::string> & txt_data)48 void MdnsResponderService::SetServiceConfig(
49     const std::string& hostname,
50     const std::string& instance,
51     uint16_t port,
52     const std::vector<NetworkInterfaceIndex> allowlist,
53     const std::map<std::string, std::string>& txt_data) {
54   OSP_DCHECK(!hostname.empty());
55   OSP_DCHECK(!instance.empty());
56   OSP_DCHECK_NE(0, port);
57   service_hostname_ = hostname;
58   service_instance_name_ = instance;
59   service_port_ = port;
60   interface_index_allowlist_ = allowlist;
61   service_txt_data_ = txt_data;
62 }
63 
OnRead(UdpSocket * socket,ErrorOr<UdpPacket> packet)64 void MdnsResponderService::OnRead(UdpSocket* socket,
65                                   ErrorOr<UdpPacket> packet) {
66   TRACE_SCOPED(TraceCategory::kMdns, "MdnsResponderService::OnRead");
67   if (!mdns_responder_) {
68     return;
69   }
70 
71   mdns_responder_->OnRead(socket, std::move(packet));
72   HandleMdnsEvents();
73 }
74 
OnSendError(UdpSocket * socket,Error error)75 void MdnsResponderService::OnSendError(UdpSocket* socket, Error error) {
76   mdns_responder_->OnSendError(socket, std::move(error));
77 }
78 
OnError(UdpSocket * socket,Error error)79 void MdnsResponderService::OnError(UdpSocket* socket, Error error) {
80   mdns_responder_->OnError(socket, std::move(error));
81 }
82 
StartListener()83 void MdnsResponderService::StartListener() {
84   task_runner_->PostTask([this]() { this->StartListenerInternal(); });
85 }
86 
StartAndSuspendListener()87 void MdnsResponderService::StartAndSuspendListener() {
88   task_runner_->PostTask([this]() { this->StartAndSuspendListenerInternal(); });
89 }
90 
StopListener()91 void MdnsResponderService::StopListener() {
92   task_runner_->PostTask([this]() { this->StopListenerInternal(); });
93 }
94 
SuspendListener()95 void MdnsResponderService::SuspendListener() {
96   task_runner_->PostTask([this]() { this->SuspendListenerInternal(); });
97 }
98 
ResumeListener()99 void MdnsResponderService::ResumeListener() {
100   task_runner_->PostTask([this]() { this->ResumeListenerInternal(); });
101 }
102 
SearchNow(ServiceListener::State from)103 void MdnsResponderService::SearchNow(ServiceListener::State from) {
104   task_runner_->PostTask([this, from]() { this->SearchNowInternal(from); });
105 }
106 
StartPublisher()107 void MdnsResponderService::StartPublisher() {
108   task_runner_->PostTask([this]() { this->StartPublisherInternal(); });
109 }
110 
StartAndSuspendPublisher()111 void MdnsResponderService::StartAndSuspendPublisher() {
112   task_runner_->PostTask(
113       [this]() { this->StartAndSuspendPublisherInternal(); });
114 }
115 
StopPublisher()116 void MdnsResponderService::StopPublisher() {
117   task_runner_->PostTask([this]() { this->StopPublisherInternal(); });
118 }
119 
SuspendPublisher()120 void MdnsResponderService::SuspendPublisher() {
121   task_runner_->PostTask([this]() { this->SuspendPublisherInternal(); });
122 }
123 
ResumePublisher()124 void MdnsResponderService::ResumePublisher() {
125   task_runner_->PostTask([this]() { this->ResumePublisherInternal(); });
126 }
127 
StartListenerInternal()128 void MdnsResponderService::StartListenerInternal() {
129   if (!mdns_responder_) {
130     mdns_responder_ = mdns_responder_factory_->Create();
131   }
132 
133   StartListening();
134   ServiceListenerImpl::Delegate::SetState(ServiceListener::State::kRunning);
135   RunBackgroundTasks();
136 }
137 
StartAndSuspendListenerInternal()138 void MdnsResponderService::StartAndSuspendListenerInternal() {
139   mdns_responder_ = mdns_responder_factory_->Create();
140   ServiceListenerImpl::Delegate::SetState(ServiceListener::State::kSuspended);
141 }
142 
StopListenerInternal()143 void MdnsResponderService::StopListenerInternal() {
144   StopListening();
145   if (!publisher_ || publisher_->state() == ServicePublisher::State::kStopped ||
146       publisher_->state() == ServicePublisher::State::kSuspended) {
147     StopMdnsResponder();
148     if (!publisher_ || publisher_->state() == ServicePublisher::State::kStopped)
149       mdns_responder_.reset();
150   }
151   ServiceListenerImpl::Delegate::SetState(ServiceListener::State::kStopped);
152 }
153 
SuspendListenerInternal()154 void MdnsResponderService::SuspendListenerInternal() {
155   StopMdnsResponder();
156   ServiceListenerImpl::Delegate::SetState(ServiceListener::State::kSuspended);
157 }
158 
ResumeListenerInternal()159 void MdnsResponderService::ResumeListenerInternal() {
160   StartListening();
161   ServiceListenerImpl::Delegate::SetState(ServiceListener::State::kRunning);
162 }
163 
SearchNowInternal(ServiceListener::State from)164 void MdnsResponderService::SearchNowInternal(ServiceListener::State from) {
165   ServiceListenerImpl::Delegate::SetState(from);
166 }
167 
StartPublisherInternal()168 void MdnsResponderService::StartPublisherInternal() {
169   if (!mdns_responder_) {
170     mdns_responder_ = mdns_responder_factory_->Create();
171   }
172 
173   StartService();
174   ServicePublisherImpl::Delegate::SetState(ServicePublisher::State::kRunning);
175   RunBackgroundTasks();
176 }
177 
StartAndSuspendPublisherInternal()178 void MdnsResponderService::StartAndSuspendPublisherInternal() {
179   mdns_responder_ = mdns_responder_factory_->Create();
180   ServicePublisherImpl::Delegate::SetState(ServicePublisher::State::kSuspended);
181 }
182 
StopPublisherInternal()183 void MdnsResponderService::StopPublisherInternal() {
184   StopService();
185   if (!listener_ || listener_->state() == ServiceListener::State::kStopped ||
186       listener_->state() == ServiceListener::State::kSuspended) {
187     StopMdnsResponder();
188     if (!listener_ || listener_->state() == ServiceListener::State::kStopped)
189       mdns_responder_.reset();
190   }
191   ServicePublisherImpl::Delegate::SetState(ServicePublisher::State::kStopped);
192 }
193 
SuspendPublisherInternal()194 void MdnsResponderService::SuspendPublisherInternal() {
195   StopService();
196   ServicePublisherImpl::Delegate::SetState(ServicePublisher::State::kSuspended);
197 }
198 
ResumePublisherInternal()199 void MdnsResponderService::ResumePublisherInternal() {
200   StartService();
201   ServicePublisherImpl::Delegate::SetState(ServicePublisher::State::kRunning);
202 }
203 
operator ()(const NetworkScopedDomainName & a,const NetworkScopedDomainName & b) const204 bool MdnsResponderService::NetworkScopedDomainNameComparator::operator()(
205     const NetworkScopedDomainName& a,
206     const NetworkScopedDomainName& b) const {
207   if (a.socket != b.socket) {
208     return (a.socket - b.socket) < 0;
209   }
210   return DomainNameComparator()(a.domain_name, b.domain_name);
211 }
212 
HandleMdnsEvents()213 void MdnsResponderService::HandleMdnsEvents() {
214   TRACE_SCOPED(TraceCategory::kMdns, "MdnsResponderService::HandleMdnsEvents");
215   // NOTE: In the common case, we will get a single combined packet for
216   // PTR/SRV/TXT/A and then no other packets.  If we don't loop here, we would
217   // start SRV/TXT queries based on the PTR response, but never check for events
218   // again.  This should no longer be a problem when we have correct scheduling
219   // of RunTasks.
220   bool events_possible = false;
221   // NOTE: This set will track which service instances were changed by all the
222   // events throughout all the loop iterations.  At the end, we can dispatch our
223   // ServiceInfo updates to |listener_| just once (e.g. instead of
224   // OnReceiverChanged, OnReceiverChanged, ..., just a single
225   // OnReceiverChanged).
226   InstanceNameSet modified_instance_names;
227   do {
228     events_possible = false;
229     for (auto& ptr_event : mdns_responder_->TakePtrResponses()) {
230       events_possible = HandlePtrEvent(ptr_event, &modified_instance_names) ||
231                         events_possible;
232     }
233     for (auto& srv_event : mdns_responder_->TakeSrvResponses()) {
234       events_possible = HandleSrvEvent(srv_event, &modified_instance_names) ||
235                         events_possible;
236     }
237     for (auto& txt_event : mdns_responder_->TakeTxtResponses()) {
238       events_possible = HandleTxtEvent(txt_event, &modified_instance_names) ||
239                         events_possible;
240     }
241     for (const auto& a_event : mdns_responder_->TakeAResponses()) {
242       events_possible =
243           HandleAEvent(a_event, &modified_instance_names) || events_possible;
244     }
245     for (const auto& aaaa_event : mdns_responder_->TakeAaaaResponses()) {
246       events_possible = HandleAaaaEvent(aaaa_event, &modified_instance_names) ||
247                         events_possible;
248     }
249     if (events_possible) {
250       // NOTE: This still needs to be called here, even though it runs in the
251       // background regularly, because we just finished processing MDNS events.
252       RunBackgroundTasks();
253     }
254   } while (events_possible);
255 
256   for (const auto& instance_name : modified_instance_names) {
257     auto service_entry = service_by_name_.find(instance_name);
258     std::unique_ptr<ServiceInstance>& service = service_entry->second;
259 
260     std::string service_id = ServiceIdFromServiceInstanceName(instance_name);
261     auto receiver_info_entry = receiver_info_.find(service_id);
262     HostInfo* host = GetHostInfo(service->ptr_socket, service->domain_name);
263     if (!IsServiceReady(*service, host)) {
264       if (receiver_info_entry != receiver_info_.end()) {
265         const ServiceInfo& receiver_info = receiver_info_entry->second;
266         listener_->OnReceiverRemoved(receiver_info);
267         receiver_info_.erase(receiver_info_entry);
268       }
269       if (!service->has_ptr_record && !service->has_srv())
270         service_by_name_.erase(service_entry);
271       continue;
272     }
273 
274     // TODO(btolsch): Verify UTF-8 here.
275     std::string friendly_name(instance_name.GetLabels()[0]);
276 
277     if (receiver_info_entry == receiver_info_.end()) {
278       ServiceInfo receiver_info{
279           std::move(service_id),
280           std::move(friendly_name),
281           GetNetworkInterfaceIndexFromSocket(service->ptr_socket),
282           {host->v4_address, service->port},
283           {host->v6_address, service->port}};
284       listener_->OnReceiverAdded(receiver_info);
285       receiver_info_.emplace(receiver_info.service_id,
286                              std::move(receiver_info));
287     } else {
288       ServiceInfo& receiver_info = receiver_info_entry->second;
289       if (receiver_info.Update(
290               std::move(friendly_name),
291               GetNetworkInterfaceIndexFromSocket(service->ptr_socket),
292               {host->v4_address, service->port},
293               {host->v6_address, service->port})) {
294         listener_->OnReceiverChanged(receiver_info);
295       }
296     }
297   }
298 }
299 
StartListening()300 void MdnsResponderService::StartListening() {
301   // TODO(btolsch): This needs the same |interface_index_allowlist_| logic as
302   // StartService, but this can also wait until the network-change TODO is
303   // addressed.
304   if (bound_interfaces_.empty()) {
305     mdns_responder_->Init();
306     bound_interfaces_ = platform_->RegisterInterfaces({});
307     for (auto& interface : bound_interfaces_) {
308       mdns_responder_->RegisterInterface(interface.interface_info,
309                                          interface.subnet, interface.socket);
310     }
311   }
312   ErrorOr<DomainName> service_type =
313       DomainName::FromLabels(service_type_.begin(), service_type_.end());
314   OSP_CHECK(service_type);
315   for (const auto& interface : bound_interfaces_) {
316     mdns_responder_->StartPtrQuery(interface.socket, service_type.value());
317   }
318 }
319 
StopListening()320 void MdnsResponderService::StopListening() {
321   ErrorOr<DomainName> service_type =
322       DomainName::FromLabels(service_type_.begin(), service_type_.end());
323   OSP_CHECK(service_type);
324   for (const auto& kv : network_scoped_domain_to_host_) {
325     const NetworkScopedDomainName& scoped_domain = kv.first;
326 
327     mdns_responder_->StopAQuery(scoped_domain.socket,
328                                 scoped_domain.domain_name);
329     mdns_responder_->StopAaaaQuery(scoped_domain.socket,
330                                    scoped_domain.domain_name);
331   }
332   network_scoped_domain_to_host_.clear();
333   for (const auto& service : service_by_name_) {
334     UdpSocket* const socket = service.second->ptr_socket;
335     mdns_responder_->StopSrvQuery(socket, service.first);
336     mdns_responder_->StopTxtQuery(socket, service.first);
337   }
338   service_by_name_.clear();
339   for (const auto& interface : bound_interfaces_) {
340     mdns_responder_->StopPtrQuery(interface.socket, service_type.value());
341   }
342   RemoveAllReceivers();
343 }
344 
StartService()345 void MdnsResponderService::StartService() {
346   // TODO(crbug.com/openscreen/45): This should really be a library-wide
347   // allowed list.
348   if (!bound_interfaces_.empty() && !interface_index_allowlist_.empty()) {
349     // TODO(btolsch): New interfaces won't be picked up on this path, but this
350     // also highlights a larger issue of the interface list being frozen while
351     // no state transitions are being made.  There should be another interface
352     // on MdnsPlatformService for getting network interface updates.
353     std::vector<MdnsPlatformService::BoundInterface> deregistered_interfaces;
354     for (auto it = bound_interfaces_.begin(); it != bound_interfaces_.end();) {
355       if (std::find(interface_index_allowlist_.begin(),
356                     interface_index_allowlist_.end(),
357                     it->interface_info.index) ==
358           interface_index_allowlist_.end()) {
359         mdns_responder_->DeregisterInterface(it->socket);
360         deregistered_interfaces.push_back(*it);
361         it = bound_interfaces_.erase(it);
362       } else {
363         ++it;
364       }
365     }
366     platform_->DeregisterInterfaces(deregistered_interfaces);
367   } else if (bound_interfaces_.empty()) {
368     mdns_responder_->Init();
369     mdns_responder_->SetHostLabel(service_hostname_);
370     bound_interfaces_ =
371         platform_->RegisterInterfaces(interface_index_allowlist_);
372     for (auto& interface : bound_interfaces_) {
373       mdns_responder_->RegisterInterface(interface.interface_info,
374                                          interface.subnet, interface.socket);
375     }
376   }
377 
378   ErrorOr<DomainName> domain_name =
379       DomainName::FromLabels(&service_hostname_, &service_hostname_ + 1);
380   OSP_CHECK(domain_name) << "bad hostname configured: " << service_hostname_;
381   DomainName name = std::move(domain_name.value());
382 
383   Error error = name.Append(DomainName::GetLocalDomain());
384   OSP_CHECK(error.ok());
385 
386   mdns_responder_->RegisterService(service_instance_name_, service_type_[0],
387                                    service_type_[1], name, service_port_,
388                                    service_txt_data_);
389 }
390 
StopService()391 void MdnsResponderService::StopService() {
392   mdns_responder_->DeregisterService(service_instance_name_, service_type_[0],
393                                      service_type_[1]);
394 }
395 
StopMdnsResponder()396 void MdnsResponderService::StopMdnsResponder() {
397   mdns_responder_->Close();
398   platform_->DeregisterInterfaces(bound_interfaces_);
399   bound_interfaces_.clear();
400   network_scoped_domain_to_host_.clear();
401   service_by_name_.clear();
402   RemoveAllReceivers();
403 }
404 
UpdatePendingServiceInfoSet(InstanceNameSet * modified_instance_names,const DomainName & domain_name)405 void MdnsResponderService::UpdatePendingServiceInfoSet(
406     InstanceNameSet* modified_instance_names,
407     const DomainName& domain_name) {
408   for (auto& entry : service_by_name_) {
409     const auto& instance_name = entry.first;
410     const auto& instance = entry.second;
411     if (instance->domain_name == domain_name) {
412       modified_instance_names->emplace(instance_name);
413     }
414   }
415 }
416 
RemoveAllReceivers()417 void MdnsResponderService::RemoveAllReceivers() {
418   bool had_receivers = !receiver_info_.empty();
419   receiver_info_.clear();
420   if (had_receivers)
421     listener_->OnAllReceiversRemoved();
422 }
423 
HandlePtrEvent(const PtrEvent & ptr_event,InstanceNameSet * modified_instance_names)424 bool MdnsResponderService::HandlePtrEvent(
425     const PtrEvent& ptr_event,
426     InstanceNameSet* modified_instance_names) {
427   bool events_possible = false;
428   const auto& instance_name = ptr_event.service_instance;
429   UdpSocket* const socket = ptr_event.header.socket;
430   auto entry = service_by_name_.find(ptr_event.service_instance);
431   switch (ptr_event.header.response_type) {
432     case QueryEventHeader::Type::kAddedNoCache:
433       break;
434     case QueryEventHeader::Type::kAdded: {
435       if (entry != service_by_name_.end()) {
436         entry->second->has_ptr_record = true;
437         modified_instance_names->emplace(instance_name);
438         break;
439       }
440       mdns_responder_->StartSrvQuery(socket, instance_name);
441       mdns_responder_->StartTxtQuery(socket, instance_name);
442       events_possible = true;
443 
444       auto new_instance = std::make_unique<ServiceInstance>();
445       new_instance->ptr_socket = socket;
446       new_instance->has_ptr_record = true;
447       modified_instance_names->emplace(instance_name);
448       service_by_name_.emplace(std::move(instance_name),
449                                std::move(new_instance));
450     } break;
451     case QueryEventHeader::Type::kRemoved:
452       if (entry == service_by_name_.end())
453         break;
454       if (entry->second->ptr_socket != socket)
455         break;
456       entry->second->has_ptr_record = false;
457       // NOTE: Occasionally, we can observe this situation in the wild where the
458       // PTR for a service is removed and then immediately re-added (like an odd
459       // refresh).  Additionally, the recommended TTL of PTR records is much
460       // shorter than the other records.  This means that short network drops or
461       // latency spikes could cause the PTR refresh queries and/or responses to
462       // be lost so the record isn't quite refreshed in time.  The solution here
463       // and in HandleSrvEvent is to only remove the service records completely
464       // when both the PTR and SRV have been removed.
465       if (!entry->second->has_srv()) {
466         mdns_responder_->StopSrvQuery(socket, instance_name);
467         mdns_responder_->StopTxtQuery(socket, instance_name);
468       }
469       modified_instance_names->emplace(std::move(instance_name));
470       break;
471   }
472   return events_possible;
473 }
474 
HandleSrvEvent(const SrvEvent & srv_event,InstanceNameSet * modified_instance_names)475 bool MdnsResponderService::HandleSrvEvent(
476     const SrvEvent& srv_event,
477     InstanceNameSet* modified_instance_names) {
478   bool events_possible = false;
479   auto& domain_name = srv_event.domain_name;
480   const auto& instance_name = srv_event.service_instance;
481   UdpSocket* const socket = srv_event.header.socket;
482   auto entry = service_by_name_.find(srv_event.service_instance);
483   if (entry == service_by_name_.end())
484     return events_possible;
485   switch (srv_event.header.response_type) {
486     case QueryEventHeader::Type::kAddedNoCache:
487       break;
488     case QueryEventHeader::Type::kAdded: {
489       NetworkScopedDomainName scoped_domain_name{socket, domain_name};
490       auto host_entry = network_scoped_domain_to_host_.find(scoped_domain_name);
491       if (host_entry == network_scoped_domain_to_host_.end()) {
492         mdns_responder_->StartAQuery(socket, domain_name);
493         mdns_responder_->StartAaaaQuery(socket, domain_name);
494         events_possible = true;
495         auto result = network_scoped_domain_to_host_.emplace(
496             std::move(scoped_domain_name), HostInfo{});
497         host_entry = result.first;
498       }
499       auto& dependent_services = host_entry->second.services;
500       if (std::find_if(dependent_services.begin(), dependent_services.end(),
501                        [entry](ServiceInstance* instance) {
502                          return instance == entry->second.get();
503                        }) == dependent_services.end()) {
504         dependent_services.push_back(entry->second.get());
505       }
506       entry->second->domain_name = std::move(domain_name);
507       entry->second->port = srv_event.port;
508       modified_instance_names->emplace(std::move(instance_name));
509     } break;
510     case QueryEventHeader::Type::kRemoved: {
511       NetworkScopedDomainName scoped_domain_name{socket, domain_name};
512       auto host_entry = network_scoped_domain_to_host_.find(scoped_domain_name);
513       if (host_entry != network_scoped_domain_to_host_.end()) {
514         auto& dependent_services = host_entry->second.services;
515         dependent_services.erase(
516             std::remove_if(dependent_services.begin(), dependent_services.end(),
517                            [entry](ServiceInstance* instance) {
518                              return instance == entry->second.get();
519                            }),
520             dependent_services.end());
521         if (dependent_services.empty()) {
522           mdns_responder_->StopAQuery(socket, domain_name);
523           mdns_responder_->StopAaaaQuery(socket, domain_name);
524           network_scoped_domain_to_host_.erase(host_entry);
525         }
526       }
527       entry->second->domain_name = DomainName();
528       entry->second->port = 0;
529       if (!entry->second->has_ptr_record) {
530         mdns_responder_->StopSrvQuery(socket, instance_name);
531         mdns_responder_->StopTxtQuery(socket, instance_name);
532       }
533       modified_instance_names->emplace(std::move(instance_name));
534     } break;
535   }
536   return events_possible;
537 }
538 
HandleTxtEvent(const TxtEvent & txt_event,InstanceNameSet * modified_instance_names)539 bool MdnsResponderService::HandleTxtEvent(
540     const TxtEvent& txt_event,
541     InstanceNameSet* modified_instance_names) {
542   bool events_possible = false;
543   const auto& instance_name = txt_event.service_instance;
544   auto entry = service_by_name_.find(instance_name);
545   if (entry == service_by_name_.end())
546     return events_possible;
547   switch (txt_event.header.response_type) {
548     case QueryEventHeader::Type::kAddedNoCache:
549       break;
550     case QueryEventHeader::Type::kAdded:
551       modified_instance_names->emplace(instance_name);
552       if (entry == service_by_name_.end()) {
553         auto result = service_by_name_.emplace(
554             std::move(instance_name), std::make_unique<ServiceInstance>());
555         entry = result.first;
556       }
557       entry->second->txt_info = std::move(txt_event.txt_info);
558       break;
559     case QueryEventHeader::Type::kRemoved:
560       entry->second->txt_info.clear();
561       modified_instance_names->emplace(std::move(instance_name));
562       break;
563   }
564   return events_possible;
565 }
566 
HandleAddressEvent(UdpSocket * socket,QueryEventHeader::Type response_type,const DomainName & domain_name,bool a_event,const IPAddress & address,InstanceNameSet * modified_instance_names)567 bool MdnsResponderService::HandleAddressEvent(
568     UdpSocket* socket,
569     QueryEventHeader::Type response_type,
570     const DomainName& domain_name,
571     bool a_event,
572     const IPAddress& address,
573     InstanceNameSet* modified_instance_names) {
574   bool events_possible = false;
575   switch (response_type) {
576     case QueryEventHeader::Type::kAddedNoCache:
577       break;
578     case QueryEventHeader::Type::kAdded: {
579       HostInfo* host = AddOrGetHostInfo(socket, domain_name);
580       if (a_event)
581         host->v4_address = address;
582       else
583         host->v6_address = address;
584       UpdatePendingServiceInfoSet(modified_instance_names, domain_name);
585     } break;
586     case QueryEventHeader::Type::kRemoved: {
587       HostInfo* host = GetHostInfo(socket, domain_name);
588 
589       if (a_event)
590         host->v4_address = IPAddress();
591       else
592         host->v6_address = IPAddress();
593 
594       if (host->v4_address || host->v6_address)
595         UpdatePendingServiceInfoSet(modified_instance_names, domain_name);
596     } break;
597   }
598   return events_possible;
599 }
600 
HandleAEvent(const AEvent & a_event,InstanceNameSet * modified_instance_names)601 bool MdnsResponderService::HandleAEvent(
602     const AEvent& a_event,
603     InstanceNameSet* modified_instance_names) {
604   return HandleAddressEvent(a_event.header.socket, a_event.header.response_type,
605                             a_event.domain_name, true, a_event.address,
606                             modified_instance_names);
607 }
608 
HandleAaaaEvent(const AaaaEvent & aaaa_event,InstanceNameSet * modified_instance_names)609 bool MdnsResponderService::HandleAaaaEvent(
610     const AaaaEvent& aaaa_event,
611     InstanceNameSet* modified_instance_names) {
612   return HandleAddressEvent(aaaa_event.header.socket,
613                             aaaa_event.header.response_type,
614                             aaaa_event.domain_name, false, aaaa_event.address,
615                             modified_instance_names);
616 }
617 
AddOrGetHostInfo(UdpSocket * socket,const DomainName & domain_name)618 MdnsResponderService::HostInfo* MdnsResponderService::AddOrGetHostInfo(
619     UdpSocket* socket,
620     const DomainName& domain_name) {
621   return &network_scoped_domain_to_host_[NetworkScopedDomainName{socket,
622                                                                  domain_name}];
623 }
624 
GetHostInfo(UdpSocket * socket,const DomainName & domain_name)625 MdnsResponderService::HostInfo* MdnsResponderService::GetHostInfo(
626     UdpSocket* socket,
627     const DomainName& domain_name) {
628   auto kv = network_scoped_domain_to_host_.find(
629       NetworkScopedDomainName{socket, domain_name});
630   if (kv == network_scoped_domain_to_host_.end())
631     return nullptr;
632 
633   return &kv->second;
634 }
635 
IsServiceReady(const ServiceInstance & instance,HostInfo * host) const636 bool MdnsResponderService::IsServiceReady(const ServiceInstance& instance,
637                                           HostInfo* host) const {
638   return (host && instance.has_ptr_record && instance.has_srv() &&
639           !instance.txt_info.empty() && (host->v4_address || host->v6_address));
640 }
641 
GetNetworkInterfaceIndexFromSocket(const UdpSocket * socket) const642 NetworkInterfaceIndex MdnsResponderService::GetNetworkInterfaceIndexFromSocket(
643     const UdpSocket* socket) const {
644   auto it = std::find_if(
645       bound_interfaces_.begin(), bound_interfaces_.end(),
646       [socket](const MdnsPlatformService::BoundInterface& interface) {
647         return interface.socket == socket;
648       });
649   if (it == bound_interfaces_.end())
650     return kInvalidNetworkInterfaceIndex;
651   return it->interface_info.index;
652 }
653 
RunBackgroundTasks()654 void MdnsResponderService::RunBackgroundTasks() {
655   if (!mdns_responder_) {
656     return;
657   }
658   const auto delay_until_next_run = mdns_responder_->RunTasks();
659   background_tasks_alarm_.ScheduleFromNow([this] { RunBackgroundTasks(); },
660                                           delay_until_next_run);
661 }
662 
663 }  // namespace osp
664 }  // namespace openscreen
665