blob: fd541ebe1a0fe2b46a597b142cb5eb7364072dbd [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/util/mdns_impl.h"
#include <arpa/inet.h>
#include <fuchsia/net/mdns/cpp/fidl.h>
#include <lib/async-loop/default.h>
#include <lib/async-loop/loop.h>
#include <lib/async/cpp/task.h>
#include <lib/async/default.h>
#include <lib/syslog/cpp/macros.h>
#include <poll.h>
#include <sys/socket.h>
#include <unistd.h>
#include <zircon/status.h>
#include <iostream>
#include <unordered_set>
#include "lib/fidl/cpp/type_converter.h"
#include "src/connectivity/network/mdns/util/commands.h"
#include "src/connectivity/network/mdns/util/formatting.h"
#include "src/lib/fsl/types/type_converters.h"
#include "src/lib/inet/ip_address.h"
namespace fidl {
template <>
struct TypeConverter<fuchsia::net::IpAddress, inet::IpAddress> {
static fuchsia::net::IpAddress Convert(const inet::IpAddress& value) {
return static_cast<fuchsia::net::IpAddress>(value);
}
};
template <>
struct TypeConverter<std::vector<uint8_t>, std::string> {
static std::vector<uint8_t> Convert(const std::string& value) {
return std::vector<uint8_t>(value.data(), value.data() + value.size());
}
};
template <typename T, typename U>
struct TypeConverter<std::vector<T>, std::vector<U>> {
static std::vector<T> Convert(const std::vector<U>& value) {
std::vector<T> result;
std::transform(value.begin(), value.end(), std::back_inserter(result),
[](const U& u) { return fidl::To<T>(u); });
return result;
}
};
} // namespace fidl
namespace mdns {
namespace {
const std::string kPrompt = "[mdns-util] ";
const std::string kHostSuffix = ".local.";
} // namespace
MdnsImpl::MdnsImpl(sys::ComponentContext* component_context, Command command,
async_dispatcher_t* dispatcher, fit::closure quit_callback)
: component_context_(component_context),
dispatcher_(dispatcher),
quit_callback_(std::move(quit_callback)) {
FX_DCHECK(component_context);
FX_DCHECK(quit_callback_);
// Based on the command entered on the command line, determine whether to run interactively.
switch (command.verb()) {
case CommandVerb::kResolveHost:
case CommandVerb::kResolveInstance:
// The command makes sense to run without interaction. Terminate when it completes.
transient_ = true;
break;
case CommandVerb::kSubscribeHost:
case CommandVerb::kSubscribeService:
case CommandVerb::kPublishHost:
case CommandVerb::kPublishInstance:
case CommandVerb::kEmpty:
// The command implies that the utility should run interactively.
transient_ = false;
break;
case CommandVerb::kUnsubscribeHost:
case CommandVerb::kUnsubscribeService:
case CommandVerb::kUnpublishHost:
case CommandVerb::kUnpublishInstance:
case CommandVerb::kQuit:
// The command makes no sense on the command line. Show help and quit.
std::cout << "error: command is not valid on the command line\n";
Command::ShowHelp(CommandVerb::kHelp);
quit_callback_();
return;
case CommandVerb::kHelp:
case CommandVerb::kMalformed:
// The command is an explicit request for help or is malformed. Show help and quit.
command.ShowHelp();
quit_callback_();
return;
}
ExecuteCommand(command);
if (transient_) {
return;
}
input_.SetEofCallback([this]() { Quit(); });
input_.Init(
[this](std::string command_line) {
CommandParser parser(command_line);
HideInput();
ExecuteCommand(parser.Parse());
ShowInput();
input_.AddToHistory(command_line);
},
kPrompt);
ShowInput();
WaitForKeystroke();
}
void MdnsImpl::ExecuteCommand(const Command& command) {
switch (command.verb()) {
case CommandVerb::kResolveHost:
ResolveHost(command.host_name(), command.timeout(), command.media(), command.ip_versions(),
command.exclude_local(), command.exclude_local_proxies());
break;
case CommandVerb::kResolveInstance:
ResolveInstance(command.instance_name(), command.service_name(), command.timeout(),
command.media(), command.ip_versions(), command.exclude_local(),
command.exclude_local_proxies());
break;
case CommandVerb::kSubscribeHost:
SubscribeHost(command.host_name(), command.media(), command.ip_versions(),
command.exclude_local(), command.exclude_local_proxies());
break;
case CommandVerb::kSubscribeService:
SubscribeService(command.service_name(), command.media(), command.ip_versions(),
command.exclude_local(), command.exclude_local_proxies());
break;
case CommandVerb::kPublishHost:
PublishHost(command.host_name(), command.addresses(), command.probe(), command.media(),
command.ip_versions());
break;
case CommandVerb::kPublishInstance:
PublishInstance(command.instance_name(), command.service_name(), command.port(),
command.text(), command.probe(), command.media(), command.ip_versions(),
command.srv_priority(), command.srv_weight(), command.ptr_ttl(),
command.srv_ttl(), command.txt_ttl(), command.proxy_host_name());
break;
case CommandVerb::kUnsubscribeHost:
UnsubscribeHost(command.host_name());
break;
case CommandVerb::kUnsubscribeService:
UnsubscribeService(command.service_name());
break;
case CommandVerb::kUnpublishHost:
UnpublishHost(command.host_name());
break;
case CommandVerb::kUnpublishInstance:
UnpublishInstance(command.instance_name(), command.service_name(), command.proxy_host_name());
break;
case CommandVerb::kHelp:
case CommandVerb::kMalformed:
command.ShowHelp();
break;
case CommandVerb::kQuit:
Quit();
break;
default:
break;
}
}
void MdnsImpl::WaitForKeystroke() {
fd_waiter_.Wait(
[this](zx_status_t status, uint32_t events) {
char ch;
if (read(STDIN_FILENO, &ch, 1) > 0) {
input_.OnInput(ch);
}
WaitForKeystroke();
},
STDIN_FILENO, POLLIN);
}
void MdnsImpl::ResolveHost(const std::string& host_name, zx::duration timeout,
fuchsia::net::mdns::Media media,
fuchsia::net::mdns::IpVersions ip_versions, bool exclude_local,
bool exclude_local_proxies) {
std::cout << Command::kResolve << " " << host_name << kHostSuffix << " starting\n";
EnsureHostNameResolver();
fuchsia::net::mdns::HostNameResolutionOptions options;
options.set_media(media);
options.set_ip_versions(ip_versions);
options.set_exclude_local(exclude_local);
options.set_exclude_local_proxies(exclude_local_proxies);
host_name_resolver_->ResolveHostName(
host_name, timeout.get(), std::move(options),
[this, host_name](std::vector<fuchsia::net::mdns::HostAddress> addresses) {
HideInput();
if (addresses.empty()) {
std::cout << Command::kResolve << " " << host_name << kHostSuffix
<< " failed: host not found\n";
ShowInput();
QuitIfTransient();
return;
}
std::cout << Command::kResolve << " " << host_name << kHostSuffix << " succeeded\n";
for (auto& address : addresses) {
std::cout << " " << inet::IpAddress(address.address) << " interface "
<< address.interface << " ttl " << zx::duration(address.ttl).to_secs() << "s\n";
}
ShowInput();
QuitIfTransient();
});
}
void MdnsImpl::ResolveInstance(const std::string& instance_name, const std::string& service_name,
zx::duration timeout, fuchsia::net::mdns::Media media,
fuchsia::net::mdns::IpVersions ip_versions, bool exclude_local,
bool exclude_local_proxies) {
std::cout << Command::kResolve << " " << instance_name << "." << service_name << " starting\n";
EnsureServiceInstanceResolver();
fuchsia::net::mdns::ServiceInstanceResolutionOptions options;
options.set_media(media);
options.set_ip_versions(ip_versions);
options.set_exclude_local(exclude_local);
options.set_exclude_local_proxies(exclude_local_proxies);
service_instance_resolver_->ResolveServiceInstance(
service_name, instance_name, timeout.get(), std::move(options),
[this, instance_name, service_name](fuchsia::net::mdns::ServiceInstance instance) {
HideInput();
if (!instance.has_addresses() || instance.addresses().empty()) {
std::cout << Command::kResolve << " " << instance_name << "." << service_name
<< " failed: instance not found\n";
ShowInput();
QuitIfTransient();
return;
}
std::cout << Command::kResolve << " " << instance_name << "." << service_name
<< " succeeded:" << instance << fostr::NewLine;
ShowInput();
QuitIfTransient();
});
}
void MdnsImpl::SubscribeHost(const std::string& host_name, fuchsia::net::mdns::Media media,
fuchsia::net::mdns::IpVersions ip_versions, bool exclude_local,
bool exclude_local_proxies) {
if (host_name_subscription_listeners_by_host_name_.find(host_name) !=
host_name_subscription_listeners_by_host_name_.end()) {
std::cout << Command::kSubscribe << " " << host_name << kHostSuffix
<< " failed: already subscribed\n";
return;
}
std::cout << Command::kSubscribe << " " << host_name << kHostSuffix << " starting\n";
EnsureHostNameSubscriber();
fuchsia::net::mdns::HostNameSubscriptionOptions options;
options.set_media(media);
options.set_ip_versions(ip_versions);
options.set_exclude_local(exclude_local);
options.set_exclude_local_proxies(exclude_local_proxies);
fidl::InterfaceHandle<fuchsia::net::mdns::HostNameSubscriptionListener> listener_handle;
auto listener = std::make_unique<HostNameSubscriptionListener>(
host_name, listener_handle.NewRequest(), input_);
auto listener_raw = listener.get();
host_name_subscription_listeners_by_host_name_.emplace(host_name, std::move(listener));
listener_raw->set_error_handler([this, host_name](zx_status_t status) mutable {
HideInput();
std::cout << Command::kSubscribe << " " << host_name << kHostSuffix
<< " listener channel disconnected unexpectedly, " << zx_status_get_string(status)
<< "\n";
ShowInput();
host_name_subscription_listeners_by_host_name_.erase(host_name);
});
host_name_subscriber_->SubscribeToHostName(host_name, std::move(options),
std::move(listener_handle));
}
void MdnsImpl::SubscribeService(const std::string& service_name, fuchsia::net::mdns::Media media,
fuchsia::net::mdns::IpVersions ip_versions, bool exclude_local,
bool exclude_local_proxies) {
if (service_subscription_listeners_by_service_name_.find(service_name) !=
service_subscription_listeners_by_service_name_.end()) {
std::cout << Command::kSubscribe << " " << service_name << " failed: already subscribed\n";
return;
}
std::cout << Command::kSubscribe << " " << service_name << " starting\n";
EnsureServiceSubscriber();
fuchsia::net::mdns::ServiceSubscriptionOptions options;
options.set_media(media);
options.set_ip_versions(ip_versions);
options.set_exclude_local(exclude_local);
options.set_exclude_local_proxies(exclude_local_proxies);
fidl::InterfaceHandle<fuchsia::net::mdns::ServiceSubscriptionListener> listener_handle;
auto listener = std::make_unique<ServiceSubscriptionListener>(
service_name, listener_handle.NewRequest(), input_);
auto listener_raw = listener.get();
service_subscription_listeners_by_service_name_.emplace(service_name, std::move(listener));
listener_raw->set_error_handler([this, service_name](zx_status_t status) mutable {
HideInput();
std::cout << Command::kSubscribe << " " << service_name
<< " listener channel disconnected unexpectedly, " << zx_status_get_string(status)
<< "\n";
ShowInput();
service_subscription_listeners_by_service_name_.erase(service_name);
});
if (service_name == Command::kAllServices) {
service_subscriber_->SubscribeToAllServices(std::move(options), std::move(listener_handle));
} else {
service_subscriber_->SubscribeToService(service_name, std::move(options),
std::move(listener_handle));
}
}
void MdnsImpl::PublishHost(const std::string& host_name, std::vector<inet::IpAddress> addresses,
bool probe, fuchsia::net::mdns::Media media,
fuchsia::net::mdns::IpVersions ip_versions) {
if (proxy_hosts_by_name_.find(host_name) != proxy_hosts_by_name_.end()) {
std::cout << Command::kPublish << " " << host_name << kHostSuffix
<< " failed: already published\n";
return;
}
std::cout << Command::kPublish << " " << host_name << kHostSuffix << " starting\n";
EnsureProxyHostPublisher();
fuchsia::net::mdns::ProxyHostPublicationOptions options;
options.set_media(media);
options.set_ip_versions(ip_versions);
options.set_perform_probe(probe);
fuchsia::net::mdns::ServiceInstancePublisherPtr service_instance_publisher;
proxy_host_publisher_->PublishProxyHost(
host_name, fidl::To<std::vector<fuchsia::net::IpAddress>>(addresses), std::move(options),
service_instance_publisher.NewRequest(),
[this, host_name, service_instance_publisher = std::move(service_instance_publisher)](
fuchsia::net::mdns::ProxyHostPublisher_PublishProxyHost_Result result) mutable {
HideInput();
if (result.is_err()) {
switch (result.err()) {
case fuchsia::net::mdns::PublishProxyHostError::ALREADY_PUBLISHED_LOCALLY:
std::cout << Command::kPublish << " " << host_name << kHostSuffix
<< " failed: already published locally\n";
break;
case fuchsia::net::mdns::PublishProxyHostError::ALREADY_PUBLISHED_ON_SUBNET:
std::cout << Command::kPublish << " " << host_name << kHostSuffix
<< " failed: already published on subnet\n";
break;
}
ShowInput();
return;
}
std::cout << Command::kPublish << " " << host_name << kHostSuffix << " succeeded\n";
auto proxy_host = std::make_unique<ProxyHost>(std::move(service_instance_publisher));
proxy_host->set_error_handler([this, host_name](zx_status_t status) {
HideInput();
std::cout << Command::kPublish << " " << host_name << kHostSuffix
<< " publisher disconnected unexpectedly, " << zx_status_get_string(status)
<< "\n";
ShowInput();
proxy_hosts_by_name_.erase(host_name);
});
proxy_hosts_by_name_.emplace(host_name, std::move(proxy_host));
ShowInput();
});
}
void MdnsImpl::PublishInstance(const std::string& instance_name, const std::string& service_name,
uint16_t port, const std::vector<std::string>& text, bool probe,
fuchsia::net::mdns::Media media,
fuchsia::net::mdns::IpVersions ip_versions, uint16_t srv_priority,
uint16_t srv_weight, zx::duration ptr_ttl, zx::duration srv_ttl,
zx::duration txt_ttl, const std::string& proxy_host_name) {
std::string instance_full_name = instance_name + "." + service_name;
std::string title;
ProxyHost* proxy_host = nullptr;
if (proxy_host_name.empty()) {
title = instance_full_name;
auto iter =
service_instance_publication_responders_by_instance_full_name_.find(instance_full_name);
if (iter == service_instance_publication_responders_by_instance_full_name_.end()) {
std::cout << Command::kPublish << " " << title << " failed: already published\n";
return;
}
EnsureServiceInstancePublisher();
} else {
title = instance_full_name + " on proxy " + proxy_host_name;
auto iter = proxy_hosts_by_name_.find(proxy_host_name);
if (iter == proxy_hosts_by_name_.end()) {
std::cout << Command::kPublish << " " << title << " failed: no such proxy\n";
return;
}
proxy_host = iter->second.get();
if (proxy_host->ResponderExists(instance_full_name)) {
std::cout << Command::kPublish << " " << title << " failed: already published on the proxy\n";
return;
}
}
fuchsia::net::mdns::ServiceInstancePublicationOptions options;
options.set_media(media);
options.set_ip_versions(ip_versions);
options.set_perform_probe(probe);
fuchsia::net::mdns::ServiceInstancePublisherPtr& publisher =
proxy_host ? proxy_host->publisher() : service_instance_publisher_;
fuchsia::net::mdns::ServiceInstancePublication publication;
publication.set_port(port);
publication.set_text(fidl::To<std::vector<std::vector<uint8_t>>>(std::move(text)));
publication.set_srv_priority(srv_priority);
publication.set_srv_weight(srv_weight);
publication.set_ptr_ttl(ptr_ttl.get());
publication.set_srv_ttl(srv_ttl.get());
publication.set_txt_ttl(txt_ttl.get());
fidl::InterfaceHandle<fuchsia::net::mdns::ServiceInstancePublicationResponder> responder_handle;
auto request = responder_handle.NewRequest();
publisher->PublishServiceInstance(
std::move(service_name), std::move(instance_name), std::move(options),
std::move(responder_handle),
[this, proxy_host, proxy_host_name, request = std::move(request),
publication = std::move(publication), instance_full_name,
title](fuchsia::net::mdns::ServiceInstancePublisher_PublishServiceInstance_Result
result) mutable {
HideInput();
if (result.is_err()) {
switch (result.err()) {
case fuchsia::net::mdns::PublishServiceInstanceError::ALREADY_PUBLISHED_LOCALLY:
std::cout << Command::kPublish << " " << title
<< " failed: already published locally\n";
break;
case fuchsia::net::mdns::PublishServiceInstanceError::ALREADY_PUBLISHED_ON_SUBNET:
std::cout << Command::kPublish << " " << title
<< " failed: already published on subnet\n";
break;
}
ShowInput();
return;
}
std::cout << Command::kPublish << " " << title << " starting\n";
auto responder = std::make_unique<ServiceInstancePublicationResponder>(
std::move(request), input_, std::move(publication));
auto responder_raw = responder.get();
if (proxy_host) {
proxy_host->AddResponder(instance_full_name, std::move(responder));
} else {
service_instance_publication_responders_by_instance_full_name_.emplace(
instance_full_name, std::move(responder));
}
responder_raw->set_error_handler(
[this, instance_full_name, title, proxy_host_name](zx_status_t status) mutable {
HideInput();
std::cout << Command::kPublish << " " << title
<< " responder channel disconnected unexpectedly, "
<< zx_status_get_string(status) << "\n";
if (proxy_host_name.empty()) {
service_instance_publication_responders_by_instance_full_name_.erase(
instance_full_name);
} else {
auto iter = proxy_hosts_by_name_.find(proxy_host_name);
if (iter != proxy_hosts_by_name_.end()) {
iter->second->RemoveResponder(instance_full_name);
}
}
ShowInput();
});
ShowInput();
});
}
void MdnsImpl::UnsubscribeHost(const std::string& host_name) {
if (host_name_subscription_listeners_by_host_name_.erase(host_name) == 0) {
std::cout << Command::kUnsubscribe << " " << host_name << kHostSuffix
<< " failed: not currenly subscribed\n";
return;
}
std::cout << Command::kUnsubscribe << " " << host_name << kHostSuffix << " succeeded\n";
}
void MdnsImpl::UnsubscribeService(const std::string& service_name) {
if (service_subscription_listeners_by_service_name_.erase(service_name) == 0) {
std::cout << Command::kUnsubscribe << " " << service_name
<< " failed: not currenly subscribed\n";
return;
}
std::cout << Command::kUnsubscribe << " " << service_name << " succeeded\n";
}
void MdnsImpl::UnpublishHost(const std::string& host_name) {
if (proxy_hosts_by_name_.erase(host_name) == 0) {
std::cout << Command::kUnpublish << " " << host_name << kHostSuffix
<< " failed: not currently published\n";
return;
}
std::cout << Command::kUnpublish << " " << host_name << kHostSuffix << " succeeded\n";
}
void MdnsImpl::UnpublishInstance(const std::string& instance_name, const std::string& service_name,
const std::string& proxy_host_name) {
std::string instance_full_name = instance_name + "." + service_name;
if (proxy_host_name.empty()) {
if (service_instance_publication_responders_by_instance_full_name_.erase(instance_full_name) ==
0) {
std::cout << Command::kUnpublish << " " << instance_full_name
<< " failed: not currently published\n";
return;
}
std::cout << Command::kUnpublish << " " << instance_full_name << " succeeded\n";
return;
}
auto iter = proxy_hosts_by_name_.find(proxy_host_name);
if (iter == proxy_hosts_by_name_.end()) {
std::cout << Command::kUnpublish << " " << instance_full_name << " on proxy " << proxy_host_name
<< " failed: proxy host not currently published\n";
return;
}
if (!iter->second->RemoveResponder(instance_full_name)) {
std::cout << Command::kUnpublish << " " << instance_full_name << " on proxy " << proxy_host_name
<< " failed: not currently published on proxy host\n";
return;
}
std::cout << Command::kUnpublish << " " << instance_full_name << " on proxy " << proxy_host_name
<< " succeeded\n";
}
void MdnsImpl::EnsureHostNameResolver() {
if (host_name_resolver_) {
return;
}
host_name_resolver_ = component_context_->svc()->Connect<fuchsia::net::mdns::HostNameResolver>();
host_name_resolver_.set_error_handler([this](zx_status_t status) {
HideInput();
std::cout << "fuchsia.net.mdns.HostNameResolver channel disconnected unexpectedly, "
<< zx_status_get_string(status) << "\n";
ShowInput();
});
}
void MdnsImpl::EnsureServiceInstanceResolver() {
if (service_instance_resolver_) {
return;
}
service_instance_resolver_ =
component_context_->svc()->Connect<fuchsia::net::mdns::ServiceInstanceResolver>();
service_instance_resolver_.set_error_handler([this](zx_status_t status) {
HideInput();
std::cout << "fuchsia.net.mdns.ServiceInstanceResolver channel disconnected: "
<< zx_status_get_string(status);
ShowInput();
});
}
void MdnsImpl::EnsureHostNameSubscriber() {
if (host_name_subscriber_) {
return;
}
host_name_subscriber_ =
component_context_->svc()->Connect<fuchsia::net::mdns::HostNameSubscriber>();
host_name_subscriber_.set_error_handler([this](zx_status_t status) {
HideInput();
std::cout << "fuchsia.net.mdns.HostNameSubscriber channel disconnected unexpectedly, "
<< zx_status_get_string(status) << "\n";
ShowInput();
});
}
void MdnsImpl::EnsureServiceSubscriber() {
if (service_subscriber_) {
return;
}
service_subscriber_ =
component_context_->svc()->Connect<fuchsia::net::mdns::ServiceSubscriber2>();
service_subscriber_.set_error_handler([this](zx_status_t status) {
HideInput();
std::cout << "fuchsia.net.mdns.ServiceSubscriber2 channel disconnected unexpectedly, "
<< zx_status_get_string(status) << "\n";
ShowInput();
});
}
void MdnsImpl::EnsureProxyHostPublisher() {
if (proxy_host_publisher_) {
return;
}
proxy_host_publisher_ =
component_context_->svc()->Connect<fuchsia::net::mdns::ProxyHostPublisher>();
proxy_host_publisher_.set_error_handler([this](zx_status_t status) {
HideInput();
std::cout << "fuchsia.net.mdns.ProxyHostPublisher channel disconnected unexpectedly, "
<< zx_status_get_string(status) << "\n";
ShowInput();
});
}
void MdnsImpl::EnsureServiceInstancePublisher() {
if (service_instance_publisher_) {
return;
}
service_instance_publisher_ =
component_context_->svc()->Connect<fuchsia::net::mdns::ServiceInstancePublisher>();
service_instance_publisher_.set_error_handler([this](zx_status_t status) {
HideInput();
std::cout << "fuchsia.net.mdns.ServiceInstancePublisher channel disconnected unexpectedly, "
<< zx_status_get_string(status) << "\n";
ShowInput();
});
}
void MdnsImpl::Quit() {
async::PostTask(dispatcher_, [this]() { QuitSync(); });
}
void MdnsImpl::QuitSync() {
host_name_subscription_listeners_by_host_name_.clear();
service_subscription_listeners_by_service_name_.clear();
proxy_hosts_by_name_.clear();
service_instance_publication_responders_by_instance_full_name_.clear();
host_name_resolver_.set_error_handler(nullptr);
service_instance_resolver_.set_error_handler(nullptr);
host_name_subscriber_.set_error_handler(nullptr);
service_subscriber_.set_error_handler(nullptr);
proxy_host_publisher_.set_error_handler(nullptr);
service_instance_publisher_.set_error_handler(nullptr);
host_name_resolver_.Unbind();
service_instance_resolver_.Unbind();
host_name_subscriber_.Unbind();
service_subscriber_.Unbind();
proxy_host_publisher_.Unbind();
service_instance_publisher_.Unbind();
HideInput();
quit_callback_();
}
void MdnsImpl::QuitIfTransient() {
if (transient_) {
Quit();
}
}
void MdnsImpl::ShowInput() {
if (!transient_) {
input_.Show();
}
}
void MdnsImpl::HideInput() {
if (!transient_) {
input_.Hide();
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// HostNameSubscriptionListener definitions.
MdnsImpl::HostNameSubscriptionListener::HostNameSubscriptionListener(
std::string host_name,
fidl::InterfaceRequest<fuchsia::net::mdns::HostNameSubscriptionListener> request,
line_input::ModalLineInput& input)
: host_name_(host_name), binding_(this, std::move(request)), input_(input) {}
void MdnsImpl::HostNameSubscriptionListener::set_error_handler(
fit::function<void(zx_status_t)> error_handler) {
binding_.set_error_handler(std::move(error_handler));
}
void MdnsImpl::HostNameSubscriptionListener::OnAddressesChanged(
std::vector<fuchsia::net::mdns::HostAddress> addresses, OnAddressesChangedCallback callback) {
input_.Hide();
std::cout << Command::kSubscribe << " " << host_name_ << ": addresses" << addresses << "\n";
input_.Show();
callback();
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// ServiceSubscriptionListener definitions.
MdnsImpl::ServiceSubscriptionListener::ServiceSubscriptionListener(
const std::string& service_name,
fidl::InterfaceRequest<fuchsia::net::mdns::ServiceSubscriptionListener> request,
line_input::ModalLineInput& input)
: service_name_(service_name), binding_(this, std::move(request)), input_(input) {}
void MdnsImpl::ServiceSubscriptionListener::set_error_handler(
fit::function<void(zx_status_t)> error_handler) {
binding_.set_error_handler(std::move(error_handler));
}
void MdnsImpl::ServiceSubscriptionListener::OnInstanceDiscovered(
fuchsia::net::mdns::ServiceInstance instance, OnInstanceDiscoveredCallback callback) {
input_.Hide();
std::cout << Command::kSubscribe << " " << service_name_ << ": instance " << instance.instance()
<< "." << instance.service() << " discovered" << instance << "\n";
input_.Show();
callback();
}
void MdnsImpl::ServiceSubscriptionListener::OnInstanceChanged(
fuchsia::net::mdns::ServiceInstance instance, OnInstanceChangedCallback callback) {
input_.Hide();
std::cout << Command::kSubscribe << " " << service_name_ << ": instance " << instance.instance()
<< "." << instance.service() << " changed" << instance << "\n";
input_.Show();
callback();
}
void MdnsImpl::ServiceSubscriptionListener::OnInstanceLost(std::string service_name,
std::string instance_name,
OnInstanceLostCallback callback) {
input_.Hide();
std::cout << Command::kSubscribe << " " << service_name_ << ": instance " << instance_name << "."
<< service_name << " lost\n";
input_.Show();
callback();
}
void MdnsImpl::ServiceSubscriptionListener::OnQuery(fuchsia::net::mdns::ResourceType resource_type,
OnQueryCallback callback) {
// This method is just for metrics. Ignore it.
callback();
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// ServiceInstancePublicationResponder definitions.
MdnsImpl::ServiceInstancePublicationResponder::ServiceInstancePublicationResponder(
fidl::InterfaceRequest<fuchsia::net::mdns::ServiceInstancePublicationResponder> request,
line_input::ModalLineInput& input, fuchsia::net::mdns::ServiceInstancePublication publication)
: binding_(this, std::move(request)), input_(input), publication_(std::move(publication)) {
(void)input_; // not used
}
void MdnsImpl::ServiceInstancePublicationResponder::set_error_handler(
fit::function<void(zx_status_t)> error_handler) {
binding_.set_error_handler(std::move(error_handler));
}
void MdnsImpl::ServiceInstancePublicationResponder::OnPublication(
fuchsia::net::mdns::ServiceInstancePublicationCause publication_cause, fidl::StringPtr subtype,
std::vector<fuchsia::net::IpAddress> source_addresses, OnPublicationCallback callback) {
if (subtype.has_value()) {
callback(fpromise::error(fuchsia::net::mdns::OnPublicationError::DO_NOT_RESPOND));
return;
}
callback(fpromise::ok(fidl::Clone(publication_)));
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// ProxyHost definitions.
MdnsImpl::ProxyHost::ProxyHost(fuchsia::net::mdns::ServiceInstancePublisherPtr publisher)
: publisher_(std::move(publisher)) {}
void MdnsImpl::ProxyHost::set_error_handler(fit::function<void(zx_status_t)> error_handler) {
publisher_.set_error_handler(std::move(error_handler));
}
bool MdnsImpl::ProxyHost::ResponderExists(std::string instance_full_name) {
return service_instance_publication_responders_by_instance_full_name_.find(instance_full_name) !=
service_instance_publication_responders_by_instance_full_name_.end();
}
void MdnsImpl::ProxyHost::AddResponder(
std::string instance_full_name,
std::unique_ptr<ServiceInstancePublicationResponder> responder) {
service_instance_publication_responders_by_instance_full_name_.emplace(instance_full_name,
std::move(responder));
}
bool MdnsImpl::ProxyHost::RemoveResponder(std::string instance_full_name) {
return service_instance_publication_responders_by_instance_full_name_.erase(instance_full_name) ==
1;
}
} // namespace mdns