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