| // Copyright 2018 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "osp/impl/mdns_responder_service.h" |
| |
| #include <algorithm> |
| #include <memory> |
| #include <utility> |
| |
| #include "osp/impl/internal_services.h" |
| #include "platform/base/error.h" |
| #include "util/osp_logging.h" |
| #include "util/trace_logging.h" |
| |
| namespace openscreen { |
| namespace osp { |
| namespace { |
| |
| // TODO(btolsch): This should probably at least also contain network identity |
| // information. |
| std::string ServiceIdFromServiceInstanceName( |
| const DomainName& service_instance) { |
| std::string service_id; |
| service_id.assign( |
| reinterpret_cast<const char*>(service_instance.domain_name().data()), |
| service_instance.domain_name().size()); |
| return service_id; |
| } |
| |
| } // namespace |
| |
| MdnsResponderService::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) |
| : service_type_{{service_name, service_protocol}}, |
| mdns_responder_factory_(std::move(mdns_responder_factory)), |
| platform_(std::move(platform)), |
| task_runner_(task_runner), |
| background_tasks_alarm_(now_function, task_runner) {} |
| |
| MdnsResponderService::~MdnsResponderService() = default; |
| |
| void MdnsResponderService::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) { |
| OSP_DCHECK(!hostname.empty()); |
| OSP_DCHECK(!instance.empty()); |
| OSP_DCHECK_NE(0, port); |
| service_hostname_ = hostname; |
| service_instance_name_ = instance; |
| service_port_ = port; |
| interface_index_allowlist_ = allowlist; |
| service_txt_data_ = txt_data; |
| } |
| |
| void MdnsResponderService::OnRead(UdpSocket* socket, |
| ErrorOr<UdpPacket> packet) { |
| TRACE_SCOPED(TraceCategory::kMdns, "MdnsResponderService::OnRead"); |
| if (!mdns_responder_) { |
| return; |
| } |
| |
| mdns_responder_->OnRead(socket, std::move(packet)); |
| HandleMdnsEvents(); |
| } |
| |
| void MdnsResponderService::OnSendError(UdpSocket* socket, Error error) { |
| mdns_responder_->OnSendError(socket, std::move(error)); |
| } |
| |
| void MdnsResponderService::OnError(UdpSocket* socket, Error error) { |
| mdns_responder_->OnError(socket, std::move(error)); |
| } |
| |
| void MdnsResponderService::StartListener() { |
| task_runner_->PostTask([this]() { this->StartListenerInternal(); }); |
| } |
| |
| void MdnsResponderService::StartAndSuspendListener() { |
| task_runner_->PostTask([this]() { this->StartAndSuspendListenerInternal(); }); |
| } |
| |
| void MdnsResponderService::StopListener() { |
| task_runner_->PostTask([this]() { this->StopListenerInternal(); }); |
| } |
| |
| void MdnsResponderService::SuspendListener() { |
| task_runner_->PostTask([this]() { this->SuspendListenerInternal(); }); |
| } |
| |
| void MdnsResponderService::ResumeListener() { |
| task_runner_->PostTask([this]() { this->ResumeListenerInternal(); }); |
| } |
| |
| void MdnsResponderService::SearchNow(ServiceListener::State from) { |
| task_runner_->PostTask([this, from]() { this->SearchNowInternal(from); }); |
| } |
| |
| void MdnsResponderService::StartPublisher() { |
| task_runner_->PostTask([this]() { this->StartPublisherInternal(); }); |
| } |
| |
| void MdnsResponderService::StartAndSuspendPublisher() { |
| task_runner_->PostTask( |
| [this]() { this->StartAndSuspendPublisherInternal(); }); |
| } |
| |
| void MdnsResponderService::StopPublisher() { |
| task_runner_->PostTask([this]() { this->StopPublisherInternal(); }); |
| } |
| |
| void MdnsResponderService::SuspendPublisher() { |
| task_runner_->PostTask([this]() { this->SuspendPublisherInternal(); }); |
| } |
| |
| void MdnsResponderService::ResumePublisher() { |
| task_runner_->PostTask([this]() { this->ResumePublisherInternal(); }); |
| } |
| |
| void MdnsResponderService::StartListenerInternal() { |
| if (!mdns_responder_) { |
| mdns_responder_ = mdns_responder_factory_->Create(); |
| } |
| |
| StartListening(); |
| ServiceListenerImpl::Delegate::SetState(ServiceListener::State::kRunning); |
| RunBackgroundTasks(); |
| } |
| |
| void MdnsResponderService::StartAndSuspendListenerInternal() { |
| mdns_responder_ = mdns_responder_factory_->Create(); |
| ServiceListenerImpl::Delegate::SetState(ServiceListener::State::kSuspended); |
| } |
| |
| void MdnsResponderService::StopListenerInternal() { |
| StopListening(); |
| if (!publisher_ || publisher_->state() == ServicePublisher::State::kStopped || |
| publisher_->state() == ServicePublisher::State::kSuspended) { |
| StopMdnsResponder(); |
| if (!publisher_ || publisher_->state() == ServicePublisher::State::kStopped) |
| mdns_responder_.reset(); |
| } |
| ServiceListenerImpl::Delegate::SetState(ServiceListener::State::kStopped); |
| } |
| |
| void MdnsResponderService::SuspendListenerInternal() { |
| StopMdnsResponder(); |
| ServiceListenerImpl::Delegate::SetState(ServiceListener::State::kSuspended); |
| } |
| |
| void MdnsResponderService::ResumeListenerInternal() { |
| StartListening(); |
| ServiceListenerImpl::Delegate::SetState(ServiceListener::State::kRunning); |
| } |
| |
| void MdnsResponderService::SearchNowInternal(ServiceListener::State from) { |
| ServiceListenerImpl::Delegate::SetState(from); |
| } |
| |
| void MdnsResponderService::StartPublisherInternal() { |
| if (!mdns_responder_) { |
| mdns_responder_ = mdns_responder_factory_->Create(); |
| } |
| |
| StartService(); |
| ServicePublisherImpl::Delegate::SetState(ServicePublisher::State::kRunning); |
| RunBackgroundTasks(); |
| } |
| |
| void MdnsResponderService::StartAndSuspendPublisherInternal() { |
| mdns_responder_ = mdns_responder_factory_->Create(); |
| ServicePublisherImpl::Delegate::SetState(ServicePublisher::State::kSuspended); |
| } |
| |
| void MdnsResponderService::StopPublisherInternal() { |
| StopService(); |
| if (!listener_ || listener_->state() == ServiceListener::State::kStopped || |
| listener_->state() == ServiceListener::State::kSuspended) { |
| StopMdnsResponder(); |
| if (!listener_ || listener_->state() == ServiceListener::State::kStopped) |
| mdns_responder_.reset(); |
| } |
| ServicePublisherImpl::Delegate::SetState(ServicePublisher::State::kStopped); |
| } |
| |
| void MdnsResponderService::SuspendPublisherInternal() { |
| StopService(); |
| ServicePublisherImpl::Delegate::SetState(ServicePublisher::State::kSuspended); |
| } |
| |
| void MdnsResponderService::ResumePublisherInternal() { |
| StartService(); |
| ServicePublisherImpl::Delegate::SetState(ServicePublisher::State::kRunning); |
| } |
| |
| bool MdnsResponderService::NetworkScopedDomainNameComparator::operator()( |
| const NetworkScopedDomainName& a, |
| const NetworkScopedDomainName& b) const { |
| if (a.socket != b.socket) { |
| return (a.socket - b.socket) < 0; |
| } |
| return DomainNameComparator()(a.domain_name, b.domain_name); |
| } |
| |
| void MdnsResponderService::HandleMdnsEvents() { |
| TRACE_SCOPED(TraceCategory::kMdns, "MdnsResponderService::HandleMdnsEvents"); |
| // NOTE: In the common case, we will get a single combined packet for |
| // PTR/SRV/TXT/A and then no other packets. If we don't loop here, we would |
| // start SRV/TXT queries based on the PTR response, but never check for events |
| // again. This should no longer be a problem when we have correct scheduling |
| // of RunTasks. |
| bool events_possible = false; |
| // NOTE: This set will track which service instances were changed by all the |
| // events throughout all the loop iterations. At the end, we can dispatch our |
| // ServiceInfo updates to |listener_| just once (e.g. instead of |
| // OnReceiverChanged, OnReceiverChanged, ..., just a single |
| // OnReceiverChanged). |
| InstanceNameSet modified_instance_names; |
| do { |
| events_possible = false; |
| for (auto& ptr_event : mdns_responder_->TakePtrResponses()) { |
| events_possible = HandlePtrEvent(ptr_event, &modified_instance_names) || |
| events_possible; |
| } |
| for (auto& srv_event : mdns_responder_->TakeSrvResponses()) { |
| events_possible = HandleSrvEvent(srv_event, &modified_instance_names) || |
| events_possible; |
| } |
| for (auto& txt_event : mdns_responder_->TakeTxtResponses()) { |
| events_possible = HandleTxtEvent(txt_event, &modified_instance_names) || |
| events_possible; |
| } |
| for (const auto& a_event : mdns_responder_->TakeAResponses()) { |
| events_possible = |
| HandleAEvent(a_event, &modified_instance_names) || events_possible; |
| } |
| for (const auto& aaaa_event : mdns_responder_->TakeAaaaResponses()) { |
| events_possible = HandleAaaaEvent(aaaa_event, &modified_instance_names) || |
| events_possible; |
| } |
| if (events_possible) { |
| // NOTE: This still needs to be called here, even though it runs in the |
| // background regularly, because we just finished processing MDNS events. |
| RunBackgroundTasks(); |
| } |
| } while (events_possible); |
| |
| for (const auto& instance_name : modified_instance_names) { |
| auto service_entry = service_by_name_.find(instance_name); |
| std::unique_ptr<ServiceInstance>& service = service_entry->second; |
| |
| std::string service_id = ServiceIdFromServiceInstanceName(instance_name); |
| auto receiver_info_entry = receiver_info_.find(service_id); |
| HostInfo* host = GetHostInfo(service->ptr_socket, service->domain_name); |
| if (!IsServiceReady(*service, host)) { |
| if (receiver_info_entry != receiver_info_.end()) { |
| const ServiceInfo& receiver_info = receiver_info_entry->second; |
| listener_->OnReceiverRemoved(receiver_info); |
| receiver_info_.erase(receiver_info_entry); |
| } |
| if (!service->has_ptr_record && !service->has_srv()) |
| service_by_name_.erase(service_entry); |
| continue; |
| } |
| |
| // TODO(btolsch): Verify UTF-8 here. |
| std::string friendly_name(instance_name.GetLabels()[0]); |
| |
| if (receiver_info_entry == receiver_info_.end()) { |
| ServiceInfo receiver_info{ |
| std::move(service_id), |
| std::move(friendly_name), |
| GetNetworkInterfaceIndexFromSocket(service->ptr_socket), |
| {host->v4_address, service->port}, |
| {host->v6_address, service->port}}; |
| listener_->OnReceiverAdded(receiver_info); |
| receiver_info_.emplace(receiver_info.service_id, |
| std::move(receiver_info)); |
| } else { |
| ServiceInfo& receiver_info = receiver_info_entry->second; |
| if (receiver_info.Update( |
| std::move(friendly_name), |
| GetNetworkInterfaceIndexFromSocket(service->ptr_socket), |
| {host->v4_address, service->port}, |
| {host->v6_address, service->port})) { |
| listener_->OnReceiverChanged(receiver_info); |
| } |
| } |
| } |
| } |
| |
| void MdnsResponderService::StartListening() { |
| // TODO(btolsch): This needs the same |interface_index_allowlist_| logic as |
| // StartService, but this can also wait until the network-change TODO is |
| // addressed. |
| if (bound_interfaces_.empty()) { |
| mdns_responder_->Init(); |
| bound_interfaces_ = platform_->RegisterInterfaces({}); |
| for (auto& interface : bound_interfaces_) { |
| mdns_responder_->RegisterInterface(interface.interface_info, |
| interface.subnet, interface.socket); |
| } |
| } |
| ErrorOr<DomainName> service_type = |
| DomainName::FromLabels(service_type_.begin(), service_type_.end()); |
| OSP_CHECK(service_type); |
| for (const auto& interface : bound_interfaces_) { |
| mdns_responder_->StartPtrQuery(interface.socket, service_type.value()); |
| } |
| } |
| |
| void MdnsResponderService::StopListening() { |
| ErrorOr<DomainName> service_type = |
| DomainName::FromLabels(service_type_.begin(), service_type_.end()); |
| OSP_CHECK(service_type); |
| for (const auto& kv : network_scoped_domain_to_host_) { |
| const NetworkScopedDomainName& scoped_domain = kv.first; |
| |
| mdns_responder_->StopAQuery(scoped_domain.socket, |
| scoped_domain.domain_name); |
| mdns_responder_->StopAaaaQuery(scoped_domain.socket, |
| scoped_domain.domain_name); |
| } |
| network_scoped_domain_to_host_.clear(); |
| for (const auto& service : service_by_name_) { |
| UdpSocket* const socket = service.second->ptr_socket; |
| mdns_responder_->StopSrvQuery(socket, service.first); |
| mdns_responder_->StopTxtQuery(socket, service.first); |
| } |
| service_by_name_.clear(); |
| for (const auto& interface : bound_interfaces_) { |
| mdns_responder_->StopPtrQuery(interface.socket, service_type.value()); |
| } |
| RemoveAllReceivers(); |
| } |
| |
| void MdnsResponderService::StartService() { |
| // TODO(crbug.com/openscreen/45): This should really be a library-wide |
| // allowed list. |
| if (!bound_interfaces_.empty() && !interface_index_allowlist_.empty()) { |
| // TODO(btolsch): New interfaces won't be picked up on this path, but this |
| // also highlights a larger issue of the interface list being frozen while |
| // no state transitions are being made. There should be another interface |
| // on MdnsPlatformService for getting network interface updates. |
| std::vector<MdnsPlatformService::BoundInterface> deregistered_interfaces; |
| for (auto it = bound_interfaces_.begin(); it != bound_interfaces_.end();) { |
| if (std::find(interface_index_allowlist_.begin(), |
| interface_index_allowlist_.end(), |
| it->interface_info.index) == |
| interface_index_allowlist_.end()) { |
| mdns_responder_->DeregisterInterface(it->socket); |
| deregistered_interfaces.push_back(*it); |
| it = bound_interfaces_.erase(it); |
| } else { |
| ++it; |
| } |
| } |
| platform_->DeregisterInterfaces(deregistered_interfaces); |
| } else if (bound_interfaces_.empty()) { |
| mdns_responder_->Init(); |
| mdns_responder_->SetHostLabel(service_hostname_); |
| bound_interfaces_ = |
| platform_->RegisterInterfaces(interface_index_allowlist_); |
| for (auto& interface : bound_interfaces_) { |
| mdns_responder_->RegisterInterface(interface.interface_info, |
| interface.subnet, interface.socket); |
| } |
| } |
| |
| ErrorOr<DomainName> domain_name = |
| DomainName::FromLabels(&service_hostname_, &service_hostname_ + 1); |
| OSP_CHECK(domain_name) << "bad hostname configured: " << service_hostname_; |
| DomainName name = std::move(domain_name.value()); |
| |
| Error error = name.Append(DomainName::GetLocalDomain()); |
| OSP_CHECK(error.ok()); |
| |
| mdns_responder_->RegisterService(service_instance_name_, service_type_[0], |
| service_type_[1], name, service_port_, |
| service_txt_data_); |
| } |
| |
| void MdnsResponderService::StopService() { |
| mdns_responder_->DeregisterService(service_instance_name_, service_type_[0], |
| service_type_[1]); |
| } |
| |
| void MdnsResponderService::StopMdnsResponder() { |
| mdns_responder_->Close(); |
| platform_->DeregisterInterfaces(bound_interfaces_); |
| bound_interfaces_.clear(); |
| network_scoped_domain_to_host_.clear(); |
| service_by_name_.clear(); |
| RemoveAllReceivers(); |
| } |
| |
| void MdnsResponderService::UpdatePendingServiceInfoSet( |
| InstanceNameSet* modified_instance_names, |
| const DomainName& domain_name) { |
| for (auto& entry : service_by_name_) { |
| const auto& instance_name = entry.first; |
| const auto& instance = entry.second; |
| if (instance->domain_name == domain_name) { |
| modified_instance_names->emplace(instance_name); |
| } |
| } |
| } |
| |
| void MdnsResponderService::RemoveAllReceivers() { |
| bool had_receivers = !receiver_info_.empty(); |
| receiver_info_.clear(); |
| if (had_receivers) |
| listener_->OnAllReceiversRemoved(); |
| } |
| |
| bool MdnsResponderService::HandlePtrEvent( |
| const PtrEvent& ptr_event, |
| InstanceNameSet* modified_instance_names) { |
| bool events_possible = false; |
| const auto& instance_name = ptr_event.service_instance; |
| UdpSocket* const socket = ptr_event.header.socket; |
| auto entry = service_by_name_.find(ptr_event.service_instance); |
| switch (ptr_event.header.response_type) { |
| case QueryEventHeader::Type::kAddedNoCache: |
| break; |
| case QueryEventHeader::Type::kAdded: { |
| if (entry != service_by_name_.end()) { |
| entry->second->has_ptr_record = true; |
| modified_instance_names->emplace(instance_name); |
| break; |
| } |
| mdns_responder_->StartSrvQuery(socket, instance_name); |
| mdns_responder_->StartTxtQuery(socket, instance_name); |
| events_possible = true; |
| |
| auto new_instance = std::make_unique<ServiceInstance>(); |
| new_instance->ptr_socket = socket; |
| new_instance->has_ptr_record = true; |
| modified_instance_names->emplace(instance_name); |
| service_by_name_.emplace(std::move(instance_name), |
| std::move(new_instance)); |
| } break; |
| case QueryEventHeader::Type::kRemoved: |
| if (entry == service_by_name_.end()) |
| break; |
| if (entry->second->ptr_socket != socket) |
| break; |
| entry->second->has_ptr_record = false; |
| // NOTE: Occasionally, we can observe this situation in the wild where the |
| // PTR for a service is removed and then immediately re-added (like an odd |
| // refresh). Additionally, the recommended TTL of PTR records is much |
| // shorter than the other records. This means that short network drops or |
| // latency spikes could cause the PTR refresh queries and/or responses to |
| // be lost so the record isn't quite refreshed in time. The solution here |
| // and in HandleSrvEvent is to only remove the service records completely |
| // when both the PTR and SRV have been removed. |
| if (!entry->second->has_srv()) { |
| mdns_responder_->StopSrvQuery(socket, instance_name); |
| mdns_responder_->StopTxtQuery(socket, instance_name); |
| } |
| modified_instance_names->emplace(std::move(instance_name)); |
| break; |
| } |
| return events_possible; |
| } |
| |
| bool MdnsResponderService::HandleSrvEvent( |
| const SrvEvent& srv_event, |
| InstanceNameSet* modified_instance_names) { |
| bool events_possible = false; |
| auto& domain_name = srv_event.domain_name; |
| const auto& instance_name = srv_event.service_instance; |
| UdpSocket* const socket = srv_event.header.socket; |
| auto entry = service_by_name_.find(srv_event.service_instance); |
| if (entry == service_by_name_.end()) |
| return events_possible; |
| switch (srv_event.header.response_type) { |
| case QueryEventHeader::Type::kAddedNoCache: |
| break; |
| case QueryEventHeader::Type::kAdded: { |
| NetworkScopedDomainName scoped_domain_name{socket, domain_name}; |
| auto host_entry = network_scoped_domain_to_host_.find(scoped_domain_name); |
| if (host_entry == network_scoped_domain_to_host_.end()) { |
| mdns_responder_->StartAQuery(socket, domain_name); |
| mdns_responder_->StartAaaaQuery(socket, domain_name); |
| events_possible = true; |
| auto result = network_scoped_domain_to_host_.emplace( |
| std::move(scoped_domain_name), HostInfo{}); |
| host_entry = result.first; |
| } |
| auto& dependent_services = host_entry->second.services; |
| if (std::find_if(dependent_services.begin(), dependent_services.end(), |
| [entry](ServiceInstance* instance) { |
| return instance == entry->second.get(); |
| }) == dependent_services.end()) { |
| dependent_services.push_back(entry->second.get()); |
| } |
| entry->second->domain_name = std::move(domain_name); |
| entry->second->port = srv_event.port; |
| modified_instance_names->emplace(std::move(instance_name)); |
| } break; |
| case QueryEventHeader::Type::kRemoved: { |
| NetworkScopedDomainName scoped_domain_name{socket, domain_name}; |
| auto host_entry = network_scoped_domain_to_host_.find(scoped_domain_name); |
| if (host_entry != network_scoped_domain_to_host_.end()) { |
| auto& dependent_services = host_entry->second.services; |
| dependent_services.erase( |
| std::remove_if(dependent_services.begin(), dependent_services.end(), |
| [entry](ServiceInstance* instance) { |
| return instance == entry->second.get(); |
| }), |
| dependent_services.end()); |
| if (dependent_services.empty()) { |
| mdns_responder_->StopAQuery(socket, domain_name); |
| mdns_responder_->StopAaaaQuery(socket, domain_name); |
| network_scoped_domain_to_host_.erase(host_entry); |
| } |
| } |
| entry->second->domain_name = DomainName(); |
| entry->second->port = 0; |
| if (!entry->second->has_ptr_record) { |
| mdns_responder_->StopSrvQuery(socket, instance_name); |
| mdns_responder_->StopTxtQuery(socket, instance_name); |
| } |
| modified_instance_names->emplace(std::move(instance_name)); |
| } break; |
| } |
| return events_possible; |
| } |
| |
| bool MdnsResponderService::HandleTxtEvent( |
| const TxtEvent& txt_event, |
| InstanceNameSet* modified_instance_names) { |
| bool events_possible = false; |
| const auto& instance_name = txt_event.service_instance; |
| auto entry = service_by_name_.find(instance_name); |
| if (entry == service_by_name_.end()) |
| return events_possible; |
| switch (txt_event.header.response_type) { |
| case QueryEventHeader::Type::kAddedNoCache: |
| break; |
| case QueryEventHeader::Type::kAdded: |
| modified_instance_names->emplace(instance_name); |
| if (entry == service_by_name_.end()) { |
| auto result = service_by_name_.emplace( |
| std::move(instance_name), std::make_unique<ServiceInstance>()); |
| entry = result.first; |
| } |
| entry->second->txt_info = std::move(txt_event.txt_info); |
| break; |
| case QueryEventHeader::Type::kRemoved: |
| entry->second->txt_info.clear(); |
| modified_instance_names->emplace(std::move(instance_name)); |
| break; |
| } |
| return events_possible; |
| } |
| |
| bool MdnsResponderService::HandleAddressEvent( |
| UdpSocket* socket, |
| QueryEventHeader::Type response_type, |
| const DomainName& domain_name, |
| bool a_event, |
| const IPAddress& address, |
| InstanceNameSet* modified_instance_names) { |
| bool events_possible = false; |
| switch (response_type) { |
| case QueryEventHeader::Type::kAddedNoCache: |
| break; |
| case QueryEventHeader::Type::kAdded: { |
| HostInfo* host = AddOrGetHostInfo(socket, domain_name); |
| if (a_event) |
| host->v4_address = address; |
| else |
| host->v6_address = address; |
| UpdatePendingServiceInfoSet(modified_instance_names, domain_name); |
| } break; |
| case QueryEventHeader::Type::kRemoved: { |
| HostInfo* host = GetHostInfo(socket, domain_name); |
| |
| if (a_event) |
| host->v4_address = IPAddress(); |
| else |
| host->v6_address = IPAddress(); |
| |
| if (host->v4_address || host->v6_address) |
| UpdatePendingServiceInfoSet(modified_instance_names, domain_name); |
| } break; |
| } |
| return events_possible; |
| } |
| |
| bool MdnsResponderService::HandleAEvent( |
| const AEvent& a_event, |
| InstanceNameSet* modified_instance_names) { |
| return HandleAddressEvent(a_event.header.socket, a_event.header.response_type, |
| a_event.domain_name, true, a_event.address, |
| modified_instance_names); |
| } |
| |
| bool MdnsResponderService::HandleAaaaEvent( |
| const AaaaEvent& aaaa_event, |
| InstanceNameSet* modified_instance_names) { |
| return HandleAddressEvent(aaaa_event.header.socket, |
| aaaa_event.header.response_type, |
| aaaa_event.domain_name, false, aaaa_event.address, |
| modified_instance_names); |
| } |
| |
| MdnsResponderService::HostInfo* MdnsResponderService::AddOrGetHostInfo( |
| UdpSocket* socket, |
| const DomainName& domain_name) { |
| return &network_scoped_domain_to_host_[NetworkScopedDomainName{socket, |
| domain_name}]; |
| } |
| |
| MdnsResponderService::HostInfo* MdnsResponderService::GetHostInfo( |
| UdpSocket* socket, |
| const DomainName& domain_name) { |
| auto kv = network_scoped_domain_to_host_.find( |
| NetworkScopedDomainName{socket, domain_name}); |
| if (kv == network_scoped_domain_to_host_.end()) |
| return nullptr; |
| |
| return &kv->second; |
| } |
| |
| bool MdnsResponderService::IsServiceReady(const ServiceInstance& instance, |
| HostInfo* host) const { |
| return (host && instance.has_ptr_record && instance.has_srv() && |
| !instance.txt_info.empty() && (host->v4_address || host->v6_address)); |
| } |
| |
| NetworkInterfaceIndex MdnsResponderService::GetNetworkInterfaceIndexFromSocket( |
| const UdpSocket* socket) const { |
| auto it = std::find_if( |
| bound_interfaces_.begin(), bound_interfaces_.end(), |
| [socket](const MdnsPlatformService::BoundInterface& interface) { |
| return interface.socket == socket; |
| }); |
| if (it == bound_interfaces_.end()) |
| return kInvalidNetworkInterfaceIndex; |
| return it->interface_info.index; |
| } |
| |
| void MdnsResponderService::RunBackgroundTasks() { |
| if (!mdns_responder_) { |
| return; |
| } |
| const auto delay_until_next_run = mdns_responder_->RunTasks(); |
| background_tasks_alarm_.ScheduleFromNow([this] { RunBackgroundTasks(); }, |
| delay_until_next_run); |
| } |
| |
| } // namespace osp |
| } // namespace openscreen |