blob: ddc08b685ed622044e71849d89c7b6bbd327bc8f [file] [log] [blame]
// Copyright 2017 The Fuchsia 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 "src/connectivity/network/mdns/service/mdns.h"
#include <lib/async/cpp/task.h>
#include <lib/async/default.h>
#include <lib/syslog/cpp/macros.h>
#include <lib/zx/clock.h>
#include <lib/zx/time.h>
#include <iostream>
#include <limits>
#include <unordered_set>
#include "src/connectivity/network/mdns/service/agents/address_prober.h"
#include "src/connectivity/network/mdns/service/agents/address_responder.h"
#include "src/connectivity/network/mdns/service/agents/host_name_requestor.h"
#include "src/connectivity/network/mdns/service/agents/host_name_resolver.h"
#include "src/connectivity/network/mdns/service/agents/instance_prober.h"
#include "src/connectivity/network/mdns/service/agents/instance_requestor.h"
#include "src/connectivity/network/mdns/service/agents/instance_responder.h"
#include "src/connectivity/network/mdns/service/agents/resource_renewer.h"
#include "src/connectivity/network/mdns/service/agents/service_instance_resolver.h"
#include "src/connectivity/network/mdns/service/common/formatters.h"
#include "src/connectivity/network/mdns/service/common/mdns_addresses.h"
#include "src/connectivity/network/mdns/service/common/mdns_names.h"
#include "src/connectivity/network/mdns/service/encoding/dns_formatting.h"
namespace mdns {
Mdns::Mdns(Transceiver& transceiver)
: dispatcher_(async_get_default_dispatcher()), transceiver_(transceiver) {}
Mdns::~Mdns() {}
void Mdns::SetVerbose(bool verbose) {
#ifdef MDNS_TRACE
verbose_ = verbose;
#endif // MDNS_TRACE
}
void Mdns::Start(fuchsia::net::interfaces::WatcherPtr interfaces_watcher,
const std::string& local_host_name, bool perform_address_probe,
fit::closure ready_callback, std::vector<std::string> alt_services) {
FX_DCHECK(!local_host_name.empty());
FX_DCHECK(ready_callback);
FX_DCHECK(state_ == State::kNotStarted);
ready_callback_ = std::move(ready_callback);
state_ = State::kWaitingForInterfaces;
original_local_host_name_ = local_host_name;
alt_services_ = std::move(alt_services);
// Create a resource renewer agent to keep resources alive.
resource_renewer_ = std::make_shared<ResourceRenewer>(this);
// Create an address responder agent to respond to address queries.
AddAgent(std::make_shared<AddressResponder>(this, Media::kBoth, IpVersions::kBoth));
// If alternate services are registered, create an address responder agent to respond to address
// queries for the alternate host name. No probe is performed.
// TODO(https://fxbug.dev/42065146): Remove this when alt_services is no longer needed.
if (!alt_services_.empty()) {
auto alt_host_name = MdnsNames::AltHostName(local_host_name);
if (alt_host_name == local_host_name) {
FX_LOGS(ERROR) << "Unexpected host name format, cannot generate alternate host name.";
}
AddAgent(std::make_shared<AddressResponder>(this, MdnsNames::HostFullName(alt_host_name),
std::vector<inet::IpAddress>{}, Media::kBoth,
IpVersions::kBoth));
}
transceiver_.Start(
std::move(interfaces_watcher),
[this, perform_address_probe]() {
// TODO(dalesat): Link changes that create host name conflicts.
// Once we have a NIC and we've decided on a unique host name, we
// don't do any more address probes. This means that we could have link
// changes that cause two hosts with the same name to be on the same
// subnet. To improve matters, we need to be prepared to change a host
// name we've been using for a while.
if (state_ == State::kWaitingForInterfaces && transceiver_.HasInterfaces()) {
OnInterfacesStarted(original_local_host_name_, perform_address_probe);
}
},
[this](std::unique_ptr<DnsMessage> message, const ReplyAddress& reply_address) {
#ifdef MDNS_TRACE
if (verbose_) {
FX_LOGS(INFO) << "Inbound message from " << reply_address << ":" << *message;
}
#endif // MDNS_TRACE
// We'll send messages when we're done processing this inbound message, so don't respond
// to |FlushSentItems| in the interim.
defer_flush_ = true;
for (const auto& question : message->questions_) {
// We reply to questions using unicast if specifically requested in
// the question or if the sender's port isn't 5353.
ReceiveQuestion(*question,
(question->unicast_response_ ||
reply_address.socket_address().port() != MdnsAddresses::port())
? reply_address
: ReplyAddress::Multicast(Media::kBoth, IpVersions::kBoth),
reply_address);
}
for (const auto& resource : message->answers_) {
ReceiveResource(*resource, MdnsResourceSection::kAnswer, reply_address);
}
for (const auto& resource : message->authorities_) {
ReceiveResource(*resource, MdnsResourceSection::kAuthority, reply_address);
}
for (const auto& resource : message->additionals_) {
ReceiveResource(*resource, MdnsResourceSection::kAdditional, reply_address);
}
resource_renewer_->EndOfMessage();
DPROHIBIT_AGENT_REMOVAL();
for (const auto& agent : agents_) {
agent->EndOfMessage();
}
DALLOW_AGENT_REMOVAL();
defer_flush_ = false;
SendMessages();
},
MdnsInterfaceTransceiver::Create);
// The interface monitor may have already found interfaces. In that case,
// start the address probe in case we don't get any link change notifications.
if (state_ == State::kWaitingForInterfaces && transceiver_.HasInterfaces()) {
OnInterfacesStarted(original_local_host_name_, perform_address_probe);
}
}
void Mdns::Stop() {
transceiver_.Stop();
ready_callback_ = nullptr;
state_ = State::kNotStarted;
}
void Mdns::ResolveHostName(const std::string& host_name, zx::duration timeout, Media media,
IpVersions ip_versions, bool include_local, bool include_local_proxies,
ResolveHostNameCallback callback) {
FX_DCHECK(MdnsNames::IsValidHostName(host_name));
FX_DCHECK(callback);
FX_DCHECK(state_ == State::kActive);
auto agent =
std::make_shared<HostNameResolver>(this, host_name, media, ip_versions, include_local,
include_local_proxies, timeout, std::move(callback));
AddAgent(agent);
}
void Mdns::SubscribeToHostName(const std::string& host_name, Media media, IpVersions ip_versions,
bool include_local, bool include_local_proxies,
HostNameSubscriber* subscriber) {
FX_DCHECK(MdnsNames::IsValidHostName(host_name));
FX_DCHECK(subscriber);
FX_DCHECK(state_ == State::kActive);
std::shared_ptr<HostNameRequestor> agent;
RequestorKey key(host_name, media, ip_versions);
auto iter = host_name_requestors_by_key_.find(key);
if (iter == host_name_requestors_by_key_.end()) {
agent = std::make_shared<HostNameRequestor>(this, host_name, media, ip_versions, include_local,
include_local_proxies);
host_name_requestors_by_key_.emplace(key, agent);
agent->SetOnQuitCallback([this, key]() { host_name_requestors_by_key_.erase(key); });
subscriber->Connect(agent);
// Add the subscriber before calling AddAgent (which starts the agent).
agent->AddSubscriber(subscriber);
AddAgent(agent);
} else {
agent = iter->second;
subscriber->Connect(agent);
agent->AddSubscriber(subscriber);
}
}
void Mdns::ResolveServiceInstance(const std::string& service, const std::string& instance,
zx::time timeout, Media media, IpVersions ip_versions,
bool include_local, bool include_local_proxies,
ResolveServiceInstanceCallback callback) {
FX_DCHECK(!service.empty());
FX_DCHECK(!instance.empty());
FX_DCHECK(callback);
FX_DCHECK(state_ == State::kActive);
AddAgent(std::make_shared<ServiceInstanceResolver>(this, service, instance, timeout, media,
ip_versions, include_local,
include_local_proxies, std::move(callback)));
}
void Mdns::SubscribeToService(const std::string& service_name, Media media, IpVersions ip_versions,
bool include_local, bool include_local_proxies,
Subscriber* subscriber) {
FX_DCHECK(MdnsNames::IsValidServiceName(service_name));
FX_DCHECK(subscriber);
FX_DCHECK(state_ == State::kActive);
std::shared_ptr<InstanceRequestor> agent;
RequestorKey key(service_name, media, ip_versions);
auto iter = instance_requestors_by_key_.find(key);
if (iter == instance_requestors_by_key_.end()) {
agent = std::make_shared<InstanceRequestor>(this, service_name, media, ip_versions,
include_local, include_local_proxies);
instance_requestors_by_key_.emplace(key, agent);
agent->SetOnQuitCallback([this, key]() { instance_requestors_by_key_.erase(key); });
subscriber->Connect(agent);
// Add the subscriber before calling AddAgent (which starts the agent), so the subscriber will
// be notified of the first query.
agent->AddSubscriber(subscriber);
AddAgent(agent);
} else {
agent = iter->second;
subscriber->Connect(agent);
agent->AddSubscriber(subscriber);
}
}
void Mdns::SubscribeToAllServices(Media media, IpVersions ip_versions, bool include_local,
bool include_local_proxies, Subscriber* subscriber) {
FX_DCHECK(subscriber);
FX_DCHECK(state_ == State::kActive);
std::shared_ptr<InstanceRequestor> agent;
RequestorKey key("", media, ip_versions);
auto iter = instance_requestors_by_key_.find(key);
if (iter == instance_requestors_by_key_.end()) {
agent = std::make_shared<InstanceRequestor>(this, media, ip_versions, include_local,
include_local_proxies);
instance_requestors_by_key_.emplace(key, agent);
agent->SetOnQuitCallback([this, key]() { instance_requestors_by_key_.erase(key); });
subscriber->Connect(agent);
// Add the subscriber before calling AddAgent (which starts the agent), so the subscriber will
// be notified of the first query.
agent->AddSubscriber(subscriber);
AddAgent(agent);
} else {
agent = iter->second;
subscriber->Connect(agent);
agent->AddSubscriber(subscriber);
}
}
bool Mdns::PublishServiceInstance(std::string host_name, std::vector<inet::IpAddress> addresses,
std::string service_name, std::string instance_name, Media media,
IpVersions ip_versions, bool perform_probe,
Publisher* publisher) {
FX_DCHECK(host_name.empty() == addresses.empty());
FX_DCHECK(host_name.empty() || MdnsNames::IsValidHostName(host_name));
FX_DCHECK(MdnsNames::IsValidServiceName(service_name));
FX_DCHECK(MdnsNames::IsValidInstanceName(instance_name));
FX_DCHECK(publisher);
FX_DCHECK(state_ == State::kActive);
std::string instance_full_name = MdnsNames::InstanceFullName(instance_name, service_name);
if (instance_responders_by_instance_full_name_.find(instance_full_name) !=
instance_responders_by_instance_full_name_.end()) {
return false;
}
// If a host name was provided, the instance is to be published by a proxy host.
bool from_proxy = !host_name.empty();
// If we're not publishing from a proxy host, and the service type is in the list of alternate
// services, publish from the alternate host name.
if (!from_proxy &&
std::find(alt_services_.begin(), alt_services_.end(), service_name) != alt_services_.end()) {
// TODO(https://fxbug.dev/42065146): Remove this when alt_services is no longer needed.
FX_LOGS(INFO) << "Alternate services specified, responding on alternate host name.";
host_name = MdnsNames::AltHostName(original_local_host_name_);
}
auto agent = std::make_shared<InstanceResponder>(this, host_name, addresses, service_name,
instance_name, media, ip_versions, publisher);
instance_responders_by_instance_full_name_.emplace(instance_full_name, agent);
agent->SetOnQuitCallback([this, instance_full_name, service_name, instance_name, from_proxy]() {
instance_responders_by_instance_full_name_.erase(instance_full_name);
OnRemoveLocalServiceInstance(service_name, instance_name, from_proxy);
});
publisher->Connect(agent);
if (perform_probe) {
// We're using a bogus port number here, which is OK, because the 'proposed'
// resource created from it is only used for collision resolution.
auto prober = std::make_shared<InstanceProber>(
this, service_name, instance_name,
host_name.empty() ? local_host_full_name_ : MdnsNames::HostFullName(host_name),
inet::IpPort::From_uint16_t(0), media, ip_versions,
[this, instance_full_name, agent, publisher, from_proxy](bool successful) {
publisher->DisconnectProber();
if (!successful) {
publisher->ReportSuccess(false);
instance_responders_by_instance_full_name_.erase(instance_full_name);
return;
}
publisher->ReportSuccess(true);
AddAgent(agent);
if (state_ == State::kActive) {
auto service_instance = agent->service_instance();
if (service_instance) {
OnAddLocalServiceInstance(*service_instance, from_proxy);
}
}
});
AddAgent(prober);
publisher->ConnectProber(prober);
} else {
publisher->ReportSuccess(true);
AddAgent(agent);
}
return true;
}
bool Mdns::PublishHost(std::string host_name, std::vector<inet::IpAddress> addresses, Media media,
IpVersions ip_versions, bool perform_probe, HostPublisher* publisher) {
FX_DCHECK(MdnsNames::IsValidHostName(host_name));
FX_DCHECK(!addresses.empty());
FX_DCHECK(publisher);
FX_DCHECK(state_ == State::kActive);
std::string host_full_name = MdnsNames::HostFullName(std::move(host_name));
if (host_full_name == local_host_full_name_) {
// Publication of the local host doesn't use this method (for now), so we check separately
// that the supplied |host_name| doesn't conflict with the local host's name.
return false;
}
if (address_responders_by_host_full_name_.find(host_full_name) !=
address_responders_by_host_full_name_.end()) {
return false;
}
auto agent =
std::make_shared<AddressResponder>(this, host_full_name, addresses, media, ip_versions);
address_responders_by_host_full_name_.emplace(host_full_name, agent);
agent->SetOnQuitCallback([this, host_full_name]() {
address_responders_by_host_full_name_.erase(host_full_name);
OnRemoveProxyHost(host_full_name);
});
publisher->Connect(agent);
if (perform_probe) {
auto prober = std::make_shared<AddressProber>(
this, host_full_name, addresses, media, ip_versions,
[this, host_full_name, agent, publisher](bool successful) {
publisher->DisconnectProber();
if (!successful) {
publisher->ReportSuccess(false);
address_responders_by_host_full_name_.erase(host_full_name);
return;
}
publisher->ReportSuccess(true);
AddAgent(agent);
if (state_ == State::kActive) {
OnAddProxyHost(host_full_name, agent->addresses());
}
});
AddAgent(prober);
publisher->ConnectProber(prober);
} else {
publisher->ReportSuccess(true);
AddAgent(agent);
}
return true;
}
void Mdns::LogTraffic() { transceiver_.LogTraffic(); }
void Mdns::OnInterfacesStarted(const std::string& local_host_name, bool perform_address_probe) {
if (perform_address_probe) {
StartAddressProbe(local_host_name);
return;
}
RegisterLocalHostName(local_host_name);
OnReady();
}
void Mdns::StartAddressProbe(const std::string& local_host_name) {
state_ = State::kAddressProbeInProgress;
RegisterLocalHostName(local_host_name);
std::cout << "mDNS: Verifying uniqueness of host name " << local_host_full_name_ << "\n";
// Create an address prober to look for host name conflicts. The address
// prober removes itself immediately before it calls the callback.
auto address_prober = std::make_shared<AddressProber>(
this, Media::kBoth, IpVersions::kBoth, [this](bool successful) {
FX_DCHECK(agents_.empty());
if (!successful) {
std::cout << "mDNS: Another host is using name " << local_host_full_name_ << "\n";
OnHostNameConflict();
return;
}
OnReady();
});
// We don't use |AddAgent| here, because agents added that way don't
// actually participate until we're done probing for host name conflicts.
agents_.emplace(address_prober);
address_prober->Start(local_host_full_name_);
SendMessages();
}
void Mdns::RegisterLocalHostName(const std::string& local_host_name) {
local_host_name_ = local_host_name;
local_host_full_name_ = MdnsNames::HostFullName(local_host_name);
address_placeholder_ = std::make_shared<DnsResource>(local_host_full_name_, DnsType::kA);
}
void Mdns::OnReady() {
std::cout << "mDNS: Using unique host name " << local_host_full_name_ << "\n";
// Start all the agents.
state_ = State::kActive;
// |resource_renewer_| doesn't need to be started, but we do it
// anyway in case that changes.
resource_renewer_->Start(local_host_full_name_);
for (const auto& agent : agents_awaiting_start_) {
AddAgent(agent);
}
agents_awaiting_start_.clear();
// Let the client know we're ready.
FX_DCHECK(ready_callback_);
ready_callback_();
ready_callback_ = nullptr;
}
void Mdns::OnAddProxyHost(const std::string& host_full_name,
const std::vector<HostAddress>& addresses) {
for (const auto& agent : agents_) {
agent->OnAddProxyHost(host_full_name, addresses);
}
}
void Mdns::OnRemoveProxyHost(const std::string& host_full_name) {
for (const auto& agent : agents_) {
agent->OnRemoveProxyHost(host_full_name);
}
}
void Mdns::OnAddLocalServiceInstance(const ServiceInstance& service_instance, bool from_proxy) {
for (const auto& agent : agents_) {
agent->OnAddLocalServiceInstance(service_instance, from_proxy);
}
}
void Mdns::OnChangeLocalServiceInstance(const ServiceInstance& service_instance, bool from_proxy) {
for (const auto& agent : agents_) {
agent->OnChangeLocalServiceInstance(service_instance, from_proxy);
}
}
void Mdns::OnRemoveLocalServiceInstance(const std::string& service_name,
const std::string& instance_name, bool from_proxy) {
for (const auto& agent : agents_) {
agent->OnRemoveLocalServiceInstance(service_name, instance_name, from_proxy);
}
}
void Mdns::OnHostNameConflict() {
// TODO(dalesat): Support other renaming strategies?
std::ostringstream os;
os << original_local_host_name_ << next_local_host_name_deduplicator_;
++next_local_host_name_deduplicator_;
StartAddressProbe(os.str());
}
zx::time Mdns::now() { return zx::clock::get_monotonic(); }
void Mdns::PostTaskForTime(MdnsAgent* agent, fit::closure task, zx::time target_time) {
task_queue_.emplace(agent, std::move(task), target_time);
PostTask();
}
void Mdns::SendQuestion(std::shared_ptr<DnsQuestion> question, ReplyAddress reply_address) {
FX_DCHECK(question);
outbound_message_builders_by_reply_address_[reply_address].AddQuestion(question);
}
void Mdns::SendResource(std::shared_ptr<DnsResource> resource, MdnsResourceSection section,
const ReplyAddress& reply_address) {
FX_DCHECK(resource);
// |reply_address| should never be the V6 multicast address, because the V4 multicast address
// is used to indicate a multicast reply.
FX_DCHECK(reply_address.socket_address() != MdnsAddresses::v6_multicast());
if (section == MdnsResourceSection::kExpired) {
// Expirations are distributed to local agents. We handle this case
// separately so we don't create an empty outbound message.
prohibit_agent_removal_ = true;
for (const auto& agent : agents_) {
agent->ReceiveResource(*resource, MdnsResourceSection::kExpired, ReplyAddress());
}
prohibit_agent_removal_ = false;
return;
}
outbound_message_builders_by_reply_address_[reply_address].AddResource(resource, section);
}
void Mdns::SendAddresses(MdnsResourceSection section, const ReplyAddress& reply_address) {
SendResource(address_placeholder_, section, reply_address);
}
void Mdns::Renew(const DnsResource& resource, Media media, IpVersions ip_versions) {
resource_renewer_->Renew(resource, media, ip_versions);
}
void Mdns::Query(DnsType type, const std::string& name, Media media, IpVersions ip_versions,
zx::time initial_query_time, zx::duration interval, uint32_t interval_multiplier,
uint32_t max_queries, bool request_unicast_response) {
resource_renewer_->Query(type, name, media, ip_versions, initial_query_time, interval,
interval_multiplier, max_queries, request_unicast_response);
}
void Mdns::RemoveAgent(std::shared_ptr<MdnsAgent> agent) {
FX_DCHECK(agent);
FX_DCHECK(!prohibit_agent_removal_);
agents_.erase(agent);
// Remove all pending tasks posted by this agent.
std::priority_queue<TaskQueueEntry> temp;
task_queue_.swap(temp);
while (!temp.empty()) {
if (temp.top().agent_ != agent.get()) {
task_queue_.emplace(temp.top().agent_, std::move(temp.top().task_), temp.top().time_);
}
temp.pop();
}
// In case the agent sent an epitaph.
SendMessages();
}
void Mdns::FlushSentItems() {
if (defer_flush_) {
// |SendMessages| will be called soon, so we don't want to call it now. This allows agents
// to call |FlushSentItems| synchronous with inbound message processing and posted task
// execution without unnecessarily fragmenting outgoing messages.
return;
}
SendMessages();
}
void Mdns::AddLocalServiceInstance(const ServiceInstance& instance, bool from_proxy) {
OnAddLocalServiceInstance(instance, from_proxy);
}
void Mdns::ChangeLocalServiceInstance(const ServiceInstance& instance, bool from_proxy) {
OnChangeLocalServiceInstance(instance, from_proxy);
}
std::vector<HostAddress> Mdns::LocalHostAddresses() { return transceiver_.LocalHostAddresses(); }
void Mdns::AddAgent(std::shared_ptr<MdnsAgent> agent) {
if (state_ == State::kActive) {
agents_.emplace(agent);
FX_DCHECK(!local_host_full_name_.empty());
agent->Start(local_host_full_name_);
// Notify the agent of all current local proxies.
for (auto& pair : address_responders_by_host_full_name_) {
agent->OnAddProxyHost(pair.first, pair.second->addresses());
}
// Notify the agent of all current local instances.
for (auto& pair : instance_responders_by_instance_full_name_) {
auto service_instance = pair.second->service_instance();
if (service_instance) {
auto from_proxy = pair.second->from_proxy();
agent->OnAddLocalServiceInstance(*service_instance, from_proxy);
}
}
SendMessages();
} else {
agents_awaiting_start_.push_back(agent);
}
}
void Mdns::SendMessages() {
for (const auto& [reply_address, builder] : outbound_message_builders_by_reply_address_) {
DnsMessage message;
builder.Build(message);
#ifdef MDNS_TRACE
if (verbose_) {
std::ostringstream os;
if (reply_address.is_multicast_placeholder()) {
os << "(multicast)";
} else {
os << "to " << reply_address;
}
switch (reply_address.media()) {
case Media::kWired:
os << ", wired only";
break;
case Media::kWireless:
os << ", wireless only";
break;
case Media::kBoth:
break;
}
switch (reply_address.ip_versions()) {
case IpVersions::kV4:
os << ", V4 only";
break;
case IpVersions::kV6:
os << ", V6 only";
break;
case IpVersions::kBoth:
break;
}
FX_LOGS(INFO) << "Outbound message " << os.str() << ":" << message;
}
#endif // MDNS_TRACE
transceiver_.SendMessage(std::move(message), reply_address);
}
outbound_message_builders_by_reply_address_.clear();
}
void Mdns::ReceiveQuestion(const DnsQuestion& question, const ReplyAddress& reply_address,
const ReplyAddress& sender_address) {
// |reply_address| should never be the V6 multicast address, because the V4 multicast address
// is used to indicate a multicast reply.
FX_DCHECK(reply_address.socket_address() != MdnsAddresses::v6_multicast());
// Renewer doesn't need questions.
DPROHIBIT_AGENT_REMOVAL();
for (const auto& agent : agents_) {
agent->ReceiveQuestion(question, reply_address, sender_address);
}
DALLOW_AGENT_REMOVAL();
}
void Mdns::ReceiveResource(const DnsResource& resource, MdnsResourceSection section,
ReplyAddress sender_address) {
// Renewer is always first.
resource_renewer_->ReceiveResource(resource, section, sender_address);
DPROHIBIT_AGENT_REMOVAL();
for (const auto& agent : agents_) {
agent->ReceiveResource(resource, section, sender_address);
}
DALLOW_AGENT_REMOVAL();
}
void Mdns::PostTask() {
FX_DCHECK(!task_queue_.empty());
if (task_queue_.top().time_ >= posted_task_time_) {
return;
}
posted_task_time_ = task_queue_.top().time_;
async::PostTaskForTime(
dispatcher_,
[this]() {
// Suppress recursive calls to this method.
posted_task_time_ = zx::time::infinite_past();
zx::time now = this->now();
// We'll send messages when we're done running ready tasks, so don't respond to
// |FlushSentItems| in the interim.
defer_flush_ = true;
while (!task_queue_.empty() && task_queue_.top().time_ <= now) {
fit::closure task = std::move(task_queue_.top().task_);
task_queue_.pop();
task();
}
defer_flush_ = false;
SendMessages();
posted_task_time_ = zx::time::infinite();
if (!task_queue_.empty()) {
PostTask();
}
},
zx::time(posted_task_time_));
}
///////////////////////////////////////////////////////////////////////////////
// static
std::unique_ptr<Mdns::Publication> Mdns::Publication::Create(
inet::IpPort port, const std::vector<std::vector<uint8_t>>& text, uint16_t srv_priority,
uint16_t srv_weight) {
auto publication = std::make_unique<Publication>();
publication->port_ = port;
publication->text_ = text;
publication->srv_priority_ = srv_priority;
publication->srv_weight_ = srv_weight;
return publication;
}
std::unique_ptr<Mdns::Publication> Mdns::Publication::Clone() const {
auto result = Create(port_, text_, srv_priority_, srv_weight_);
result->ptr_ttl_seconds_ = ptr_ttl_seconds_;
result->srv_ttl_seconds_ = srv_ttl_seconds_;
result->txt_ttl_seconds_ = txt_ttl_seconds_;
return result;
}
///////////////////////////////////////////////////////////////////////////////
Mdns::HostNameSubscriber::~HostNameSubscriber() { Unsubscribe(); }
void Mdns::HostNameSubscriber::Connect(std::shared_ptr<HostNameRequestor> host_name_requestor) {
FX_DCHECK(host_name_requestor);
host_name_requestor_ = host_name_requestor;
}
void Mdns::HostNameSubscriber::Unsubscribe() {
if (host_name_requestor_) {
host_name_requestor_->RemoveSubscriber(this);
host_name_requestor_ = nullptr;
}
}
///////////////////////////////////////////////////////////////////////////////
Mdns::Subscriber::~Subscriber() { Unsubscribe(); }
void Mdns::Subscriber::Connect(std::shared_ptr<InstanceRequestor> instance_requestor) {
FX_DCHECK(instance_requestor);
instance_requestor_ = instance_requestor;
}
void Mdns::Subscriber::Unsubscribe() {
if (instance_requestor_) {
instance_requestor_->RemoveSubscriber(this);
instance_requestor_ = nullptr;
}
}
///////////////////////////////////////////////////////////////////////////////
Mdns::Publisher::~Publisher() { Unpublish(); }
void Mdns::Publisher::SetSubtypes(std::vector<std::string> subtypes) {
if (instance_responder_) {
instance_responder_->SetSubtypes(std::move(subtypes));
}
}
void Mdns::Publisher::Reannounce() {
if (instance_responder_) {
instance_responder_->Reannounce();
}
}
void Mdns::Publisher::Unpublish() {
if (instance_prober_) {
instance_prober_->Quit();
instance_prober_ = nullptr;
}
if (instance_responder_) {
instance_responder_->Quit();
instance_responder_ = nullptr;
}
}
void Mdns::Publisher::Connect(std::shared_ptr<InstanceResponder> instance_responder) {
FX_DCHECK(instance_responder);
instance_responder_ = instance_responder;
}
void Mdns::Publisher::ConnectProber(std::shared_ptr<InstanceProber> instance_prober) {
FX_DCHECK(instance_prober);
instance_prober_ = instance_prober;
}
void Mdns::Publisher::DisconnectProber() {
FX_DCHECK(instance_prober_);
instance_prober_ = nullptr;
}
///////////////////////////////////////////////////////////////////////////////
Mdns::HostPublisher::~HostPublisher() { Unpublish(); }
void Mdns::HostPublisher::Unpublish() {
if (address_prober_) {
address_prober_->Quit();
address_prober_ = nullptr;
}
if (address_responder_) {
address_responder_->Quit();
address_responder_ = nullptr;
}
}
void Mdns::HostPublisher::Connect(std::shared_ptr<AddressResponder> address_responder) {
FX_DCHECK(address_responder);
address_responder_ = address_responder;
}
void Mdns::HostPublisher::ConnectProber(std::shared_ptr<AddressProber> address_prober) {
FX_DCHECK(address_prober);
address_prober_ = address_prober;
}
void Mdns::HostPublisher::DisconnectProber() {
FX_DCHECK(address_prober_);
address_prober_ = nullptr;
}
} // namespace mdns