You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
665 lines
25 KiB
665 lines
25 KiB
// 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
|